Tuesday, November 28, 2023
HomeVideo EditingRewriting Historical past with Git Rebase

Rewriting Historical past with Git Rebase


Within the basic Git workflow, you develop a brand new characteristic in a devoted subject department, then merge it again right into a manufacturing department as soon as it is completed. This makes git merge an integral instrument for combining branches. Nevertheless, it is not the one one which Git affords.

Combining branches by merging them togetherCombining branches by merging them togetherCombining branches by merging them together
Combining branches by merging them collectively

As an alternative choice to the above situation, you can mix the branches with the git rebase command. As a substitute of tying the branches along with a merge commit, rebasing strikes the whole characteristic department to the tip of grasp as proven beneath.

Combining branches with git rebaseCombining branches with git rebaseCombining branches with git rebase
Combining branches with git rebase

This serves the identical goal as git merge, integrating commits from totally different branches. However there are two the explanation why we’d wish to go for a rebase over a merge:

  • It ends in a linear undertaking historical past.
  • It provides us the chance to scrub up native commits.

On this tutorial, we’ll discover these two widespread use circumstances of git rebase. Sadly, the advantages of git rebase come at a trade-off. When used incorrectly, it may be probably the most harmful operations you’ll be able to carry out in a Git repository. So, we’ll even be taking a cautious take a look at the hazards of rebasing.

Conditions

This tutorial assumes that you just’re aware of the fundamental Git instructions and collaboration workflows. You ought to be comfy staging and committing snapshots, growing options in remoted branches, merging branches collectively, and pushing/pulling branches to/from distant repositories.

1. Rebasing for a Linear Historical past

The primary use case we’ll discover includes a divergent undertaking historical past. Contemplate a repository the place your manufacturing department has moved ahead whilst you had been growing a characteristic:

Developing a feature on a feature branchDeveloping a feature on a feature branchDeveloping a feature on a feature branch

To rebase the characteristic department onto the grasp department, you’ll run the next instructions:

1
git checkout characteristic
2
git rebase grasp

This transplants the characteristic department from its present location to the tip of the grasp department:

Transplanting the feature branch to the tip of the master branchTransplanting the feature branch to the tip of the master branchTransplanting the feature branch to the tip of the master branch

There are two eventualities the place you’ll wish to do that. First, if the characteristic relied on the brand new commits in grasp, it will now have entry to them. Second, if the characteristic was full, it will now be arrange for a fast-forward merge into grasp. In each circumstances, rebasing ends in a linear historical past, whereas git merge would lead to pointless merge commits.

For instance, take into account what would occur in the event you built-in the upstream commits with a merge as an alternative of a rebase:

1
git checkout characteristic
2
git merge grasp

This might have given us an additional merge commit within the characteristic department. What’s extra, this might occur each time you needed to include upstream commits into your characteristic. Finally, your undertaking historical past could be affected by meaningless merge commits.

Integrating upstream commits with a mergeIntegrating upstream commits with a mergeIntegrating upstream commits with a merge
Integrating upstream commits with a merge

This similar profit might be seen when merging within the different path. And not using a rebase, integrating the completed characteristic department into grasp requires a merge commit. Whereas that is really a significant merge commit (within the sense that it represents a accomplished characteristic), the ensuing historical past is filled with forks:

Integrating a completed feature with a mergeIntegrating a completed feature with a mergeIntegrating a completed feature with a merge
Integrating a accomplished characteristic with a merge

Once you rebase earlier than merging, Git is ready to fast-forward grasp to the tip of characteristic. You will discover a linear story of how your undertaking has progressed within the git log output—the commits in characteristic are neatly grouped collectively on prime of the commits in grasp. This isn’t essentially the case when branches are tied along with a merge commit.

Rebasing before mergingRebasing before mergingRebasing before merging
Rebasing earlier than merging

Resolving Conflicts

Once you run git rebase, Git takes every commit within the department and strikes them, one-by-one, onto the brand new base. If any of those commits alter the identical line(s) of code because the upstream commits, it would lead to a battle.

conflicts 1conflicts 1conflicts 1

The git merge command helps you to resolve all the department’s conflicts on the finish of the merge, which is among the major functions of a merge commit. Nevertheless, it really works a bit bit otherwise while you’re rebasing. Conflicts are resolved on a per-commit foundation. So, if git rebase finds a battle, it would cease the rebase process and show a warning message:

1
Auto-merging readme.txt
2
CONFLICT (content material): Merge battle in readme.txt
3
Didn't merge in the adjustments.
4
....
5
When you could have resolved this drawback, run "git rebase --continue".
6
For those who favor to skip this patch, run "git rebase --skip" as an alternative.
7
To take a look at the unique department and cease rebasing, run "git rebase --abort".

Visually, that is what your undertaking historical past seems like when git rebase encounters a battle:

conflicts 2conflicts 2conflicts 2

The conflicts might be inspected by operating git standing. The output seems similar to a merge battle:

