rebase -i: support --root without --onto

Allow --root to be specified to rebase -i without --onto, making it possible to edit and re-order all commits right back to the root(s). If there is a conflict to be resolved when applying the first change, the user will expect a sane index and working tree to get sensible behaviour from git-diff and friends, so create a sentinel commit with an empty tree to rebase onto. Automatically squash the sentinel with any commits rebased directly onto it, so they end up as root commits in their own right and retain their authorship and commit message. Implicitly use rebase -i for non-interactive rebase of --root without an --onto argument now that rebase -i can correctly do this. Signed-off-by: Chris Webb <chris@arachsys.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Chris Webb committed Jun 26, 2012 at 22:55 UTC df5df20c1308f936ea542c86df1e9c6974168472
3 files changed +43 -12
Documentation/git-rebase.txt
+5 -4
@@ -10,7 +10,7 @@ SYNOPSIS
10 [verse]
11 'git rebase' [-i | --interactive] [options] [--onto <newbase>]
12 [<upstream>] [<branch>]
13 -'git rebase' [-i | --interactive] [options] --onto <newbase>
13 +'git rebase' [-i | --interactive] [options] [--onto <newbase>]
14 --root [<branch>]
15 'git rebase' --continue | --skip | --abort
16
@@ -348,10 +348,11 @@ idea unless you know what you are doing (see BUGS below).
348 --root::
349 Rebase all commits reachable from <branch>, instead of
350 limiting them with an <upstream>. This allows you to rebase
351 - the root commit(s) on a branch. Must be used with --onto, and
351 + the root commit(s) on a branch. When used with --onto, it
352 will skip changes already contained in <newbase> (instead of
353 - <upstream>). When used together with --preserve-merges, 'all'
354 - root commits will be rewritten to have <newbase> as parent
353 + <upstream>) whereas without --onto it will operate on every change.
354 + When used together with both --onto and --preserve-merges,
355 + 'all' root commits will be rewritten to have <newbase> as parent
356 instead.
357
358 --autosquash::
git-rebase--interactive.sh
+26 -6
@@ -417,6 +417,29 @@ record_in_rewritten() {
417 esac
418 }
419
420 +do_pick () {
421 + if test "$(git rev-parse HEAD)" = "$squash_onto"
422 + then
423 + # Set the correct commit message and author info on the
424 + # sentinel root before cherry-picking the original changes
425 + # without committing (-n). Finally, update the sentinel again
426 + # to include these changes. If the cherry-pick results in a
427 + # conflict, this means our behaviour is similar to a standard
428 + # failed cherry-pick during rebase, with a dirty index to
429 + # resolve before manually running git commit --amend then git
430 + # rebase --continue.
431 + git commit --allow-empty --allow-empty-message --amend \
432 + --no-post-rewrite -n -q -C $1 &&
433 + pick_one -n $1 &&
434 + git commit --allow-empty --allow-empty-message \
435 + --amend --no-post-rewrite -n -q -C $1 ||
436 + die_with_patch $1 "Could not apply $1... $2"
437 + else
438 + pick_one $1 ||
439 + die_with_patch $1 "Could not apply $1... $2"
440 + fi
441 +}
442 +
443 do_next () {
444 rm -f "$msg" "$author_script" "$amend" || exit
445 read -r command sha1 rest < "$todo"
@@ -428,16 +451,14 @@ do_next () {
451 comment_for_reflog pick
452
453 mark_action_done
431 - pick_one $sha1 ||
432 - die_with_patch $sha1 "Could not apply $sha1... $rest"
454 + do_pick $sha1 "$rest"
455 record_in_rewritten $sha1
456 ;;
457 reword|r)
458 comment_for_reflog reword
459
460 mark_action_done
439 - pick_one $sha1 ||
440 - die_with_patch $sha1 "Could not apply $sha1... $rest"
461 + do_pick $sha1 "$rest"
462 git commit --amend --no-post-rewrite || {
463 warn "Could not amend commit after successfully picking $sha1... $rest"
464 warn "This is most likely due to an empty commit message, or the pre-commit hook"
@@ -451,8 +472,7 @@ do_next () {
472 comment_for_reflog edit
473
474 mark_action_done
454 - pick_one $sha1 ||
455 - die_with_patch $sha1 "Could not apply $sha1... $rest"
475 + do_pick $sha1 "$rest"
476 warn "Stopped at $sha1... $rest"
477 exit_with_patch $sha1 0
478 ;;
git-rebase.sh
+12 -2
@@ -31,7 +31,7 @@ SUBDIRECTORY_OK=Yes
31 OPTIONS_KEEPDASHDASH=
32 OPTIONS_SPEC="\
33 git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>]
34 -git rebase [-i] [options] --onto <newbase> --root [<branch>]
34 +git rebase [-i] [options] [--onto <newbase>] --root [<branch>]
35 git-rebase [-i] --continue | --abort | --skip
36 --
37 Available options are
@@ -364,6 +364,11 @@ and run me again. I am stopping in case you still have something
364 valuable there.'
365 fi
366
367 +if test -n "$rebase_root" && test -z "$onto"
368 +then
369 + test -z "$interactive_rebase" && interactive_rebase=implied
370 +fi
371 +
372 if test -n "$interactive_rebase"
373 then
374 type=interactive
@@ -397,7 +402,12 @@ then
402 die "invalid upstream $upstream_name"
403 upstream_arg="$upstream_name"
404 else
400 - test -z "$onto" && die "You must specify --onto when using --root"
405 + if test -z "$onto"
406 + then
407 + empty_tree=`git hash-object -t tree /dev/null`
408 + onto=`git commit-tree $empty_tree </dev/null`
409 + squash_onto="$onto"
410 + fi
411 unset upstream_name
412 unset upstream
413 upstream_arg=--root