#572851 fully implement rebase -i -p

Package:
git
Source:
git
Description:
fast, scalable, distributed revision control system
Submitter:
Jiří Paleček
Date:
2010-06-13 18:39:06 UTC
Severity:
wishlist
#572851#5
Date:
2009-06-11 14:06:04 UTC
From:
To:
Hello,

I had a history similar to this:

     +--------+      +-------+   +-------+   +-------+   +-------+
+-------+
     | master +------+ Xs... +---+   A   +---+ Ys... +---+   B   +---+
Zs... |
     +--------+      +-------+   +-------+   +-------+   +-------+
+-------+

(This whole thing is a single branch)

I wanted to rebase it to master (that is, I did "rebase -i -p master
branch"), and, while doing it, squash commits A and B together. What I
didn't expect, though, is that after rebase, commits Ys disappeared
 from a branch. After doing git lost-found, I discovered that the
history after the rebase was something like this:

     +--------+      +-------+   +-------+   +-------+
     | master +------+ Xs... +---+  A+B  +---+ Zs... |
     +--------+      +-------+   +-------+   +-------+
                                       \--
                                          \- +-------+
                                            \+ Ys... |
                                             +-------+

The Ys weren't on any branch.

Regards
     Jiri Palecek

#572851#10
Date:
2009-06-16 20:45:13 UTC
From:
To:
* Jiří Paleček:

This arrived garbled over here.

They are still held by the reflog, I would assume.  This output is
expected if you deleted all the lines in the "git rebase -i" control
script after the squash commit.  Are you sure you haven't done this?

#572851#15
Date:
2009-06-16 23:59:58 UTC
From:
To:
Sorry, but I think you get what I meant. There were, on top of master,
commits Xs, then A, then Ys, then B, then Zs. The Xs etc. means there are
multiple commits (eg. X1, X2, ...)

No, they aren't since the rebase is only put in the reflog as a whole, in
one record. However, the original (non-rebased) Ys are, of course. I had
to dig the rebased commits from git-lost-found.

Yes, the rebase-todo looked like this:

pick Xs
pick A
squash B
pick Ys
pick Zs

Also, I wouldn't assume the rebase would create any new rebased commits
 from Ys, had I deleted them from rebase-todo.

Regards
     Jiri Palecek

#572851#20
Date:
2009-11-29 20:21:03 UTC
From:
To:
found 532775 git-core/1:1.6.5.3-1
retitle 532775 rebase -i -p: very easy to mess up history
thanks

Hi,

Jiří Paleček wrote:

Yes, I can reproduce this:

	cat >test_editor <<\EOF
	#!/bin/sh
	if expr "$1" : ".*/COMMIT_EDITMSG" >/dev/null 2>&1
	then
		echo A+B >"$1"
	else
		{
			read two
			read three
			read four
			read five
			echo "$two"
			echo "$four" | sed "s/pick/squash/"
			echo "$three"
			echo "$five"
		} <"$1" >"$1"+
		mv "$1"+ "$1"
	fi
	EOF
	chmod +x test_editor
	git init test_repo
	cd test_repo
	echo 1 > 1 && git add 1 && git commit -m 1
	git checkout -b topic
	seq 2 5 |
	while read n
	do
		echo $n > $n
		git add $n
		git commit -m $n
	done
	GIT_EDITOR=../test_editor git rebase -i -p master
	test $(git log --oneline | wc -l) = 4 || echo FAILED

This fails as written, succeeds if "-p" is removed from the 'git rebase'
line.

The problem is that 'git rebase -i -p' is not good for anything but
transplanting a branch wholesale at the moment.  This report would be
a wishlist bug for a new feature, if the documentation did not already
misleadingly promise so much.

Sorry I can’t help much.  Hope that makes things a little clearer, anyway.

Jonathan

#572851#29
Date:
2010-03-06 15:44:15 UTC
From:
To:
Hi again,

Jiří Paleček explained how trying to interactively rebase

1 --- 2 --- 3 --- 4 --- 5

to

1 --- 2+4 --- 3 --- 5

with git rebase -i -p fails utterly (the 3 is just forgotten).

I got a chance to look at this briefly again, and here’s what I
learned.

The current code
----------------

The “git rebase -i -p” code is very lightly modified from
“git rebase -i”.  The list of commits you edit does not represent the
topology of the revision graph.  It is just a list of pick lines for
_all_ commits in HEAD but not upstream, just like what you would get
with “git rebase -i”.

Example: given the history

     4 --- 5
    /       \
  3 --- 6 --- 7 [HEAD, master]
 /
1 --- 2 [upstream]

the “insn sheet” produced by “git rebase -i -p upstream” is simply
something like:

 pick 6a2a427 3
 pick 5437c76 6
 pick ab03bd2 4
 pick 1adb5b5 5
 pick f021136 7

(try it!).

The pick insn then looks to the original commits to figure out what
the parents of the new commit should be!  So you can’t change their
parentage, which means this is only good for transplanting a branch
wholesale.

A past attempt to fix it
------------------------
Jörg Sommer made a valiant attempt to fix this in a series including
a patch called “Teach rebase interactive the mark command”.  The idea
is that if one wants to transplant a history like the above by hand,
you might do something like this:

 git checkout 2
 git cherry-pick 3
 git tag A
 git cherry-pick 4
 git cherry-pick 5
 git tag B
 git checkout A
 git cherry-pick 6
 git merge B -m 7
 git tag -d A
 git tag -d B

The tags here are temporary bookmarkers to allow moving around within
the newly created history.  Jörg’s idea was to use a new “mark” insn
to create what are in effect temporary tags like this.

Reception of the mark command
-----------------------------
Johannes Schindelin wrote [1]:

 Actually, I think that this whole "mark" stuff is way too complicated,
 as can be seen by the amount of patches needed to get it somewhere
 usable.

 I would like it much better, if there was something like

  pick 5cc8f37 (init: show "Reinit" message even in ...)
  pick 18d077c (quiltimport: fix misquoting of parse...)
  merge 9876543:5cc8f37,18d077c (Merge blub)
  reset 5cc8f37
  ...

 I.e. like with filter-branch, and like with rebase -i -p in its
 current form, we take the _original_ names as keys as to which
 commits to merge, or where to reset to.

which makes a lot of sense.  In other words, it is a pain to maintain
an insn list with many markers in it (and which do not have meaningful
names).  It would be simpler to be able to refer to the commits
themselves.

Another variant by Dmitry Potapov [2]:

 Maybe, it would be better if re-written commits were marked a bit
 differently, so there will be no confusion about whether an original
 or re-written commit is referred. For instance, re-written commits
 can be marked by adding apostrophe at the end, so if the original
 commit was "abcdef" then the re-written should be called as
 "abcdef'". At least, it will make plain clear for anyone where in
 merge rewritten commits are mentioned. Otherwise, it looks too
 magical to me.

Dscho has written a lot more about this elsewhere, but it is kind of
hard to find today since gmane is down.

[1] http://marc.info/?l=git&m=120818225406611&w=2
[2] http://marc.info/?l=git&m=120956084007135&w=2

Recent work
-----------
See the thread http://markmail.org/message/jzi7xbotwq6bhb7b
and the repositories

http://repo.or.cz/w/git/price.git
and http://repo.or.cz/w/git/dscho.git/shortlog/refs/heads/rebase-i-p

Please keep me informed if you get a chance to work on this.

Many thanks,
Jonathan