Skip to main content

Branches

Git's branching model is known as its "killer feature", and it's what makes Git stand out from the rest of the version control systems out there.

Introduction to Branching

Because Git doesn't save changes or differences in files, it saves a series of snapshots at different times.

When you do a commit, Git saves a commit object. This commit object contains a pointer to a snapshot of the staging content, the author's name and email address entered during the commit, and a pointer to its parent object. A commit object from a first commit has no parent, a commit object from a normal commit has a parent, and a commit object from multiple branches has multiple parents.

Git's branches are essentially just mutable pointers to commit objects. Git's branches are essentially just mutable pointers to commit objects.

Creating Branches

// Create a branch
git branch <branch-name>

This will create a pointer to the commit object you are currently on.

Branch switching

To switch to an existing branch, you need to use the git checkout command.

// Switch to an existing branch
git checkout <branch-name>

// Create a new branch on the commit object you are currently on, and switch to the new branch.
git checkout -b <branch-name>

Check out a branch

// Check out the branches that have been created
git branch

Merge branches

// Merge a branch into the current branch.
git merge <branch-name>

Delete a branch

git branch -d <branch-name>

Branch example

You will go through the following steps.

  1. Develop a website.

  2. Create a branch to implement a new user requirement. 3.

  3. work on this branch.

    At this point, you receive a call saying that there is a serious problem that needs to be fixed urgently. You will handle it as follows.

  4. switch to your online branch (production branch).

  5. create a new branch for this urgent task and fix it in it. 3.

  6. after testing and passing, switch back to the online branch, then merge the repair branch and finally push the changes to the online branch. 4.

  7. Switch back to the branch you originally worked on and continue working.

Creating a new branch

First, let's assume you are working on your project and already have some commits on the master branch.

Now, you create a branch iss53 to implement some new requirement.

git checkout -b iss53

While we did some commits, the iss53 branch was moving forward.

Now you get that call that there's an urgent issue waiting for you to fix, and all you have to do now is switch back to the master branch.

git checkout master

Next, you want to fix this urgent problem. Create a new hotfix branch until the problem is fixed.

git checkout -b hotfix

Merging branches

Then merge the hotfix branch back into your master branch to deploy online.

git checkout master
git merge hotfix

When you're trying to merge two branches, if you can follow one branch to the other, Git will simply move the pointer forward (pointer right) when merging the two, because there are no differences to resolve in this case - it's called " fast-forward".

At this point, the hotfix branch is no longer needed and can be removed.

git branch -d hotfix

Now you cut back to the branch you were working on, continue your work, and have a new commit.

Suppose at this point you have finished with the iss53 requirements and need to merge the iss53 branch into the master branch.

git checkout master
git merge iss53

This looks a little different than when you merged the hotfix branch before.

In this case, your development history is forked (diverged) from an earlier point. Because the commit where the master branch is located is not a direct ancestor of the commit where the iss53 branch is located, Git has to do some extra work. When this happens, Git does a simple three-way merge using the snapshot referred to at the end of the two branches and the common ancestor of the two branches.

Branch merging in case of conflicts

Sometimes merges don't go as smoothly as they should, because if they involve the same file in the same place, a conflict can arise during the merge. In this case, Git does the merge, but doesn't automatically create a new merge commit. Git will pause and wait for you to resolve the merge conflict.

Any file that contains a merge conflict that is pending resolution will be marked with an unmerged status. Git adds standard conflict resolution markers to conflicting files so that you can open the file containing the conflict and resolve it manually.

Conflicting files will contain special sections like the following.

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

This means that the version indicated by HEAD (i.e. where your master branch is located, as you have already checked out this branch when running the merge command) is in the top half of this section (the top half of =======).

And the version indicated by the iss53 branch is in the lower half of =======. To resolve the conflict, you must choose to use one of the two parts split by =======, or you can merge these yourself.

For example, you can resolve the conflict by replacing the segment with the following.

<div id="footer">
please contact us at email.support@github.com
</div>

The conflict resolution above only keeps the changes in one of the branches, and the lines <<<<<<< , ======= , and >>>>>>> are completely deleted. After you have resolved the conflicts in all the files, use the git add command on each file to mark it as resolved. Once you have staged the files that had conflicts, Git will mark them as resolved.

Remote Branches

Remote references are references (pointers) to remote repositories, including branches, tags and so on. Please think of them as bookmarks, which will remind you that the branch's location in the remote repository is where you last connected to them.

Push branches

