83763

How to rebase a series of branches?

Suppose we have the following revision graph:

A-B (master) \ C (feature-a) \ D (feature-b) [depends on feature a] \ E (feature-c) [depends on feature b]

And master is then modified to follow with commit F. <strong>Is there any simple way to rebase E onto F (master) so that branches feature-a, feature-b and feature-c all end up as follows:</strong>

A-B-F (master) \ C' (feature-a) \ D' (feature-b) \ E' (feature-c)

?

In real world situations there are obviously multiple patches between each feature so re-attaching branches in-between manually to rebased history is tedious and error prone job. I know that I can rebase E to E' with a simple git checkout feature-c && git rebase master but that leaves branches feature-a and feature-b pointing to commits C and D instead of C' and D'. Rebase should have all the info to move all the branches, right?

Answer1:

There's nothing built in to git to do this, but it's certainly script-able.

What you need to do is:

    <li>Identify the branch(es) to be rebased (here feature-a, feature-b, and feature-c).</li> <li>For each branch, determine which to-be-rebased branches "contain" them (let's call this "is a predecessor"). Branch X is a predecessor of Y if Y is a descendent of the X (and choose something here to handle/break-ties-with two branch names that identify the same commit). In this case, feature-a is a predecessor of both feature-b and feature-c, while feature-b is a predecessor of feature-c (only). Save the "distance back" values for predecessors (how far to chase parent chain).</li> <li>Perform a topological sort. (Actually you can cheat and just find leaves. You only need a cycle-check if you pick branch names by some method other than observing the commit DAG.)</li> <li>For each leaf (in this case just feature-c):
      <li>Rebase it.</li> <li>For each predecessor of this leaf, move it from wherever it is now, to N-parents-back from the new leaf tip.</li> </ul></li> </ul>

      (That's it, all done.)

      Predecessor testing is easy with git merge-base --is-ancestor (pairwise) or git branch --contains (en masse, but requires filtering away non-rebased branches). Finding the "N back" value is a little trickier, but I believe can be done with git rev-list piped to wc -l, for instance.

      Edit: I see that the linked answer (in comment above) uses a similar algorithm—including topo-sort/cyclicality-checking, needed because the branch selection method is not "take from commit DAG"—but with more work, explicitly rebasing each branch as directed. If you work from the DAG, the leaf rebase has done all the work, and the predecessors can simply be relabeled, as I noted.

      Answer2:

      Try this:

      git checkout featurea git rebase git branch --contains (sha1 of your old commit C) |xargs -n 1 git rebase --onto (sha1 of your new commit C') (sha1 of your old commit C)

      Answer3:

      The best way I can currently think of is rebasing one after the other:

      git rebase --onto master B feature-a git rebase --onto feature-a C feature-b git rebase --onto feature-b D feature-c

      But whenever you run into a problem like this, please think again, if you really want this and understand the consequences.

      Answer4:

      It should be simple. Rebasing E on F would rabes the whole branch with commits C and D.

      git checkout B … git commit -m 'F' git checkout E git rebase F

Recommend

  • Conflicting versions error adding ASP.NET MVC 3 Web Role to Windows Azure project
  • How to determine the number of grouped numbers in a string in bash
  • How to get result of child activities in a parent tab activity?
  • jQuery to hide all row classes except 'row2' only when the parent class is viewContainerTo
  • Merging older versions of a file within Clearcase
  • Why does this excel formula return different results for conditional formatting?
  • Styling descendent element in slotted element
  • What products support 3-digit region subtags, e.g., es-419 for Latin-American Spanish?
  • Java Garbage collection, setting reference to null
  • How to determine the CCSID used in CPYFRMIMPF command?
  • OCILogon during Grace Period - ORA-28002
  • css background images not always displayed
  • Why are “sc.addFile” and “spark-submit --files” not distributing a local file to all workers?
  • Why can't I use non-integral types with switch [duplicate]
  • How to discover Font Type?
  • Is there any way to call saveCurrentTurnWithMatchData without sending a push notification?
  • Dynamically add UI elements to StackPanel after button pressed
  • triggering user space with kernel
  • SIP API media codecs
  • sweetalert2 inputoptions from file in select example
  • New Firebase failed: First argument must be a valid firebase URL and the path can't contain “.”
  • JSON encode and decode on PHP
  • Building Qt project for C++11 standard
  • Angular2 - Template reference inside NgSwitch
  • SyntaxError: (irb):26: both block arg and actual block given
  • Who propagate bugfixes across branches (corporate development)?
  • How do I display a dialog that asks the user multi-choice questıon using tkInter?
  • x64 applications using gdi+: what are the consequences on performance?
  • Django rest serializer Breaks when data exists
  • The plugin 'org.apache.maven.plugins:maven-jboss-as-plugin' does not exist or no valid ver
  • Scrapy recursive link crawler
  • Jenkins: How To Build multiple projects from a TFS repository?
  • Azure Cloud Service Web Role web pages do not load
  • Microsoft Visual Studio Community 2015 always crashes in Windows 10 if swithed to Visual FoxPro
  • what is the difference between the asp.net mvc application and asp.net web application
  • How do I rollback to a specific git commit
  • Change div Background jquery
  • How does Linux kernel interrupt the application?
  • Busy indicator not showing up in wpf window [duplicate]
  • Why do underscore prefixed variables exist?