1
Unmerged paths:
2
  (use "git reset HEAD <file>..." to unstage)
3
  (use "git add <file>..." to mark decision)
4

5
    each modified:   readme.txt
6

7
no adjustments added to commit (use "git add" and/or "git commit -a")

To resolve the battle, open up the conflicted file (readme.txt within the above instance), discover the affected traces, and manually edit them to the specified end result. Then, inform Git that the battle is resolved by staging the file:

Notice that that is the very same manner you mark a git merge battle as resolved. However do not forget that you are in the midst of a rebase—you do not wish to overlook about the remainder of the commits that have to be moved. The final step is to inform Git to complete rebasing with the --continue possibility:

It will transfer the remainder of the commits, one-by-one, and if every other conflicts come up, you will should repeat this course of once more.

For those who do not wish to resolve the battle, you’ll be able to go for both the --skip or --abort flags. The latter is especially helpful you probably have no thought what is going on on and simply wish to get again to security.

1
# Ignore the commit that induced the battle
2
git rebase --skip
3

4
# Abort the whole rebase and return to the drafting board
5
git rebase --abort

2. Rebasing to Clear Up Native Commits

To this point, we have solely been utilizing git rebase to maneuver branches, but it surely’s far more highly effective than that. By passing the -i flag, you’ll be able to start an interactive rebasing session. Interactive rebasing helps you to outline exactly how every commit can be moved to the brand new base. This offers you the chance to scrub up a characteristic’s historical past earlier than sharing it with different builders.

For instance, as an instance you completed working in your characteristic department and also you’re able to combine it into grasp. To start an interactive rebasing session, run the next command:

1
git checkout characteristic
2
git rebase -i grasp

It will open an editor containing all of the commits in characteristic which might be about to be moved:

1
decide 5c43c2b [Description for oldest commit]
2
decide b8f3240 [Description for 2nd oldest commit]
3
decide c069f4a [Description for most recent commit]

This itemizing defines what the characteristic department goes to seem like after the rebase. Every line represents a commit and the decide command earlier than every commit hash defines what is going on to occur to it in the course of the rebase. Notice that the commits are listed from oldest to most up-to-date. By altering this itemizing, you achieve full management over your undertaking historical past.

If you wish to change the order of the commits, merely reorder the traces. If you wish to change a commit’s message, use the reword command. If you wish to mix two commits, change the decide command to squash. It will roll all the adjustments in that commit into the one above it. For instance, in the event you squashed the second commit within the above itemizing, the characteristic department would seem like the next after saving and shutting the editor:

Squashing the 2nd commit with an interactive rebaseSquashing the 2nd commit with an interactive rebaseSquashing the 2nd commit with an interactive rebase
Squashing the 2nd commit with an interactive rebase

The edit command is especially highly effective. When it reaches the required commit, Git will pause the rebase process, very similar to when it encounters a battle. This offers you the chance to change the contents of the commit with git commit --amend and even add extra commits with the usual git add/git commit instructions. Any new commits you add can be a part of the brand new department.

Interactive rebasing can have a profound influence in your growth workflow. As a substitute of worrying about breaking apart your adjustments into encapsulated commits, you’ll be able to deal with writing your code. For those who ended up committing what ought to be a single turn into 4 separate snapshots, then that is not an issue—rewrite historical past with git rebase -i and squash all of them into one significant commit.

3. Risks of Rebasing

Now that you’ve got an understanding of git rebase, we will discuss when not to make use of it. Internally, rebasing would not really transfer commits to a brand new department. As a substitute, it creates model new commits that comprise the specified adjustments. With that is thoughts, rebasing is best visualized as the next:

dangerdangerdanger

After the rebase, the commits in characteristic may have totally different commit hashes. Which means we did not simply reposition a department—we have actually rewritten our undertaking historical past. It is a essential facet impact of git rebase.

Once you’re working alone on a undertaking, rewriting historical past is not an enormous deal. Nevertheless, as quickly as you begin working in a collaborative atmosphere, it might change into very harmful. For those who rewrite commits that different builders are utilizing (e.g., commits on the grasp department), it would look as if these commits vanished the following time they attempt to pull in your work. This ends in a complicated situation that is tough to get well from.

With that is thoughts, it is best to by no means rebase commits which have been pushed to a public repository until you are constructive that no person has based mostly their work off of them.

Conclusion

This tutorial launched the 2 commonest use circumstances of git rebase. We talked quite a bit about shifting branches round, however take into account that rebasing is admittedly about controlling your undertaking historical past. The ability to rewrite commits after the actual fact frees you to focus in your growth duties as an alternative of breaking down your work into remoted snapshots.

Notice that rebasing is a completely non-compulsory addition to your Git toolbox. You possibly can nonetheless do all the pieces it is advisable with plain outdated git merge instructions. Certainly, that is safer because it avoids the opportunity of rewriting public historical past. Nevertheless, in the event you perceive the dangers, git rebase generally is a a lot cleaner approach to combine branches in comparison with merging commits.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments