Git uses the difference and skills of Merge and Rebase

The git rebase command is often put off by Git newbies because of its reputation as a Git magic command,
but it can actually make life a lot easier for a team when used correctly.

In this post we'll compare git rebase to its often-comparable git merge command, and identify potential scenarios where rebase could be used in a real typical Git workflow.

Overview of Merge and Rebase concepts

First of all we should understand that git rebase is used to deal with the same problem that git merge command deals with. Both commands are used to integrate changes from one branch into another - they just do the same thing in different ways.

Consider this scenario, when you start developing new features in a proprietary branch, and another team member updates the content of the main branch. This will result in a forked commit history, familiar to anyone who uses Git as a code collaboration tool.
insert image description here
Now suppose that the new content in the main branch is related to the new feature you are developing. In order to apply the new code in the main branch to your feature branch, you have two methods: merge and rebase.

use merge

The easiest way is to merge the main branch into the feature branch:

git checkout feature git merge main

Or with a one-line command like this:

git merge feature main

This will create a merge commit in the feature branch, which joins the commit histories of the two branches, and looks like this in the branch diagram structure:
insert image description here

The merge operation is friendly because it is not destructive. Existing branch histories are unchanged. This feature avoids all the pitfalls of the rebase operation (discussed in detail below).

But on the other hand, this also means that whenever the feature branch needs to apply changes from the upstream branch, an unrelated commit history will be added to the commit history. If the update of the main branch is very active, this operation will also pollute the commit history of the feature branch to a considerable extent.
Although the clutter of this commit history can be alleviated by complex git log commands, it can still confuse other developers about the commit history.

use rebase

Instead of merging, you can also rebase the commit history of the feature branch onto the top of the commit history of the main branch:

git checkout feature git rebase main

These operations will put the starting history of the feature branch on top of the last commit of the main branch, and also achieve the purpose of using the new code in the main branch. However, compared to creating a new merge commit in the merge operation, the rebase operation will rewrite the commit history of the original branch by creating a new commit for each commit of the original branch.
insert image description here
The biggest advantage of using the rebase operation is that you can make the project commit history very clean and tidy. First, it eliminates unnecessary merge commits that git merge operations need to create. Second, as shown in the figure above, rebase will create a linear project submission history-that is, you can start from the top of the feature branch and find the starting point of the branch down without encountering any historical forks. This is easier when using commands such as git log, git bisect, and gitk.
However, in order to obtain this kind of easy-to-understand commit history, two prices need to be paid: security and traceability. Rewriting a project's commit history can have potentially disastrous consequences for a collaborative workflow if the golden rule of rebase isn't followed. Again, the rebase operation loses the contextual information that a merge commit can provide - so you have no way of knowing when the feature branch applied changes from the upstream branch.

interactive rebase

Interactive rebase gives you the opportunity to modify the commit record before committing changes to other branches. This is even more powerful than automatic rebase operations, after all it provides complete control over the commit history of the branch.

Generally speaking, the usage scenario of this operation is to sort out the messy submission records of the function branch before merging the function branch into the main branch.
For interactive rebase operations, you need to pass the i option parameter to the git rebase command

git checkout feature git rebase -i main

Executing the above command will open a text editor with a list of all commits in the branch that need to be moved:

pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3

A list like the above is exactly what the branch's history looks like after it has been rebased. By modifying the pick command or reordering the commit history, you can make the final commit history look whatever you want. For example, if the second submission fixes a bug in the first submission, you can use the fixup command instead of pick to compress the two submissions together.

pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3

When you save and close this file, Git will perform a rebase operation based on your modification results. According to the above example, the project history will become as shown below: By clearing those unimportant commit histories, the overall history of the project can be
insert image description here
easier Easy to read. This is something that the git merge operation cannot provide.

Golden rules for rebase operations

Once you understand what rebase is, the next most important thing is to understand when you should not use it. The golden rule about git rebase is never to use it on a public branch.

For example, consider what would happen if you rebase the main branch onto the feature branch:
insert image description here

The rebase command will put all the commits in the main branch on top of the commit record of the feature branch. The problem is that this change is currently only present in your local repository. Other developers are still developing on the original main branch. Since rebasing produces brand new commits, Git will think that your local main branch is now forked from everyone else's.

The only way to synchronize two different main branches is to merge them, which creates a redundant merge commit, and most of the commits in this merge are the same (the previous main branch and your local main branch). Needless to say, this is really puzzling.

So any time before executing the git rebase command, first confirm "Is anyone else using this branch?" If the answer is yes, then you should stop and think about whether there are other non-destructive operations (such as trying git revert command). Except in cases like this, it is safe to rewrite the commit history.

Force-Pushing

If you did rebase the main branch and then want to push the main branch to the remote repository. At this time, Git will prevent you from pushing this time because the submission of the local branch conflicts with the submission of the remote branch. However, you can still force a push by using the --force option, like this:
Be very careful with this command!
The result of git push --force forced push will make the main branch of the remote warehouse use the branch commit history that you have rebased , which of course confuses the rest of the team very much. So don't use the force push option lightly unless you know exactly what you are doing.
There is only one case where you "should" use the force push command, and that is when you push a private branch to the remote warehouse and then do some cleanup. At this point, your general thought is: "Oh! I find it more appropriate to use the current branch record, not the branch record that has been pushed before." Even so, it is still very important to make sure that no one is collaborating with you on this branch.

