The main points of this article are first written in the front.

  1. develop on the same branch with fewer changes per commit, it is recommended to use rebase
  2. it is recommended to use rebase when merging from a public branch to a personal feature branch
  3. when merging from different branches with many change records, merge is recommended
  4. To merge from a personal topic branch to a public branch, you should use merge, don’t use rebase instead of git merge

If you are used to using the default git merge operation, this article may help you.

By using git rebase instead of git merge, you can keep a clearer change history and quickly troubleshoot problems based on that history.

Introduction to git merge

Example of a git merge operation.

1
2
3
git checkout feature && git merge master
# 或者
git merge master feature

The above action is to merge the latest content of master branch to feature branch. When the operation succeeds, a Merge commit will be automatically generated on the maser branch.

The above are operations on different branches. Examples of operations on the same branch.

1
2
3
4
5
git add .
git commit -m 'feat: xxxx'
git fetch && git merge
# 或使用 pull: git pull = git fetch && git merge
git pull
  • Advantages of merge: simple, easy and fast, a non-destructive operation
  • Disadvantage of merge: automatically created Merge commit leads to a very complex change tree and change history, which is difficult to understand when analyzing file change records

Introduction to git rebase

git rebase rewrites the project history by creating a new commit for each commit in the original branch.

Example of a git rebase operation.

1
2
git checkout feature
git rebase master --no-verify

The above operation will merge the contents of the master branch into the feature branch. After the operation succeeds, all the change history is updated to the latest, and no additional commits are generated.

These are operations on different branches. The difference between operating on the same branch and merge is in the last step. Example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
git add .
git commit -m 'feat: xxxx'
# 或者修订上一次提交
git commit --amend --no-edit
git fetch && git rebase --no-verify
# 或使用 pull: git pull --rebase = git fecth && git rebase
git pull --rebase --no-verify
<解决冲突>
<git add 冲突文件>
<git rebase –continue>

This completes a rebase operation on the same branch. When a conflict arises, you will need to continue with git rebase and then resolve the conflict.

  • Advantages of rebase: more concise project history, no merge commit
  • Disadvantage of rebase: When there are a lot of changes, you have to keep resolving conflicts when there is a high probability of conflicts, because you have to rebase each change one by one.

Summary: git merge and git rebase usage suggestions

Compare the directory tree structure of the two operations.

Summary and recommendations.

  • On the same branch, it is recommended to use rebase when there are few changes per commit
1
2
git commit <--amend> -m 'fix: fix error for ...'
git pull --rebase
  • Merge from a public branch to a personal feature branch, rebase is recommended
1
2
3
git fetch --all -v
git checkout feature
git rebase master
  • To merge from a personal feature branch to a public branch, you should use merge and don’t use the rebase operation
1
2
3
4
git fetch --all -v
git checkout master && git merge feature
# or
git merge feature master
  • It is recommended to use merge when merging different branches with a large number of change records

Some tips on how to do git rebase

Using TortoiseGit for rebase operations

Right-click and select Sync, set the pull method to Get, then Change Base (English equivalent path: Git Sync -> Fecth & Rebase). As shown in the image.

gitlab turns on the fast-forward merge option

Settings - General - Merge request - Merge method and select the Fast-forward merge option.

  • No automatically created Merge commits
  • Use only the Fast-forward merges policy
  • When a conflict arises, you can use the rebase variable base method

git rebase –onto

Basic usage.

1
git rebase --onto base from to
  • base : a branch name (representing the HEAD of this branch), or a commit_id (this id is not on to)
  • from : a branch name (this branch has a common ancestor commit with to), or a commit_id (this id is on to)
  • to : a branch name

What the command does.

  1. first executes git checkout to switch to the to branch
  2. writes the commits in the range from from to to(HEAD) to a temporary file. If from is the branch name, find commit, the common ancestor of from and to, and write the commits in the range from commit to to(HEAD) to a temporary file.
  3. Force a reset (git reset -hard) of the current branch to base.
  4. From the list of commits in the temporary file in 2, recommit the commits to the reset branch, one by one, in order

Notes.

  1. If a commit is encountered that is already contained in a branch, the commit is skipped.
  2. If you encounter a conflict during the commit process, the diff process is suspended. After resolving the conflict, the user can either continue the rebase operation with git rebase --continue, skip the commit with git rebase --skip, or terminate the rebase operation with git rebase --abort and switch to the branch before the rebase.
  3. When the operation is finished, the current branch is to.

git rebase -i Interactive Rebase

Interactive rebase gives you the opportunity to change these commits when pushing commits to the remote branch. To use interactive rebase, you need to add the -i (interactive) option. Example.

1
2
git checkout feature
git rebase -i master

The above action will open a text editor that lists all commits that will be moved. Example.

1
2
3
pick 34fd80c19 commit message #1
pick d14b6ae48 commit message #2
pick 139ca1752 commit message #3

The pick at the beginning of each line indicates the type of directive for that commit. git provides the following directive types:

  • pick: keeps the commit (abbreviation:p)
  • reword: keep the commit, but I need to change the commit’s comments (abbreviation:r)
  • edit: keep the commit, but I want to stop and change the commit (not just the comments) (abbreviation:e)
  • squash: merge this commit with the previous one (abbreviation:s)
  • fixup: merge this commit with the previous commit, but I don’t want to keep the commit’s comment information (abbreviation:f)
  • exec: execute shell command (abbreviation:x)
  • drop: I want to discard the commit (abbreviation:d)

By changing the pick command or reordering entries, you can make the branch history change to the result you want.

For example, if the second commit fixes a minor issue in the first commit, you can condense them into a single commit using the following fixup command.

1
2
3
pick 34fd80c19 commit message #1
fixup d14b6ae48 commit message #2
pick 139ca1752 commit message #3

Another example is a feature that was committed multiple times during local development. Finally, you want to compress them into a single commit.

1
2
3
pick 34fd80c19 commit message #1
squash d14b6ae48 commit message #2
squash 139ca1752 commit message #3

When you save and close the file, Git will perform a rebase based on the modified instructions.

Undo rebase base change operation

git reflog allows you to see the reference log of all operations. Using the git reset command, you can roll back to any historical state based on the reference log.

1
2
3
4
5
git reflog
# 在输出结果中找到 rebase 操作后进行 commit 时的 ref 标记,然后执行 reset 命令,如:
git reset --hard HEAD@{2}
# 如该 rebase 操作已推送至远端,需推送更新远端仓库,可继续 push 操作
git push --force-with-lease

Using TortoiseGit is also relatively simple: right mouse button -> TortoiseGit -> Show Reference Records and you can do it visually in the popup list of reference records.

rebase and git hooks

In the previous example, you may have noticed the use of the -no-verify argument.

Since rebase uses an item-by-item commit approach when executing the pick command, when there is a git hook in the repository, the hook will be executed for every operation, making it very slow, but the rebase process hook doesn’t make much sense. Adding the -no-verify argument can bypass the git hook execution. So the recommended conclusion is.

When git hooks are present, the rebase operation is recommended to add the -no-verify argument to bypass the execution of the hook.