Working with Git: a practical guide
Tags: github, git
Published: 2022-10-01 10:57:35

### Some basic understandings
- You need to well distinguish the concepts of the local repo and the remote repo. There are basically two separate repo. Each developer has their local repo, while the remote repo sitting on github acts like a single globalised repo. The interaction between a local repo and the remote repo is done via commands like `git push`, `git fetch`, etc.
- Each branch has a HEAD, which is just a pointer to a commit that marks the tip of the branch. A branch is basically just a pointer to a commit and its commit history.

### Fetch vs. Pull
To put it simple: `git pull` = `git fetch` + `git merge`
`git fetch` refreshes the local view of the remote repo to keep it up-to-date with all the newest changes in the remote repo. Notice the phrase "local view of the remote repo", it means that this action only let us know the current status of the remote repo, without changing(merging to) the local repo.
To merge the changes in any of the remote branches into our local branch, do `git merge origin/<remote_branch>`.

### Working with branches
#### List all branches
To list all local branches and the branch you're on locally: `git branch`
To list all remote branches: `git branch -r`

#### Create a local branch
Create another local branch: `git branch <branch_name>`
Checkout to a local branch: `git checkout <branch_name>`
Create and checkout to a local branch: `git checkout -b <branch>`
Create a branch from another local branch and checkout to it: `git checkout -b <new_branch> <from_branch>`

#### Checkout to a remote branch
For `git checkout <branch_name>`, if there exists a remote branch with <branch_name>, git will 
1. create a local branch with the same name as <remote_branch>
2. checkout to this local branch
3. make this branch track <remote_branch>

The purpose of "making a branch track a remote branch" is that when using implicit commands like `git pull`, `git push` or `git merge`, git knows which remote branch you're refering to. Otherwise it will say something like "no tracking information for the current branch".

To set the remote branch to track, use the command
`git branch --set-upstream-to=origin/<remote_branch> <local_branch>`

Notice that once this is done, the command `git checkout <remote_branch>` no longers set the local branch <remote_branch> to track the remote branch <remote_branch>, instead it stays tracking the remote branch specified in the previous `--set-upstream-to` command.

#### Create remote branch from the local branch
Checkout to a branch that you only have locally:
`git checkout local1`
Assuming that you've commited successfully, run the command:
`git push origin local1`
This will create a new remote branch named "local1", and push to it. Notice that it does not set the local branch to track the remote branch "local1".

#### Push to a different remote branch
Say you're in local branch "local1", doing `git push origin main` will push from local branch "main" to the remote branch "main". (So nothing to do with the branch you're currently in)
If you wanna push from local branch "local1" to a remote branch "remote", you can do `git push origin local1:remote`.

### Integrating different works
#### The mechanism
Let's say we're on a feature branch and we wanna merge the main branch into this feature branch. 
Before merging main into feature, git first determines the 3 commits that are: the base of the two branches, the tip(current commit) of branch "main", the tip(current commit) of branch "feature".
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g64347jeiogrfj9lq3k0.png)
Then git will grab everything new from the newest commit(known as the tip) in branch "main" and chuck it into the tip of branch "feature", making this resultant repo a new commit on top of the previous commit in branch "feature".

If the branch "feature" has no newer commits after the common "base" commit, git simply makes the tip of branch "main" the new merge commit of branch "feature", known as a "fast forwarding" merge commit. And yes, the tip of a branch is nothing but a pointer to a commit, so in this case, git just let the tip of branch "feature" point to the same commit as the tip of "main", making this the "new" merge commit as the result of `git merge feature main`.

#### Merge from a branch
If you're already on branch A, then to merge branch B into branch A: `git merge <branch_B>`
Merge 2 branches explicitly: `git merge <receiving_branch> <incoming_branch>`
If the merge results in a conflict and you wanna abandon this merge action and roll back to the state before this merge, do `git reset --hard HEAD`.

#### Rebase to a branch
Rebase moves all the commits in one branch on top of another branch, making a single-line commit history. This is another technique used in merging different commits, and has advantages over `git merge` in the sense that it creates a linear branch history instead.