19

Is it possible to rebase a branch with all its parent branches with Git? (I think "parent branches" is the correct form to use here. Depending on your viewpoint, you might also call them dependent sub-branches. Yet, following the parent-pointers of the commits will get you to those branches, so IMHO it is fair to call them "parent branches").

I often use branches as quick/mutable tags/checkpoints to mark certain commits. All parent branches/parent refs are fully included in the branch-to-be-rebased; there are no commits only in the parent branches.

* master
*
* featureA-finished
*
* origin/master

Now I want to rebase -i master onto origin/master to change/reword the commit featureA-finished^

After git rebase -i --onto origin/master origin/master master, I basically want the history to be:

* master
*
* featureA-finished
* (changed/reworded)
* origin/master

but what I get is:

* master
*
* (same changeset as featureA-finished)
* (changed/reworded)
| * featureA-finished
|.* (original commit i wanted to edit)
* origin/master

is there a way around it, or am I stuck with recreating all parent branches – more specifically the branch labels – on the new, rebased commits?

This question is related to, but still very different from Rebasing a branch including all its children.

knittl
  • 246,190
  • 53
  • 318
  • 364
  • 1
    You could instead of branches use `git notes` to mark your commits - those are automatically copied along during rebases, I believe. (It's a new feature, so you'll need the latest release) http://www.kernel.org/pub/software/scm/git/docs/git-notes.html – Cascabel Jun 17 '10 at 13:50
  • See also [how I'd rebase a whole subhistory -- several branches, with some links between them resulting from merge](http://stackoverflow.com/a/9706495/94687). The unpleasant part of that solution is the need to reset the topic branch refs to the new rebased commits afterwards. – imz -- Ivan Zakharyaschev Mar 14 '12 at 22:07

4 Answers4

6

Looks like this feature is slowly getting into Git. rebase will gain the option --rebase-refs which will do exactly what my original answer asked. For the proposed patch series see the thread rebase: command "ref" and options --rewrite-{refs,heads,tags} on gmane.


Update 2022:

The option --update-refs has finally landed in Git v2.38.0, which can now do exactly what was asked in the initial question more than 12 years ago :)

This feature was merged with commit 3d8e3dc4fc22fe41f8ee1184f085c600f35ec76f in August. Hooray!

knittl
  • 246,190
  • 53
  • 318
  • 364
5

According to git's Object Model if you only change the meta-data of a commit (i.e. commit message) but not the underlying data ("tree(s)") contained within it then it's Tree hash will remain unchanged.

Aside from editing a commit message, you are also performing a rebase, which will change the Tree hashes of each commit in your history, because any changes pulled from origin/master will affect the files in your re-written history: which means some of the files (blobs) that your commit points to have changed.

So there is no bullet-proof way to do what you want.

That said, editing a commit with rebase -i does not usually alter the commit's timestamp and author, so you could use this to uniquely identify your commits before and after a rebase operation.

You would have to write a script which records all the branch start-points against these "timestamp:author" identifier before doing a rebase, and then find the rewritten commits with the same "timestamp:author" ID and rebase the branch on it.

Sadly, I don't have time to try writing this script myself now, so I can only wish you the best of luck!

Edit: You can obtain the author email address and timestamp using:

$ git log --graph --all --pretty=format:"%h %ae:%ci"
* 53ca31a robert.meerman@gmail.com:2010-06-16 13:50:12 +0100
* 03dda75 robert.meerman@gmail.com:2010-06-16 13:50:11 +0100
| * a8bb03a robert.meerman@gmail.com:2010-06-16 13:49:46 +0100
| * b93e59d robert.meerman@gmail.com:2010-06-16 13:49:44 +0100
|/
* d4214a2 robert.meerman@gmail.com:2010-06-16 13:49:41 +0100

And you can obtain a list of branches for each of these based on their commit hash:

$ git branch --contains 03dda75
* testbranch

Watch out for multiple branches per commit, the common ancestor d4214a2 belongs to both branches!

RobM
  • 8,373
  • 3
  • 45
  • 37
2

I am not sure how exactly you got there, but:

git branch -f (same changeset as featureA-finished)

should be enough to reset your featureA-finished branch with the right history.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • yes, that's what i meant with »recreating the branches on the new rebased commits«. i know it is possible this way, but it gets really cumbersome for three branches already – knittl Apr 28 '10 at 18:19
  • 1
    @knittl: interesting. A more detail log diagram of those branches before and after might help here. – VonC Apr 28 '10 at 18:46
  • just imagine the same graph with 15 commits, every second commit being featureA-finished, featureB-finished, featureC-finished, etc. – knittl Apr 28 '10 at 19:11
1

What I'd advise is to rebase featureA-finished onto origin/master first. Do the rewording step then. After that, rebase master onto featureA-finished. This will net you the end result you are wanting.

Note that you'll need to use -i on both rebases, and may have to delete all the commits from the original featureA-finshed down in the second rebase. If you wanted, you could write a script that would eliminate this by saving off the intermediate branch and using that as the base for a rebase --onto the new version. It could even handle a sequence of such 'subbranches' if you wrote it right. If you need help I can attempt to bang one out.

Walter Mundt
  • 24,753
  • 5
  • 53
  • 61