Workflow in practice

Regardless of the size of the team, the rebase operation can be smoothly integrated into the workflow of the existing team. In this section, let's take a look at the benefits that rebase can provide in different stages of feature development.
In any kind of workflow, if we want to get rebase involved, the first step is to create a dedicated branch for feature development. This provides the necessary branching structure to use rebase safely:
insert image description here

local cleanup

One of the most suitable scenarios for including rebase operations in existing workflows is to clean up local in-progress development branches. By regularly using the interactive rebase operation, you can clean up the commit records of this branch, making each commit more focused and meaningful. The interactive rebase operation allows you to write code without paying too much attention to the commit history, in fact you can clean up the commit history after the fact.

When using the git rebase command, there are two options for the new base: the parent branch of the feature branch (such as the main branch), or a commit in the history of this branch. An example of the first case we saw in the interactive rebase section. The latter option is quite useful for modifying the commit history in this branch. For example, the following command will start a rebase operation for the last three commit histories.

git checkout feature git rebase -i HEAD~3

By specifying HEAD 3 as the new base for the rebase operation, you're not actually moving the branch—you're just interactively rewriting the history of three commits after the commit in HEAD 3. Note that this operation does not bring upstream changes into the feature branch:
insert image description here
if you want to rewrite the entire feature branch history, you should try the git merge-base command, which returns the original base of your feature branch. The following command returns the commit ID of the original base, which can then be used as a parameter of the git rebase command:

git merge-base feature main

A rebase usage scenario like the one above is very beneficial for introducing git rebase into the existing workflow, after all it only affects local branches. All other developers can see is the work you've already done, a beautiful branch commit history with a clean commit history, easy to understand branch content, and easy to track the development process.

Still, this can only be done for private branches. If you collaborate with other developers on the same branch, then this branch is a public branch and rewriting the commit history is not allowed.

There is no alternative for git merge to clean up the local commit history.

Introduce upstream changes

In the beginning of this article, we discussed how to introduce changes to the upstream main branch through git merge or git rebase. The merge operation is safe enough because it preserves the complete commit history, but the rebase operation creates a linear commit history by moving the commit history of the feature branch to the top of the main branch.

This use of the git rebase operation is similar to cleaning up the local commit history (it can also be operated at the same time), the difference is that the commit of the upstream main branch will be introduced during the execution.

Remember that rebase can operate on any remote branch, not just the main branch. For example, when you need to collaborate with others to develop a function, you can introduce other people's development content through rebase.

For example, when you and another developer named John commit to the feature branch, after you fetch the remote feature branch, the local warehouse should look like this: In order to integrate this fork,
insert image description here
you You can treat it like the main branch: either merge the john/feature branch into the local feature branch through the merge operation, or rebase the local feature branch to the top of the john/feature branch.
insert image description here
insert image description here
Note that this does not conflict with the golden rule of rebase, since only new commits from your local feature branch are moved to the top of the john/feature branch, and all commit history before the new commit is unchanged. It's like saying: "Add my new commit to John's already committed content." In most cases, this operation is more in line with human intuition than using the merge operation.
The default behavior of the git pull command is to perform a merge operation, but you can specify that the behavior of the pull operation is rebase by adding the --rebase option.

Feature review using pull request

If you use pull requests for code review work, you should avoid using git rebase after creating pull requests. Once you create a pull request, other developers will come to view your submission, which means that the branch at this time is counted as a public branch. Then rewriting the commit history at this time will make Git and team members unable to judge which commits belong to this function.

When bringing in anyone else's changes, you should use git merge instead of git rebase.
So it's usually a good idea to do an interactive rebase after submitting a pull request to clean up the commit history.

Integrate approved features

For the functional code that has been reviewed by the team, you can first use rebase to move the new code to the top of the main branch, and then perform git merge to merge the new function into the main branch.

This operation is similar to rebasing the upstream branch to the local function branch, except that you cannot rewrite the commit history of the main branch, so you can only integrate the code of the function branch into the main branch through the git merge operation at the end. However, doing a rebase before merging can ensure that the merge operation can move forward quickly, so that the commit history looks perfectly linear. This also gives you the opportunity to do a cleanup of the commit history before actually merging.
insert image description here
insert image description here

insert image description here
If you are not very used to git rebase operation, you can always use a temporary branch for rebase operation. In this way, if you accidentally mess up the commit history of the feature branch, there is always a chance to start over from the original feature branch. Like this:

git checkout feature git checkout -b temporary-branch git rebase -i main # [Clean up the history] git checkout main git merge temporary-branch

Summarize

That's all you need to know to get started with rebase. If you want a clean, linear commit history, rather than one with many merge commits intertwined, you should try using git rebase instead of git merge when integrating branches.

On the other hand, if you want to preserve the complete commit history and avoid rewriting the history of public commits, you can still stick to git merge. Both are fine, but at least you now have another option to take advantage of git rebase at your fingertips.

Guess you like

Origin blog.csdn.net/weixin_55042716/article/details/128079958