git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: rebase-with-history -- a technique for rebasing without trashing your repo history

From
Björn Steinbrink <b.steinbrink@gmx.de>
Date
Aug 13, 2009, 16:12 UTC
Message-ID
<20090813161256.GA8292@atjola.homenet>
In-Reply-To
<4A840B0F.9060003@alum.mit.edu>
On 2009.08.13 14:46:07 +0200, Michael Haggerty wrote:
Show 20 quoted lines
> Sorry to cross-post, but I think this might be interesting to all three
> projects...
> 
> I've been thinking a lot about the problems of tracking upstream changes
> while developing a feature branch.  As I think everybody knows, both
> rebasing and merging have serious disadvantages for this use case.
> Rebasing discards history and makes it difficult to share
> work-in-progress with others, whereas merging makes it difficult to
> prepare a clean patch series that is suitable for submission upstream.
> 
> I've written some articles describing another possibility, which
> combines the advantages of both methods.  The key idea is to retain
> rebase history correctly, on a patch-by-patch level.  The resulting DAG
> retains enough history to prevent problems with merge conflicts
> downstream, while also allowing the patch series to be kept tidy.
> 
> (Please note that this technique only works for the typical "tracking
> upstream" type of rebase; it doesn't help with rebases whose goals are
> changing the order of commits, moving only part of a branch, rewriting
> commits, etc.)

Hm, so that pretty much doesn't work at all for creating a clean patch series, which usually involves rewriting commits, squasing bug fixes into the original commits that introduced the bug etc.

And even for just continously forward porting a series of commits, a common case might be that upstream applied some patches, but not all. Can you deal with that?

Example:
A---B---C (upstream)
     \
      H---I---J---K (yours)
Upstream takes some changes:
A---B---C---I'--K'--D (upstream)
     \
      H---I---J---K (yours)
rebase leads to:
A---B---C---I'--K'--D (upstream)
                     \
                      H'--J' (yours)
What would your approach generate in that case?
> For more information, please see the full articles:
> 
> * Upstream rebase Just Works™ if history is retained [2]

In this one you have two DAGs: (I fixed the second one to also have the merge commit in "subsystem" instead of "topic", so they only differ WRT to the rebased stuff)

A)
m---N---m---m---m---m---m---M  (master)
     \                       \
      o---o---O---o---o       o'--o'--o'--o'--o'--S  (subsystem)
                       \                         /
                        *---*---*-..........-*--T (topic)
B)
m---N---m---m---m---m---m---M  (master)
     \                       \
      \                       o'--o'--o'--o'--o'----------S  (subsystem)
       \                     /   /   /   /   /           /
        --------------------o---o---O---o---o---*---*---T (topic)

And you say that the former creates problems when you want to merge again. How so?

Merging "master" to "subsystem" is a no-op in both cases. Merging "subsystem" to "master" is a fast-forward in both cases. Merging "subsystem" to "topic" is a fast-forward in both cases. Merging "topic" to "subsystem" is a no-op in both cases.

Merging "topic" and "master" (in either direction) has merge base N in both cases.

Let's assume that there's another dev, having his own history based on the old "O" commit. So:

A)
m---N---m---m---m---m---m---M  (master)
     \                       \
      o---o---O---o---o       o'--o'--o'--o'--o'--S  (subsystem)
               \       \                         /
                \       *---*---*-..........-*--T (topic)
                 \
                  X---Y---Z (outsider)
B)
m---N---m---m---m---m---m---M  (master)
     \                       \
      \                       o'--o'--o'--o'--o'----------S  (subsystem)
       \                     /   /   /   /   /           /
        --------------------o---o---O---o---o---*---*---T (topic)
                                     \
                                      X---Y---Z (outsider)

Merging "master" and "outsider" has merge base N in both cases. Merging "subsystem" and "outsider" has merge base O in both cases. Merging "topic" and "outsider" has merge base O in both cases.

The only thing that really makes a difference is when you have another dev having based his history upon one of the o' commits. If that history is merged with "topic", then you get merge base N in A) and one of the o's in B).

But, in that case, merging "topic" is basically just a complicated way of merging "subsystem". Both contain the full series of "o" commits, and all the "topic" commits (due to the merge). So you could just trivially merge "subsystem" instead, which leads the o' commit as the merge base in both cases.

And, that merge of "topic" to "subsystem" was wrong to begin with. If you rewrite history, that has to trickle down. So A) should really have been:

m---m---m---m-...--m---m (master)
                        \
                         o'--o'-...-o'--*'--*'--*' (topic) (subsystem)
(topic was rebased, and subsystem fast-forwarded)

So AFAICT, what your system achieves WRT ease of rebasing is that it obsoletes the need to use "--onto" with git's rebase.

Instead of "git rebase --onto subsystem old_subsystem", you can just say "git rebase subsystem", at the cost of a very complicated DAG.

Björn
Previous: Michael HaggertyNext: Michael Haggerty
Message 2 of 10 in “rebase-with-history -- a technique for rebasing without trashing your repo history”
  1. Michael HaggertyAug 13, 2009
  2. Björn SteinbrinkAug 13, 2009
  3. Michael HaggertyAug 13, 2009
  4. Björn SteinbrinkAug 13, 2009
  5. Michael HaggertyAug 14, 2009
  6. Nanako ShiraishiAug 14, 2009
  7. Björn SteinbrinkAug 15, 2009
  8. Sitaram ChamartyAug 14, 2009
  9. Bryan O'SullivanAug 13, 2009
  10. Abderrahim KitouniAug 13, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.