Plastic vs Git - rerere and rebase

Tuesday, June 14, 2016 , 1 Comments

It happens quite often -- teams moving away from Git to Plastic ask me what happens with features like rebase and even commands like rerere. They normally jump to Plastic because of the big files, the GUIs, but they want to ensure that they won’t miss any of the things they love in Git.

This blogpost tries to answer these questions, and more specifically: why you won't miss Git-style rebase (or history rewriting entirely) nor rerere in Plastic.

And it all lies in how Plastic handles branching differently than Git...

Git rebase

Rebasing in Git (don't confuse it with what we call rebase in Plastic jargon, which is simply merge down) is an operation to rewrite the history of the repo and keep a linear history instead doing merges.

Note: I'm saying "rebase" just for the sake of simplicity but the actual samples describe rebase + squash, which is converting series of commits into a single one.

It is much more clearly seen with an example:

Example - Rebase explained - pic.01

You create Feature 001 branch out of master. Then you make a couple of changes (commits in Git jargon, changesets in Plastic parlance). Very simple so far.

Later, you do more changes, but in the meantime someone else made more changes on master, as follows:

Example - Rebase explained - pic.02

And now you need to get, somehow, the changes made on master to continue your development.

And here there are two options: whether you go and merge down from master to feature 001 as you would always do in Plastic, or you can go and rebase if you're using Git (and only if you never-ever published your feature 001 pushing it to a remote repo, if you did that, you can't rewrite history).

Git rebase is all about rewriting history to create a simplified repository timeline as if things were done linearly. It is explained in the next figure:

Example - Rebase explained - pic.03

Rebase basically creates a new commit (c3b0’ in my case) which basically sums the 3 commits done before and asks you to resolve any conflicts that might happen between your changes and the ones done in parallel in master.

At the end, the new commit is as "if you started your changes on fb46 instead of d903", so when you diff c3b0 you simply see the changes you did in feature 001.

Note that:

  • History gets rewritten because changes where not really done that way (but ok, it is not a capital sin, if you get a simplified diff, why not?).
  • The original commits are grayed out; this is because they are no longer "linked" from any branch and here will be deleted (garbage collected) as soon as possible.

The alternative to this is merging, would be as follows:

Example - Rebase explained - pic.04

This is what you will always do in Plastic and this is one valid option in Git too.

What is the main disadvantage of this as a Git user? Well, diff the new deh1 and, what you get? You get the changes introduced on Feature 001 coming from master (aa31 + fb46). So, it is now not as easy to find out what was really modified on Feature 001. More on diff math.

By the way, this is a Git problem. This is not a problem in Plastic, and that's why we say Plastic doesn't need Git-style history rewriting for rebase... keep on reading.

Git rerere

Rerere is a Git feature capable of recording merge decisions to reproduce them later on. It means it acts at the mergetool level, recording which chunks have been included on a specific merge, so that if the same merge happens again, the same chunks can be selected.

When does rerere help?

Rerere helps in several scenarios. To mention 2 key ones:

  • Rebasing: When rewriting branch history, it is sometimes useful to perform a merge attempt, then record the conflict resolution, then delay the merge for the future.
  • Working on long lived branches that require merge from master: Similar to above -- the user can try a merge, cancel it, but keep conflict resolutions for the future.

The key point from the Plastic SCM side is "why merges need to be cancelled and postponed and not simply committed?". The answer lies in the core differences between Git and Plastic branching and visualization approaches.

Difference 1: Visualization is key to understand branch evolution

Git users are used to rewriting history on a regular basis. They amend commits, or they simply rebase (privately, before pushing, to avoid breaking the replication process). And they also delete branches regularly.

One of the key reasons for this is to "keep history clean" so it is easy to understand and follow by other developers.

If you are working on a branch, and you merge down from master instead of rebasing, diffing in Git no longer helps you understand what was done in the branch.

Git solves this visualization issue at a core level by rewriting history. Plastic SCM solves it at the GUI level by proper visualization.

One of the key Plastic SCM features is the Branch Explorer. It provides an advanced and clean visualization of the repository evolution. It is not just a beautiful graphic; it is basically the reason why rebases (in Git jargon) are not needed in Plastic. Take a look at the picture below:

Rebase

It is extremely easy to understand that branch filesystem-900 has a merge from main (or master in Git jargon). It is also very easy to understand branch evolution since changesets belong to a branch (and they stay on the same branch, something that can't be guaranteed in Git unless fast-forward is avoided).

It is also possible to track and understand repo evolution even when heavy branching happens, without the need of removing branches as the following Branch Explorer shows:

More complex Branch Explorer

Get further information about the Branch Explorer.

While a similar representation is theoretically possible for Git, currently no tool is able of doing anything closer. In fact, tools dramatically break when dealing with thousands of branches (just give a try to some of the newest Git UIs).

Difference 2: Ability to explain branch diffs correctly

Since Plastic SCM precisely tracks changesets and to which branch they belong to, it is very simple to diff a branch and not only diff a changeset.

Let's go back to the previous example:

Rebase

This is what Plastic SCM does when diffing the branch:

Diff with item merge tracking

Besides the built-in semantic capabilities and overall graphical representation, it is important to highlight that:

  • Plastic diff separates what was modified on the branch from what comes from the merge. We call this "item merge tracking" and basically this is the second reason why "rebasing in Git style" is simply not needed in Plastic.
  • In case a file was modified both on the branch and during merge (as it is the case for FileSystem.cs in the picture above), Plastic identifies which lines come from the branch, which ones from the merge, and even which ones were modified *during* the merge (they belong to a manual conflict).

Get more information about this feature.

Additionally, although not strictly related to the "rebasing" problem, Plastic is capable of advanced refactor analysis as explained here.

The Plastic SCM alternative to rerere

It is important to highlight that there's nothing preventing us from implementing something similar to rerere, and in fact it could be a nice addition.

That being said, it is not something Plastic users would probably use frequently because their workflow is different.

As explained in the two previous sections, in Plastic you can simply go and complete the merge, no need for just "simulating a merge, cancelling it, and recording the merge resolutions to apply them later". There is no need for that. Simply go and commit the merge, you will still be able to understand the repository history.

In Git, you would be restricted by the way to understand changes, so you have to manually keep the history clean because the tools don't help you understand what happened. That’s why rerere is helpful.

Conclusion

There are a few key reasons why rebasing and overall history rewriting are not heavily needed in Plastic as they are on Git: repository visualization with Branch Explorer, the ability to diff branches easily explaining what comes from the merge and what from the branch. And the most important one: since in Plastic every changeset / commit belongs to a branch, it is straightforward to implement "branch diff", something which would have to rely on heuristics or a manual commit selection in Git.

1 comment:

  1. TIP: Regarding "rerere", you can do similar things in Plastic by doing a Merge, resolving any conflicts, then instead of a normal "Checkin" do one of the following (available from the Checkin drop-down) :

    * "Checkin changes to a different branch" - This lets you create a new child branch with the result of the merge, keeping full track of the merge. You can later merge this child branch back into its parent to apply that merge and the related conflict resolutions.

    * "Shelve pending changes" - This lets you store the result of the merge for later, but drops the merge-tracking. If you later apply this Shelve it will appear not as a merge but as normal changes. Useful if you want to apply only a smaller part of a merge.

    ReplyDelete