When you want to share a branch publicly, you need to push it to a remote repository that has write access to it. Local branches are not automatically synchronised with remote repositories - you have to explicitly push the branch you want to share.

// 推送本地分支到正在跟踪远程分支
git push <remote> <branch>

// 推送本地分支到远程分支(自定义远程分支命名)
git push <remote> <branch>:<origin-branch-name>

Trace branches

Checking out a local branch from a remote trace branch automatically creates what is known as a "trace branch" (the branch it follows is called the "upstream branch"). A trace branch is a local branch that is directly related to a remote branch.

// Create a local track branch from a remote branch
git checkout --track <remote>/<branch>

// Create a new local track branch from the remote branch (custom local branch naming)
git checkout -b <branch> <remote>/<branch>

// Modify the remote branch for tracking
git branch -u <remote>/<branch>

// View all tracked branches set
git branch -vv

Pulling

When grabbing data from the server that is not available locally, you can run git fetch or git pull. it is recommended to explicitly use the fetch and merge commands separately.

// Only pull the default remote branch data, no automatic merge.
git fetch <remote>

// Pull the remote branch and try to merge it automatically.
git pull <remote>

Delete the remote branch

git push <remote> --delete <branch>

Changing Bases

You can use the rebase command to move all changes committed to a branch to another branch, like a "replay". In Git, this is called a rebase.

git rebase <base-branch> <topic-branch>

It works by first finding the most recent common ancestor of the two branches (the current branch, assuming experiment, and the target base branch, master), then comparing the current branch to the ancestor's commits, extracting the changes and saving them as a temporary file, then pointing the current branch to the target base, and finally applying the changes you saved as a temporary file in that order.

// 1. Switch the experiment branch.
git checkout experiment

// 2. Change the base to the master branch.
git rebase master

// 3. Switch the master branch.
git checkout master

// 4. Merge the experiment branch.
git merge experiment
git merge experiment

More interesting examples of base changes

When basing two branches, the resulting "replay" doesn't have to be applied on the target branch; you can also specify another branch to You can also specify another branch to be applied.

You have created a server branch from the master branch, added functionality and committed it. You create a client branch based on the committed server branch, again add functionality and commit. You go back to the server branch, add more functionality and commit. Suppose you want to merge the changes in the client into the master branch and publish it, but don't want to merge the changes in the server at the moment because they need to be tested more thoroughly.

You can then use the --onto option of the git rebase command to select changes that are in the client branch but not in the server branch and replay them on the master branch.

// 1. Take the client branch and find the patches it has made since it split from the server branch. Then replay those patches on the master branch so that the client looks like it was modified directly from the master.
git rebase --onto master server client

// 2. Switch the master branch
git checkout master

// 3. Merge the client branch
git merge client
git merge client

Next you decide to merge in the changes from the server branch as well.

// 1. Change the base to the master branch
git rebase master server

// 2. Switch to the master branch
git checkout master

// 3. Merge the branch
git merge server

// 4. Delete a branch
git merge server //

Risks of changing bases

If commits exist outside of your repository and someone else might be working on them, don't perform a base change.

If you follow this golden rule, you can't go wrong. Otherwise, the people will hate you, and your friends and family will laugh and spit on you.

Change of base vs merge

At this point, you have learned the use of base changing and merging in practice, and you must be wondering which is better. Before we answer that question, let's take a step back and think about discussing what commit history actually means.

One way of looking at it is that the commit history of a repository is a record of what has actually happened. It is a document that is specific to the history and has value in itself, and cannot be changed indiscriminately. From this perspective, changing the commit history is blasphemy, you are using a lie to hide what actually happened. What if the submission history resulting from the merge

submission history is a mess? Since that is what happened, the traces should be preserved so that future generations can consult them. The other view is the opposite, that the history of the submission is what happened during the project. No one publishes the first draft of a book, and software maintenance manuals need to be revised repeatedly to make them easier to use. Those who hold this view use tools such as rebase and filter-branch to write the story in a way that is convenient for later readers.

Now, let's go back to the previous question: is it better to merge or to change the base? Hopefully you understand that there is no easy answer to this. Git is a very powerful tool that allows you to do many things with your commit history, but every team and every project has different needs for this. Now that you've learned how to use both separately, I'm sure you can make an informed choice based on your situation.

The general rule is that you should only base clean your history on local changes that have not been pushed or shared with others, and never on commits that have been pushed elsewhere, so that you can enjoy the convenience of both approaches.