* [PATCH 0/3] rebase --root @ 2008-12-29 16:45 Thomas Rast [not found] ` <cover.1230569041.git.trast@student.ethz.ch> 0 siblings, 1 reply; 27+ messages in thread From: Thomas Rast @ 2008-12-29 16:45 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Inspired by f95ebf7 (Allow cherry-picking root commits, 2008-07-04), this teaches git rebase to rebase everything up to the root commit(s). The main use-case for this is if you hack on new history, then later notice that you want to unroll the commits "on" a git-svn branch to dcommit them separately. (If you just want to squash them, a merge will do fine.) Note that --root (in both modes) requires --onto; you cannot rebase "onto nothing" to edit the root commit. Such an option --onto-nothing might be a worthwile feature however, since it would also allow "detaching" a set of commits from their parent history. BTW, while hacking this I noticed that there are two different tests t3409: t3409-rebase-hook.sh t3409-rebase-preserve-merges.sh Is this okay, or should one of them be renamed? Thomas Rast (3): rebase: learn to rebase root commit rebase -i: learn to rebase root commit rebase: update documentation for --root Documentation/git-rebase.txt | 17 +++++++--- git-rebase--interactive.sh | 53 +++++++++++++++++++++++++-------- git-rebase.sh | 51 ++++++++++++++++++++++---------- t/t3412-rebase-root.sh | 66 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 34 deletions(-) create mode 100755 t/t3412-rebase-root.sh ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <cover.1230569041.git.trast@student.ethz.ch>]
* rebase: learn to rebase root commit [not found] ` <cover.1230569041.git.trast@student.ethz.ch> @ 2008-12-29 16:45 ` Thomas Rast 2008-12-29 16:45 ` rebase -i: " Thomas Rast 2008-12-29 16:45 ` rebase: update documentation for --root Thomas Rast 2 siblings, 0 replies; 27+ messages in thread From: Thomas Rast @ 2008-12-29 16:45 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Teach git-rebase a new option --root, which instructs it to rebase the entire history leading up to <branch>. The main use-case is with git-svn: suppose you start hacking (perhaps offline) on a new project, but later notice you want to commit this work to SVN. You will have to rebase the entire history, including the root commit, on a (possibly empty) commit coming from git-svn, to establish a history connection. This previously had to be done by cherry-picking the root commit manually. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- git-rebase.sh | 51 ++++++++++++++++++++++++++++++++-------------- t/t3412-rebase-root.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 16 deletions(-) create mode 100755 t/t3412-rebase-root.sh diff --git a/git-rebase.sh b/git-rebase.sh index ebd4df3..89de3c4 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]' +USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -47,6 +47,7 @@ dotest="$GIT_DIR"/rebase-merge prec=4 verbose= git_am_opt= +rebase_root= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -297,6 +298,9 @@ do -C*) git_am_opt="$git_am_opt $1" ;; + --root) + rebase_root=t + ;; -*) usage ;; @@ -344,17 +348,23 @@ case "$diff" in ;; esac +if test -z "$rebase_root"; then # The upstream head must be given. Make sure it is valid. -upstream_name="$1" -upstream=`git rev-parse --verify "${upstream_name}^0"` || - die "invalid upstream $upstream_name" + upstream_name="$1" + shift + upstream=`git rev-parse --verify "${upstream_name}^0"` || + die "invalid upstream $upstream_name" +fi + +test ! -z "$rebase_root" -a -z "$newbase" && + die "--root must be used with --onto" # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -run_pre_rebase_hook ${1+"$@"} +run_pre_rebase_hook ${upstream_name+"$upstream_name"} "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -362,16 +372,16 @@ run_pre_rebase_hook ${1+"$@"} # $head_name -- refs/heads/<that-branch> or "detached HEAD" switch_to= case "$#" in -2) +1) # Is it "rebase other $branchname" or "rebase other $commit"? - branch_name="$2" - switch_to="$2" + branch_name="$1" + switch_to="$1" - if git show-ref --verify --quiet -- "refs/heads/$2" && - branch=$(git rev-parse -q --verify "refs/heads/$2") + if git show-ref --verify --quiet -- "refs/heads/$1" && + branch=$(git rev-parse -q --verify "refs/heads/$1") then - head_name="refs/heads/$2" - elif branch=$(git rev-parse -q --verify "$2") + head_name="refs/heads/$1" + elif branch=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -393,7 +403,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch on top of $onto +# Now we are rebasing commits $upstream..$branch (or simply $branch +# with --root) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -429,10 +440,18 @@ then exit 0 fi +if test ! -z "$rebase_root"; then + revisions="$orig_head" + fp_flag="--root" +else + revisions="$upstream..$orig_head" + fp_flag="--ignore-if-in-upstream" +fi + if test -z "$do_merge" then - git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - "$upstream..$orig_head" | + git format-patch -k --stdout --full-index "$fp_flag" \ + "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -455,7 +474,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` +for cmt in `git rev-list --reverse --no-merges "$revisions"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh new file mode 100755 index 0000000..63ec5e6 --- /dev/null +++ b/t/t3412-rebase-root.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='git rebase --root + +Tests if git rebase --root --onto <newparent> can rebase the root commit. +' +. ./test-lib.sh + +test_expect_success 'prepare repository' ' + echo 1 > A && + git add A && + git commit -m 1 && + echo 2 > A && + git add A && + git commit -m 2 && + git symbolic-ref HEAD refs/heads/other && + rm .git/index && + rm A && + echo 3 > B && + git add B && + git commit -m 3 && + echo 4 > B && + git add B && + git commit -m 4 +' + +test_expect_success 'rebase --root expects --onto' ' + test_must_fail git rebase --root +' + +cat > expect <<EOF +4 +3 +2 +1 +EOF + +test_expect_success 'rebase --root --onto <newbase>' ' + git checkout -b work && + git rebase --root --onto master && + git log --pretty=tformat:"%s" > rebased && + test_cmp expect rebased +' + +test_expect_success 'rebase --root --onto <newbase> <branch>' ' + git branch work2 other && + git rebase --root --onto master work2 && + git log --pretty=tformat:"%s" > rebased2 && + test_cmp expect rebased2 +' + +test_done -- 1.6.1.1.g4c1d9.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* rebase -i: learn to rebase root commit [not found] ` <cover.1230569041.git.trast@student.ethz.ch> 2008-12-29 16:45 ` rebase: learn to rebase root commit Thomas Rast @ 2008-12-29 16:45 ` Thomas Rast 2008-12-29 21:49 ` Thomas Rast 2008-12-29 16:45 ` rebase: update documentation for --root Thomas Rast 2 siblings, 1 reply; 27+ messages in thread From: Thomas Rast @ 2008-12-29 16:45 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Teach git-rebase -i a new option --root, which instructs it to rebase the entire history leading up to <branch>. This is mainly for symmetry with ordinary git-rebase; it cannot be used to edit the root commit in-place (it requires --onto). Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- git-rebase--interactive.sh | 53 +++++++++++++++++++++++++++++++++---------- t/t3412-rebase-root.sh | 14 +++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c8b0861..f29b5ee 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -27,6 +27,7 @@ continue continue rebasing process abort abort rebasing process and restore original branch skip skip current patch and continue rebasing process no-verify override pre-rebase hook from stopping the operation +root rebase all reachable commmits up to the root(s) " . git-sh-setup @@ -44,6 +45,7 @@ STRATEGY= ONTO= VERBOSE= OK_TO_SKIP_PRE_REBASE= +REBASE_ROOT= GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add <paths>', and @@ -154,6 +156,11 @@ pick_one () { output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return + if test ! -z "$REBASE_ROOT" + then + output git cherry-pick "$@" + return + fi parent_sha1=$(git rev-parse --verify $sha1^) || die "Could not get the parent of $sha1" current_sha1=$(git rev-parse --verify HEAD) @@ -443,6 +450,7 @@ get_saved_options () { test -d "$REWRITTEN" && PRESERVE_MERGES=t test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" test -f "$DOTEST"/verbose && VERBOSE=t + test ! -s "$DOTEST"/upstream && REBASE_ROOT=t } while test $# != 0 @@ -547,6 +555,9 @@ first and then run 'git rebase --continue' again." -i) # yeah, we know ;; + --root) + REBASE_ROOT=t + ;; --onto) shift ONTO=$(git rev-parse --verify "$1") || @@ -555,7 +566,7 @@ first and then run 'git rebase --continue' again." --) shift run_pre_rebase_hook ${1+"$@"} - test $# -eq 1 -o $# -eq 2 || usage + test ! -z "$REBASE_ROOT" -o $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@ -566,15 +577,22 @@ first and then run 'git rebase --continue' again." require_clean_work_tree - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" - test -z "$ONTO" && ONTO=$UPSTREAM + if test -z "$REBASE_ROOT" + then + UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + test -z "$ONTO" && ONTO=$UPSTREAM + shift + else + test -z "$ONTO" && + die "You must specify --onto when using --root" + fi - if test ! -z "$2" + if test ! -z "$1" then - output git show-ref --verify --quiet "refs/heads/$2" || - die "Invalid branchname: $2" - output git checkout "$2" || - die "Could not checkout $2" + output git show-ref --verify --quiet "refs/heads/$1" || + die "Invalid branchname: $1" + output git checkout "$1" || + die "Could not checkout $1" fi HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" @@ -613,12 +631,21 @@ first and then run 'git rebase --continue' again." MERGES_OPTION="--no-merges --cherry-pick" fi - SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) SHORTHEAD=$(git rev-parse --short $HEAD) SHORTONTO=$(git rev-parse --short $ONTO) + if test -z "$REBASE_ROOT" + # this is now equivalent to ! -z "$UPSTREAM" + then + SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) + REVISIONS=$UPSTREAM...$HEAD + SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD + else + REVISIONS=$HEAD + SHORTREVISIONS=$SHORTHEAD + fi git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse --left-right --topo-order \ - $UPSTREAM...$HEAD | \ + $REVISIONS | \ sed -n "s/^>//p" | while read shortsha1 rest do if test t != "$PRESERVE_MERGES" @@ -647,11 +674,11 @@ first and then run 'git rebase --continue' again." then mkdir "$DROPPED" # Save all non-cherry-picked changes - git rev-list $UPSTREAM...$HEAD --left-right --cherry-pick | \ + git rev-list $REVISIONS --left-right --cherry-pick | \ sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks # Now all commits and note which ones are missing in # not-cherry-picks and hence being dropped - git rev-list $UPSTREAM..$HEAD | + git rev-list $REVISIONS | while read rev do if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = "" @@ -670,7 +697,7 @@ first and then run 'git rebase --continue' again." test -s "$TODO" || echo noop >> "$TODO" cat >> "$TODO" << EOF -# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO +# Rebase $SHORTREVISIONS onto $SHORTONTO # # Commands: # p, pick = use commit diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 63ec5e6..bb44a52 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -49,4 +49,18 @@ test_expect_success 'rebase --root --onto <newbase> <branch>' ' test_cmp expect rebased2 ' +test_expect_success 'rebase -i --root --onto <newbase>' ' + git checkout -b work3 other && + GIT_EDITOR=: git rebase -i --root --onto master && + git log --pretty=tformat:"%s" > rebased3 && + test_cmp expect rebased3 +' + +test_expect_success 'rebase -i --root --onto <newbase> <branch>' ' + git branch work4 other && + GIT_EDITOR=: git rebase -i --root --onto master work4 && + git log --pretty=tformat:"%s" > rebased4 && + test_cmp expect rebased4 +' + test_done -- 1.6.1.1.g4c1d9.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: rebase -i: learn to rebase root commit 2008-12-29 16:45 ` rebase -i: " Thomas Rast @ 2008-12-29 21:49 ` Thomas Rast 2008-12-29 22:21 ` Boyd Stephen Smith Jr. 2008-12-30 8:22 ` rebase -i: " Junio C Hamano 0 siblings, 2 replies; 27+ messages in thread From: Thomas Rast @ 2008-12-29 21:49 UTC (permalink / raw) To: git; +Cc: Junio C Hamano [-- Attachment #1: Type: text/plain, Size: 710 bytes --] Thomas Rast wrote: > Teach git-rebase -i a new option --root, which instructs it to rebase > the entire history leading up to <branch>. This is mainly for > symmetry with ordinary git-rebase; it cannot be used to edit the root > commit in-place (it requires --onto). Actually, I forgot the "rebase -i -p" code path, which dies if --root is used with -p. Apologies. So for now, consider this broken and RFC: is there any sensible use/interpretation of -p --root that I'm missing? Or should it just disallow this combination? [I also seem to manage to shoot myself with format-patch & topgit every time, no matter how trivial the issue.] -- Thomas Rast trast@{inf,student}.ethz.ch [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: rebase -i: learn to rebase root commit 2008-12-29 21:49 ` Thomas Rast @ 2008-12-29 22:21 ` Boyd Stephen Smith Jr. 2008-12-30 12:23 ` Thomas Rast 2008-12-30 8:22 ` rebase -i: " Junio C Hamano 1 sibling, 1 reply; 27+ messages in thread From: Boyd Stephen Smith Jr. @ 2008-12-29 22:21 UTC (permalink / raw) To: Thomas Rast; +Cc: git, Junio C Hamano [-- Attachment #1: Type: text/plain, Size: 1456 bytes --] On Monday 2008 December 29 15:49:42 Thomas Rast wrote: > Thomas Rast wrote: > > Teach git-rebase -i a new option --root, which instructs it to rebase > > the entire history leading up to <branch>. This is mainly for > > symmetry with ordinary git-rebase; it cannot be used to edit the root > > commit in-place (it requires --onto). > > Actually, I forgot the "rebase -i -p" code path, which dies if --root > is used with -p. Apologies. > > So for now, consider this broken and RFC: is there any sensible > use/interpretation of -p --root that I'm missing? Or should it just > disallow this combination? Here's the interpretation that *I* come up with for -p --root used together: The commit with no parents (OLD_ROOT) is rebased as if -p were not given, call the resulting commit NEW_ROOT. Then, the rebase continues as if "--onto NEW_ROOT OLD_ROOT <branch>" was specified instead of "--onto=NEW_ROOT^ --root <branch>". Basically, --root only changes how the first commit is handled, which I think is consistent with other uses of --root. It's also similar to cherry-picking the first commit, follwed by a non-root rebase, which I think is also consistent with the intention of --root. -- Boyd Stephen Smith Jr. ,= ,-_-. =. bss@iguanasuicide.net ((_/)o o(\_)) ICQ: 514984 YM/AIM: DaTwinkDaddy `-'(. .)`-' http://iguanasuicide.net/ \_/ [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: rebase -i: learn to rebase root commit 2008-12-29 22:21 ` Boyd Stephen Smith Jr. @ 2008-12-30 12:23 ` Thomas Rast 2008-12-30 12:29 ` [PATCH v2 1/3] rebase: " Thomas Rast 0 siblings, 1 reply; 27+ messages in thread From: Thomas Rast @ 2008-12-30 12:23 UTC (permalink / raw) To: git; +Cc: Boyd Stephen Smith Jr. [-- Attachment #1: Type: text/plain, Size: 2042 bytes --] Boyd Stephen Smith Jr. wrote: > Here's the interpretation that *I* come up with for -p --root used together: > The commit with no parents (OLD_ROOT) is rebased as if -p were not given, call > the resulting commit NEW_ROOT. Then, the rebase continues as if "--onto > NEW_ROOT OLD_ROOT <branch>" was specified instead of "--onto=NEW_ROOT^ --root > <branch>". I like this logic, but it feels inconsistent as soon as there are several root commits. (This may be somewhat academic, since any repo with several roots should also be able to cope with a merge...) Some digging into the -p code shows that it knows which commits were rewritten, and which were untouched. It rewrites such that _all_ commits in $(git merge-base --all $branch $upstream) are rewritten to look like $onto instead, i.e., all their occurrences in parent lists of commits are rewritten to $onto. All other commits are only rewritten if they have a parent that was rewritten. So I think one sane way is to define a virtual parent 'root', and think of parentless commits as having the (sole) parent 'root'. Then we can rewrite such that 'root' becomes $onto, i.e., all occurrences of 'root' in parent lists become $onto, consistent with the normal operation. (For the other commits, the same rule as above is applied.) Of course this just boils down to saying that _all_ root commits reachable from $branch are rewritten to have $onto as their parent. Subsequently, all other commits will also be rewritten because they all must have at least one rewritten parent. > Basically, --root only changes how the first commit is handled, which I think > is consistent with other uses of --root. It's also similar to cherry-picking > the first commit, follwed by a non-root rebase, which I think is also > consistent with the intention of --root. I believe this remark still holds if there is only a single root commit on $branch. I will reroll with an updated 2/3 shortly. -- Thomas Rast trast@{inf,student}.ethz.ch [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2 1/3] rebase: learn to rebase root commit 2008-12-30 12:23 ` Thomas Rast @ 2008-12-30 12:29 ` Thomas Rast 2008-12-30 12:29 ` [PATCH v2 2/3] rebase -i: " Thomas Rast 2009-01-01 21:00 ` [PATCH v2 1/3] rebase: learn to rebase root commit Junio C Hamano 0 siblings, 2 replies; 27+ messages in thread From: Thomas Rast @ 2008-12-30 12:29 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Teach git-rebase a new option --root, which instructs it to rebase the entire history leading up to <branch>. The main use-case is with git-svn: suppose you start hacking (perhaps offline) on a new project, but later notice you want to commit this work to SVN. You will have to rebase the entire history, including the root commit, on a (possibly empty) commit coming from git-svn, to establish a history connection. This previously had to be done by cherry-picking the root commit manually. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- No changes since v1. git-rebase.sh | 51 ++++++++++++++++++++++++++++++++-------------- t/t3412-rebase-root.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 16 deletions(-) create mode 100755 t/t3412-rebase-root.sh diff --git a/git-rebase.sh b/git-rebase.sh index ebd4df3..89de3c4 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]' +USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -47,6 +47,7 @@ dotest="$GIT_DIR"/rebase-merge prec=4 verbose= git_am_opt= +rebase_root= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -297,6 +298,9 @@ do -C*) git_am_opt="$git_am_opt $1" ;; + --root) + rebase_root=t + ;; -*) usage ;; @@ -344,17 +348,23 @@ case "$diff" in ;; esac +if test -z "$rebase_root"; then # The upstream head must be given. Make sure it is valid. -upstream_name="$1" -upstream=`git rev-parse --verify "${upstream_name}^0"` || - die "invalid upstream $upstream_name" + upstream_name="$1" + shift + upstream=`git rev-parse --verify "${upstream_name}^0"` || + die "invalid upstream $upstream_name" +fi + +test ! -z "$rebase_root" -a -z "$newbase" && + die "--root must be used with --onto" # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -run_pre_rebase_hook ${1+"$@"} +run_pre_rebase_hook ${upstream_name+"$upstream_name"} "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -362,16 +372,16 @@ run_pre_rebase_hook ${1+"$@"} # $head_name -- refs/heads/<that-branch> or "detached HEAD" switch_to= case "$#" in -2) +1) # Is it "rebase other $branchname" or "rebase other $commit"? - branch_name="$2" - switch_to="$2" + branch_name="$1" + switch_to="$1" - if git show-ref --verify --quiet -- "refs/heads/$2" && - branch=$(git rev-parse -q --verify "refs/heads/$2") + if git show-ref --verify --quiet -- "refs/heads/$1" && + branch=$(git rev-parse -q --verify "refs/heads/$1") then - head_name="refs/heads/$2" - elif branch=$(git rev-parse -q --verify "$2") + head_name="refs/heads/$1" + elif branch=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -393,7 +403,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch on top of $onto +# Now we are rebasing commits $upstream..$branch (or simply $branch +# with --root) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -429,10 +440,18 @@ then exit 0 fi +if test ! -z "$rebase_root"; then + revisions="$orig_head" + fp_flag="--root" +else + revisions="$upstream..$orig_head" + fp_flag="--ignore-if-in-upstream" +fi + if test -z "$do_merge" then - git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - "$upstream..$orig_head" | + git format-patch -k --stdout --full-index "$fp_flag" \ + "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -455,7 +474,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` +for cmt in `git rev-list --reverse --no-merges "$revisions"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh new file mode 100755 index 0000000..63ec5e6 --- /dev/null +++ b/t/t3412-rebase-root.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='git rebase --root + +Tests if git rebase --root --onto <newparent> can rebase the root commit. +' +. ./test-lib.sh + +test_expect_success 'prepare repository' ' + echo 1 > A && + git add A && + git commit -m 1 && + echo 2 > A && + git add A && + git commit -m 2 && + git symbolic-ref HEAD refs/heads/other && + rm .git/index && + rm A && + echo 3 > B && + git add B && + git commit -m 3 && + echo 4 > B && + git add B && + git commit -m 4 +' + +test_expect_success 'rebase --root expects --onto' ' + test_must_fail git rebase --root +' + +cat > expect <<EOF +4 +3 +2 +1 +EOF + +test_expect_success 'rebase --root --onto <newbase>' ' + git checkout -b work && + git rebase --root --onto master && + git log --pretty=tformat:"%s" > rebased && + test_cmp expect rebased +' + +test_expect_success 'rebase --root --onto <newbase> <branch>' ' + git branch work2 other && + git rebase --root --onto master work2 && + git log --pretty=tformat:"%s" > rebased2 && + test_cmp expect rebased2 +' + +test_done -- 1.6.1.20.g7f5c5.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 2/3] rebase -i: learn to rebase root commit 2008-12-30 12:29 ` [PATCH v2 1/3] rebase: " Thomas Rast @ 2008-12-30 12:29 ` Thomas Rast 2008-12-30 12:29 ` [PATCH v2 3/3] rebase: update documentation for --root Thomas Rast 2009-01-01 21:00 ` [PATCH v2 1/3] rebase: learn to rebase root commit Junio C Hamano 1 sibling, 1 reply; 27+ messages in thread From: Thomas Rast @ 2008-12-30 12:29 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Teach git-rebase -i a new option --root, which instructs it to rebase the entire history leading up to <branch>. This is mainly for symmetry with ordinary git-rebase; it cannot be used to edit the root commit in-place (it requires --onto). In the normal mode of operation, this is fairly straightforward. We run cherry-pick in a loop, and cherry-pick has supported picking the root commit since f95ebf7 (Allow cherry-picking root commits, 2008-07-04). In --preserve-merges mode, we track the mapping from old to rewritten commits and use it to update the parent list of each commit. In this case, we define 'rebase -i -p --root --onto $onto $branch' to rewrite the parent list of all root commit(s) on $branch to contain $onto instead. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- Changes since v1: also supports -p --root, with tests. git-rebase--interactive.sh | 99 +++++++++++++++++++++++++++++++------------ t/t3412-rebase-root.sh | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 28 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c8b0861..d6f54eb 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -27,6 +27,7 @@ continue continue rebasing process abort abort rebasing process and restore original branch skip skip current patch and continue rebasing process no-verify override pre-rebase hook from stopping the operation +root rebase all reachable commmits up to the root(s) " . git-sh-setup @@ -44,6 +45,7 @@ STRATEGY= ONTO= VERBOSE= OK_TO_SKIP_PRE_REBASE= +REBASE_ROOT= GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add <paths>', and @@ -154,6 +156,11 @@ pick_one () { output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return + if test ! -z "$REBASE_ROOT" + then + output git cherry-pick "$@" + return + fi parent_sha1=$(git rev-parse --verify $sha1^) || die "Could not get the parent of $sha1" current_sha1=$(git rev-parse --verify HEAD) @@ -197,7 +204,11 @@ pick_one_preserving_merges () { # rewrite parents; if none were rewritten, we can fast-forward. new_parents= - pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)" + pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)" + if test "$pend" = " " + then + pend=" root" + fi while [ "$pend" != "" ] do p=$(expr "$pend" : ' \([^ ]*\)') @@ -443,6 +454,7 @@ get_saved_options () { test -d "$REWRITTEN" && PRESERVE_MERGES=t test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" test -f "$DOTEST"/verbose && VERBOSE=t + test ! -s "$DOTEST"/upstream && REBASE_ROOT=t } while test $# != 0 @@ -547,6 +559,9 @@ first and then run 'git rebase --continue' again." -i) # yeah, we know ;; + --root) + REBASE_ROOT=t + ;; --onto) shift ONTO=$(git rev-parse --verify "$1") || @@ -555,7 +570,7 @@ first and then run 'git rebase --continue' again." --) shift run_pre_rebase_hook ${1+"$@"} - test $# -eq 1 -o $# -eq 2 || usage + test ! -z "$REBASE_ROOT" -o $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@ -566,15 +581,22 @@ first and then run 'git rebase --continue' again." require_clean_work_tree - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" - test -z "$ONTO" && ONTO=$UPSTREAM + if test -z "$REBASE_ROOT" + then + UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + test -z "$ONTO" && ONTO=$UPSTREAM + shift + else + test -z "$ONTO" && + die "You must specify --onto when using --root" + fi - if test ! -z "$2" + if test ! -z "$1" then - output git show-ref --verify --quiet "refs/heads/$2" || - die "Invalid branchname: $2" - output git checkout "$2" || - die "Could not checkout $2" + output git show-ref --verify --quiet "refs/heads/$1" || + die "Invalid branchname: $1" + output git checkout "$1" || + die "Could not checkout $1" fi HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" @@ -598,12 +620,19 @@ first and then run 'git rebase --continue' again." # This ensures that commits on merged, but otherwise # unrelated side branches are left alone. (Think "X" # in the man page's example.) - mkdir "$REWRITTEN" && - for c in $(git merge-base --all $HEAD $UPSTREAM) - do - echo $ONTO > "$REWRITTEN"/$c || + if test -z "$REBASE_ROOT" + then + mkdir "$REWRITTEN" && + for c in $(git merge-base --all $HEAD $UPSTREAM) + do + echo $ONTO > "$REWRITTEN"/$c || + die "Could not init rewritten commits" + done + else + mkdir "$REWRITTEN" && + echo $ONTO > "$REWRITTEN"/root || die "Could not init rewritten commits" - done + fi # No cherry-pick because our first pass is to determine # parents to rewrite and skipping dropped commits would # prematurely end our probe @@ -613,12 +642,21 @@ first and then run 'git rebase --continue' again." MERGES_OPTION="--no-merges --cherry-pick" fi - SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) SHORTHEAD=$(git rev-parse --short $HEAD) SHORTONTO=$(git rev-parse --short $ONTO) + if test -z "$REBASE_ROOT" + # this is now equivalent to ! -z "$UPSTREAM" + then + SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) + REVISIONS=$UPSTREAM...$HEAD + SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD + else + REVISIONS=$HEAD + SHORTREVISIONS=$SHORTHEAD + fi git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse --left-right --topo-order \ - $UPSTREAM...$HEAD | \ + $REVISIONS | \ sed -n "s/^>//p" | while read shortsha1 rest do if test t != "$PRESERVE_MERGES" @@ -626,14 +664,19 @@ first and then run 'git rebase --continue' again." echo "pick $shortsha1 $rest" >> "$TODO" else sha1=$(git rev-parse $shortsha1) - preserve=t - for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-) - do - if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \) - then - preserve=f - fi - done + if test -z "$REBASE_ROOT" + then + preserve=t + for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) + do + if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \) + then + preserve=f + fi + done + else + preserve=f + fi if test f = "$preserve" then touch "$REWRITTEN"/$sha1 @@ -647,11 +690,11 @@ first and then run 'git rebase --continue' again." then mkdir "$DROPPED" # Save all non-cherry-picked changes - git rev-list $UPSTREAM...$HEAD --left-right --cherry-pick | \ + git rev-list $REVISIONS --left-right --cherry-pick | \ sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks # Now all commits and note which ones are missing in # not-cherry-picks and hence being dropped - git rev-list $UPSTREAM..$HEAD | + git rev-list $REVISIONS | while read rev do if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = "" @@ -660,7 +703,7 @@ first and then run 'git rebase --continue' again." # not worthwhile, we don't want to track its multiple heads, # just the history of its first-parent for others that will # be rebasing on top of it - git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$rev + git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO" rm "$REWRITTEN"/$rev @@ -670,7 +713,7 @@ first and then run 'git rebase --continue' again." test -s "$TODO" || echo noop >> "$TODO" cat >> "$TODO" << EOF -# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO +# Rebase $SHORTREVISIONS onto $SHORTONTO # # Commands: # p, pick = use commit diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 63ec5e6..c845dfc 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -49,4 +49,86 @@ test_expect_success 'rebase --root --onto <newbase> <branch>' ' test_cmp expect rebased2 ' +test_expect_success 'rebase -i --root --onto <newbase>' ' + git checkout -b work3 other && + GIT_EDITOR=: git rebase -i --root --onto master && + git log --pretty=tformat:"%s" > rebased3 && + test_cmp expect rebased3 +' + +test_expect_success 'rebase -i --root --onto <newbase> <branch>' ' + git branch work4 other && + GIT_EDITOR=: git rebase -i --root --onto master work4 && + git log --pretty=tformat:"%s" > rebased4 && + test_cmp expect rebased4 +' + +test_expect_success 'rebase -i -p with linear history' ' + git checkout -b work5 other && + GIT_EDITOR=: git rebase -i -p --root --onto master && + git log --pretty=tformat:"%s" > rebased5 && + test_cmp expect rebased5 +' + +test_expect_success 'set up merge history' ' + git checkout other^ && + git checkout -b side && + echo 5 > C && + git add C && + git commit -m 5 && + git checkout other && + git merge side +' + +sed 's/#/ /g' > expect-side <<'EOF' +* Merge branch 'side' into other +|\## +| * 5 +* | 4 +|/## +* 3 +* 2 +* 1 +EOF + +test_expect_success 'rebase -i -p with merge' ' + git checkout -b work6 other && + GIT_EDITOR=: git rebase -i -p --root --onto master && + git log --graph --topo-order --pretty=tformat:"%s" > rebased6 && + test_cmp expect-side rebased6 +' + +test_expect_success 'set up second root and merge' ' + git symbolic-ref HEAD refs/heads/third && + rm .git/index && + rm A B C && + echo 6 > D && + git add D && + git commit -m 6 && + git checkout other && + git merge third +' + +sed 's/#/ /g' > expect-third <<'EOF' +* Merge branch 'third' into other +|\## +| * 6 +* | Merge branch 'side' into other +|\ \## +| * | 5 +* | | 4 +|/ /## +* | 3 +|/## +* 2 +* 1 +EOF + +test_expect_success 'rebase -i -p with two roots' ' + git checkout -b work7 other && + GIT_EDITOR=: git rebase -i -p --root --onto master && + git log --graph --topo-order --pretty=tformat:"%s" > rebased7 && + test_cmp expect-third rebased7 +' + test_done -- 1.6.1.20.g7f5c5.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v2 3/3] rebase: update documentation for --root 2008-12-30 12:29 ` [PATCH v2 2/3] rebase -i: " Thomas Rast @ 2008-12-30 12:29 ` Thomas Rast 0 siblings, 0 replies; 27+ messages in thread From: Thomas Rast @ 2008-12-30 12:29 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Since the new option depends on --onto and omission of <upstream>, use a separate invocation style, and omit most options to save space. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- Changed since v1: remark about the effect of --root --preserve-merges. Documentation/git-rebase.txt | 19 ++++++++++++++----- 1 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index c8ad86a..e3b4b83 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -8,10 +8,11 @@ git-rebase - Forward-port local commits to the updated upstream head SYNOPSIS -------- [verse] -'git rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] - [-s <strategy> | --strategy=<strategy>] [--no-verify] - [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges] - [--onto <newbase>] <upstream> [<branch>] +'git rebase' [-i | --interactive] [options] [--onto <newbase>] + <upstream> [<branch>] +'git rebase' [-i | --interactive] [options] --onto <newbase> + --root [<branch>] + 'git rebase' --continue | --skip | --abort DESCRIPTION @@ -22,7 +23,8 @@ it remains on the current branch. All changes made by commits in the current branch but that are not in <upstream> are saved to a temporary area. This is the same set -of commits that would be shown by `git log <upstream>..HEAD`. +of commits that would be shown by `git log <upstream>..HEAD` (or +`git log HEAD`, if --root is specified). The current branch is reset to <upstream>, or <newbase> if the --onto option was supplied. This has the exact same effect as @@ -255,6 +257,13 @@ OPTIONS --preserve-merges:: Instead of ignoring merges, try to recreate them. +--root:: + Rebase all commits reachable from <branch>, instead of + limiting them with an <upstream>. This allows you to rebase + the root commit(s) on a branch. Must be used with --onto. + When used together with --preserve-merges, 'all' root commits + will be rewritten to have <newbase> as parent instead. + include::merge-strategies.txt[] NOTES -- 1.6.1.20.g7f5c5.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit 2008-12-30 12:29 ` [PATCH v2 1/3] rebase: " Thomas Rast 2008-12-30 12:29 ` [PATCH v2 2/3] rebase -i: " Thomas Rast @ 2009-01-01 21:00 ` Junio C Hamano 2009-01-02 18:58 ` Johannes Schindelin 2009-01-02 22:20 ` Thomas Rast 1 sibling, 2 replies; 27+ messages in thread From: Junio C Hamano @ 2009-01-01 21:00 UTC (permalink / raw) To: Thomas Rast; +Cc: git Thomas Rast <trast@student.ethz.ch> writes: > Teach git-rebase a new option --root, which instructs it to rebase the > entire history leading up to <branch>. > > The main use-case is with git-svn: suppose you start hacking (perhaps > offline) on a new project, but later notice you want to commit this > work to SVN. You will have to rebase the entire history, including > the root commit, on a (possibly empty) commit coming from git-svn, to > establish a history connection. This previously had to be done by > cherry-picking the root commit manually. I like what this series tries to do. Using the --root option is probably a more natural way to do what people often do with the "add graft and filter-branch the whole history once" procedure. But it somewhat feels sad if the "main" use-case for this is to start your project in git and then migrate away by feeding your history to subversion ;-). > @@ -344,17 +348,23 @@ case "$diff" in > ;; > esac > > +if test -z "$rebase_root"; then > # The upstream head must be given. Make sure it is valid. > -upstream_name="$1" > -upstream=`git rev-parse --verify "${upstream_name}^0"` || > - die "invalid upstream $upstream_name" > + upstream_name="$1" > + shift > + upstream=`git rev-parse --verify "${upstream_name}^0"` || > + die "invalid upstream $upstream_name" > +fi > + > +test ! -z "$rebase_root" -a -z "$newbase" && > + die "--root must be used with --onto" This is much easier to read if it were: if test -z "$rebase_root" then ... do the upstream_name/upstream thing, such as upstream_name="$1" ... else ... do the rebase_root thing, including unset upstream unset upstream_name test -z "$newbase" && die "--root without --onto" ... fi (Mental note. You shifted positional parameters and the remainders need to be adjusted, which you seem to have done). > # Make sure the branch to rebase onto is valid. > onto_name=${newbase-"$upstream_name"} > onto=$(git rev-parse --verify "${onto_name}^0") || exit > > # If a hook exists, give it a chance to interrupt > -run_pre_rebase_hook ${1+"$@"} > +run_pre_rebase_hook ${upstream_name+"$upstream_name"} "$@" I do not think ${upstream_name+"$upstream_name"} is a good check to begin with, because presense of it does not necessarily mean the command was invoked without --root; it could have come from the environment of the invoker (hence the suggestion to unset the variable explicitly). And I do not think this is a good way to extend the calling convention of the hook, either. pre-rebase-hook used to always take upstream and optionally the explicit branch name. When --root is given, your code will give the hook a single parameter which is the explicit branch name (i.e. "we will switch to this branch and rebase it, are you Ok with it?"), but the hook will mistakenly think you are feeding the fork-point commit. Because an old pre-rebase-hook cannot verify --root case correctly anyway and needs to be updated, how about doing 'run_pre_rebase_hook --root "$@"' when --root was given? > @@ -393,7 +403,8 @@ case "$#" in > esac > orig_head=$branch > > -# Now we are rebasing commits $upstream..$branch on top of $onto > +# Now we are rebasing commits $upstream..$branch (or simply $branch > +# with --root) on top of $onto "or simply everything leading to $branch if --root is given"? > # Check if we are already based on $onto with linear history, > # but this should be done only when upstream and onto are the same. > @@ -429,10 +440,18 @@ then > exit 0 > fi > > +if test ! -z "$rebase_root"; then > + revisions="$orig_head" > + fp_flag="--root" > +else > + revisions="$upstream..$orig_head" > + fp_flag="--ignore-if-in-upstream" > +fi Hmph, the reason why --ignore-if-in-upstream is irrelevant when --root is given is because...? > diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh > new file mode 100755 > index 0000000..63ec5e6 > --- /dev/null > +++ b/t/t3412-rebase-root.sh > @@ -0,0 +1,52 @@ > +#!/bin/sh > + > +test_description='git rebase --root > ... > +test_done Including tests for the pre-rebase-hook would be nice. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit 2009-01-01 21:00 ` [PATCH v2 1/3] rebase: learn to rebase root commit Junio C Hamano @ 2009-01-02 18:58 ` Johannes Schindelin 2009-01-02 22:20 ` Thomas Rast 1 sibling, 0 replies; 27+ messages in thread From: Johannes Schindelin @ 2009-01-02 18:58 UTC (permalink / raw) To: Junio C Hamano; +Cc: Thomas Rast, git Hi, On Thu, 1 Jan 2009, Junio C Hamano wrote: > Thomas Rast <trast@student.ethz.ch> writes: > > > Teach git-rebase a new option --root, which instructs it to rebase the > > entire history leading up to <branch>. > > > > The main use-case is with git-svn: suppose you start hacking (perhaps > > offline) on a new project, but later notice you want to commit this > > work to SVN. You will have to rebase the entire history, including > > the root commit, on a (possibly empty) commit coming from git-svn, to > > establish a history connection. This previously had to be done by > > cherry-picking the root commit manually. > > I like what this series tries to do. Using the --root option is probably > a more natural way to do what people often do with the "add graft and > filter-branch the whole history once" procedure. > > But it somewhat feels sad if the "main" use-case for this is to start your > project in git and then migrate away by feeding your history to subversion > ;-). FWIW I had a single case where I could have used something like this myself, in my whole life. It was when I started to write git-edit-patch-series.sh in its own repository, only to realize at the end that I should have started it in a topic branch in my git.git tree. Ciao, Dscho ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit 2009-01-01 21:00 ` [PATCH v2 1/3] rebase: learn to rebase root commit Junio C Hamano 2009-01-02 18:58 ` Johannes Schindelin @ 2009-01-02 22:20 ` Thomas Rast 2009-01-02 22:28 ` [PATCH v3 1/4] rebase -i: execute hook only after argument checking Thomas Rast 2009-01-02 22:49 ` [PATCH v2 1/3] " Junio C Hamano 1 sibling, 2 replies; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:20 UTC (permalink / raw) To: Junio C Hamano; +Cc: git [-- Attachment #1: Type: text/plain, Size: 3524 bytes --] Junio C Hamano wrote: > Thomas Rast <trast@student.ethz.ch> writes: > > The main use-case is with git-svn: [...] > > I like what this series tries to do. Using the --root option is probably > a more natural way to do what people often do with the "add graft and > filter-branch the whole history once" procedure. Their uses are somewhat disjoint, as grafting only works if the grafted parent has an empty tree. Otherwise the grafted child will appear to revert the entire history leading to it. Rebase OTOH changes the committers. > But it somewhat feels sad if the "main" use-case for this is to start your > project in git and then migrate away by feeding your history to subversion > ;-). You can remove that paragraph if you don't want to "support" SVN in git.git ;-) > > # Make sure the branch to rebase onto is valid. > > onto_name=${newbase-"$upstream_name"} > > onto=$(git rev-parse --verify "${onto_name}^0") || exit > > > > # If a hook exists, give it a chance to interrupt > > -run_pre_rebase_hook ${1+"$@"} > > +run_pre_rebase_hook ${upstream_name+"$upstream_name"} "$@" > > I do not think ${upstream_name+"$upstream_name"} is a good check to begin > with, because presense of it does not necessarily mean the command was > invoked without --root; it could have come from the environment of the > invoker (hence the suggestion to unset the variable explicitly). Good catch, thanks. I'm still not sure what ${1+"$@"} was about by the way. The most authoritative reference I can find is http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02 which says If there are no positional parameters, the expansion of '@' shall generate zero fields, even when '@' is double-quoted. ('man bash' agrees.) > And I do not think this is a good way to extend the calling convention of > the hook, either. pre-rebase-hook used to always take upstream and > optionally the explicit branch name. When --root is given, your code will > give the hook a single parameter which is the explicit branch name > (i.e. "we will switch to this branch and rebase it, are you Ok with it?"), > but the hook will mistakenly think you are feeding the fork-point commit. > > Because an old pre-rebase-hook cannot verify --root case correctly anyway > and needs to be updated, how about doing 'run_pre_rebase_hook --root "$@"' > when --root was given? True. I noticed that I had to fix the positionals, but forgot about the hook afterwards. v3 implements this as you suggested. > > +if test ! -z "$rebase_root"; then > > + revisions="$orig_head" > > + fp_flag="--root" > > +else > > + revisions="$upstream..$orig_head" > > + fp_flag="--ignore-if-in-upstream" > > +fi > > Hmph, the reason why --ignore-if-in-upstream is irrelevant when --root is > given is because...? Well, originally because format-patch didn't like the argument. Thanks for prodding however, $onto..$head makes sort of makes sense here and I discovered that even $onto...$head works, which is used in 'rebase -i'. However, it's still a change of semantics: With --root we now ignore patches that are already contained in $onto, as opposed to patches that were already in $upstream in normal operation. It seems sensible to do it this way, and perhaps even normal rebase should do it the same way... if only format-patch would support it. Thanks for the review! v3 upcoming. -- Thomas Rast trast@{inf,student}.ethz.ch [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v3 1/4] rebase -i: execute hook only after argument checking 2009-01-02 22:20 ` Thomas Rast @ 2009-01-02 22:28 ` Thomas Rast 2009-01-02 22:28 ` [PATCH v3 2/4] rebase: learn to rebase root commit Thomas Rast 2009-01-02 22:49 ` [PATCH v2 1/3] " Junio C Hamano 1 sibling, 1 reply; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, bss Previously, the pre-rebase-hook would be launched before we knew if the <upstream> [<branch>] arguments were supplied. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- This is a quick fix and not really part of the series. 2/4 textually depends on it, however, so I'm sending it along. git-rebase--interactive.sh | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c8b0861..2c668cd 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -554,7 +554,6 @@ first and then run 'git rebase --continue' again." ;; --) shift - run_pre_rebase_hook ${1+"$@"} test $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@ -562,11 +561,13 @@ first and then run 'git rebase --continue' again." git var GIT_COMMITTER_IDENT >/dev/null || die "You need to set your committer info first" + UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + run_pre_rebase_hook ${1+"$@"} + comment_for_reflog start require_clean_work_tree - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" test -z "$ONTO" && ONTO=$UPSTREAM if test ! -z "$2" -- 1.6.1.71.gaaa47.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v3 2/4] rebase: learn to rebase root commit 2009-01-02 22:28 ` [PATCH v3 1/4] rebase -i: execute hook only after argument checking Thomas Rast @ 2009-01-02 22:28 ` Thomas Rast 2009-01-02 22:28 ` [PATCH v3 3/4] rebase -i: " Thomas Rast ` (2 more replies) 0 siblings, 3 replies; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, bss Teach git-rebase a new option --root, which instructs it to rebase the entire history leading up to <branch>. This option must be used with --onto <newbase>, and causes commits that already exist in <newbase> to be skipped. (Normal operation skips commits that already exist in <upstream> instead.) The main use-case is with git-svn: suppose you start hacking (perhaps offline) on a new project, but later notice you want to commit this work to SVN. You will have to rebase the entire history, including the root commit, on a (possibly empty) commit coming from git-svn, to establish a history connection. This previously had to be done by cherry-picking the root commit manually. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- git-rebase.sh | 50 +++++++++++++++++++-------- t/t3412-rebase-root.sh | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 15 deletions(-) create mode 100755 t/t3412-rebase-root.sh diff --git a/git-rebase.sh b/git-rebase.sh index ebd4df3..9437e51 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]' +USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -47,6 +47,7 @@ dotest="$GIT_DIR"/rebase-merge prec=4 verbose= git_am_opt= +rebase_root= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -297,6 +298,9 @@ do -C*) git_am_opt="$git_am_opt $1" ;; + --root) + rebase_root=t + ;; -*) usage ;; @@ -344,17 +348,26 @@ case "$diff" in ;; esac +if test -z "$rebase_root"; then # The upstream head must be given. Make sure it is valid. -upstream_name="$1" -upstream=`git rev-parse --verify "${upstream_name}^0"` || - die "invalid upstream $upstream_name" + upstream_name="$1" + shift + upstream=`git rev-parse --verify "${upstream_name}^0"` || + die "invalid upstream $upstream_name" + unset root_flag +else + test -z "$newbase" && die "--root must be used with --onto" + unset upstream_name + unset upstream + root_flag="--root" +fi # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -run_pre_rebase_hook ${1+"$@"} +run_pre_rebase_hook $root_flag $upstream_name "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -362,16 +375,16 @@ run_pre_rebase_hook ${1+"$@"} # $head_name -- refs/heads/<that-branch> or "detached HEAD" switch_to= case "$#" in -2) +1) # Is it "rebase other $branchname" or "rebase other $commit"? - branch_name="$2" - switch_to="$2" + branch_name="$1" + switch_to="$1" - if git show-ref --verify --quiet -- "refs/heads/$2" && - branch=$(git rev-parse -q --verify "refs/heads/$2") + if git show-ref --verify --quiet -- "refs/heads/$1" && + branch=$(git rev-parse -q --verify "refs/heads/$1") then - head_name="refs/heads/$2" - elif branch=$(git rev-parse -q --verify "$2") + head_name="refs/heads/$1" + elif branch=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -393,7 +406,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch on top of $onto +# Now we are rebasing commits $upstream..$branch (or with --root, +# everything leading up to $branch) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -429,10 +443,16 @@ then exit 0 fi +if test ! -z "$rebase_root"; then + revisions="$onto..$orig_head" +else + revisions="$upstream..$orig_head" +fi + if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - "$upstream..$orig_head" | + $root_flag "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -455,7 +475,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` +for cmt in `git rev-list --reverse --no-merges "$revisions"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh new file mode 100755 index 0000000..1978512 --- /dev/null +++ b/t/t3412-rebase-root.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +test_description='git rebase --root + +Tests if git rebase --root --onto <newparent> can rebase the root commit. +' +. ./test-lib.sh + +test_expect_success 'prepare repository' ' + echo 1 > A && + git add A && + git commit -m 1 && + echo 2 > A && + git add A && + git commit -m 2 && + git symbolic-ref HEAD refs/heads/other && + rm .git/index && + echo 1 > A && + git add A && + git commit -m 1b && + echo 3 > B && + git add B && + git commit -m 3 && + echo 4 > B && + git add B && + git commit -m 4 +' + +test_expect_success 'rebase --root expects --onto' ' + test_must_fail git rebase --root +' + +test_expect_success 'setup pre-rebase hook' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +echo "\$1,\$2" >.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' +cat > expect <<EOF +4 +3 +2 +1 +EOF + +test_expect_success 'rebase --root --onto <newbase>' ' + git checkout -b work && + git rebase --root --onto master && + git log --pretty=tformat:"%s" > rebased && + test_cmp expect rebased +' + +test_expect_success 'pre-rebase got correct input (1)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + +test_expect_success 'rebase --root --onto <newbase> <branch>' ' + git branch work2 other && + git rebase --root --onto master work2 && + git log --pretty=tformat:"%s" > rebased2 && + test_cmp expect rebased2 +' + +test_expect_success 'pre-rebase got correct input (2)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2 +' + +test_expect_success 'setup pre-rebase hook that fails' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +false +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook stops rebase' ' + git checkout -b stops1 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 + test 0 = $(git rev-list other...stops1 | wc -l) +' + +test_done -- 1.6.1.71.gaaa47.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v3 3/4] rebase -i: learn to rebase root commit 2009-01-02 22:28 ` [PATCH v3 2/4] rebase: learn to rebase root commit Thomas Rast @ 2009-01-02 22:28 ` Thomas Rast 2009-01-02 22:28 ` [PATCH v3 4/4] rebase: update documentation for --root Thomas Rast 2009-01-02 22:41 ` [INTERDIFF v3 3/4] rebase -i: learn to rebase root commit Thomas Rast 2009-01-02 22:41 ` [INTERDIFF v3 2/4] rebase: " Thomas Rast 2009-01-02 23:06 ` [PATCH " Junio C Hamano 2 siblings, 2 replies; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, bss Teach git-rebase -i a new option --root, which instructs it to rebase the entire history leading up to <branch>. This is mainly for symmetry with ordinary git-rebase; it cannot be used to edit the root commit in-place (it requires --onto <newbase>). Commits that already exist in <newbase> are skipped. In the normal mode of operation, this is fairly straightforward. We run cherry-pick in a loop, and cherry-pick has supported picking the root commit since f95ebf7 (Allow cherry-picking root commits, 2008-07-04). In --preserve-merges mode, we track the mapping from old to rewritten commits and use it to update the parent list of each commit. In this case, we define 'rebase -i -p --root --onto $onto $branch' to rewrite the parent list of all root commit(s) on $branch to contain $onto instead. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- git-rebase--interactive.sh | 109 +++++++++++++++++++++++++++++++------------ t/t3412-rebase-root.sh | 101 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 31 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 2c668cd..14d3e38 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -27,6 +27,7 @@ continue continue rebasing process abort abort rebasing process and restore original branch skip skip current patch and continue rebasing process no-verify override pre-rebase hook from stopping the operation +root rebase all reachable commmits up to the root(s) " . git-sh-setup @@ -44,6 +45,7 @@ STRATEGY= ONTO= VERBOSE= OK_TO_SKIP_PRE_REBASE= +REBASE_ROOT= GIT_CHERRY_PICK_HELP=" After resolving the conflicts, mark the corrected paths with 'git add <paths>', and @@ -154,6 +156,11 @@ pick_one () { output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" test -d "$REWRITTEN" && pick_one_preserving_merges "$@" && return + if test ! -z "$REBASE_ROOT" + then + output git cherry-pick "$@" + return + fi parent_sha1=$(git rev-parse --verify $sha1^) || die "Could not get the parent of $sha1" current_sha1=$(git rev-parse --verify HEAD) @@ -197,7 +204,11 @@ pick_one_preserving_merges () { # rewrite parents; if none were rewritten, we can fast-forward. new_parents= - pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)" + pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)" + if test "$pend" = " " + then + pend=" root" + fi while [ "$pend" != "" ] do p=$(expr "$pend" : ' \([^ ]*\)') @@ -227,7 +238,9 @@ pick_one_preserving_merges () { if test -f "$DROPPED"/$p then fast_forward=f - pend=" $(cat "$DROPPED"/$p)$pend" + replacement="$(cat "$DROPPED"/$p)" + test -z "$replacement" && replacement=root + pend=" $replacement$pend" else new_parents="$new_parents $p" fi @@ -443,6 +456,7 @@ get_saved_options () { test -d "$REWRITTEN" && PRESERVE_MERGES=t test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" test -f "$DOTEST"/verbose && VERBOSE=t + test ! -s "$DOTEST"/upstream && REBASE_ROOT=t } while test $# != 0 @@ -547,6 +561,9 @@ first and then run 'git rebase --continue' again." -i) # yeah, we know ;; + --root) + REBASE_ROOT=t + ;; --onto) shift ONTO=$(git rev-parse --verify "$1") || @@ -554,28 +571,36 @@ first and then run 'git rebase --continue' again." ;; --) shift - test $# -eq 1 -o $# -eq 2 || usage + test ! -z "$REBASE_ROOT" -o $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" git var GIT_COMMITTER_IDENT >/dev/null || die "You need to set your committer info first" - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" - run_pre_rebase_hook ${1+"$@"} + if test -z "$REBASE_ROOT" + then + UPSTREAM_ARG="$1" + UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" + test -z "$ONTO" && ONTO=$UPSTREAM + shift + else + UPSTREAM_ARG=--root + test -z "$ONTO" && + die "You must specify --onto when using --root" + fi + run_pre_rebase_hook "$UPSTREAM_ARG" "$@" comment_for_reflog start require_clean_work_tree - test -z "$ONTO" && ONTO=$UPSTREAM - - if test ! -z "$2" + if test ! -z "$1" then - output git show-ref --verify --quiet "refs/heads/$2" || - die "Invalid branchname: $2" - output git checkout "$2" || - die "Could not checkout $2" + output git show-ref --verify --quiet "refs/heads/$1" || + die "Invalid branchname: $1" + output git checkout "$1" || + die "Could not checkout $1" fi HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" @@ -599,12 +624,19 @@ first and then run 'git rebase --continue' again." # This ensures that commits on merged, but otherwise # unrelated side branches are left alone. (Think "X" # in the man page's example.) - mkdir "$REWRITTEN" && - for c in $(git merge-base --all $HEAD $UPSTREAM) - do - echo $ONTO > "$REWRITTEN"/$c || + if test -z "$REBASE_ROOT" + then + mkdir "$REWRITTEN" && + for c in $(git merge-base --all $HEAD $UPSTREAM) + do + echo $ONTO > "$REWRITTEN"/$c || + die "Could not init rewritten commits" + done + else + mkdir "$REWRITTEN" && + echo $ONTO > "$REWRITTEN"/root || die "Could not init rewritten commits" - done + fi # No cherry-pick because our first pass is to determine # parents to rewrite and skipping dropped commits would # prematurely end our probe @@ -614,12 +646,21 @@ first and then run 'git rebase --continue' again." MERGES_OPTION="--no-merges --cherry-pick" fi - SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) SHORTHEAD=$(git rev-parse --short $HEAD) SHORTONTO=$(git rev-parse --short $ONTO) + if test -z "$REBASE_ROOT" + # this is now equivalent to ! -z "$UPSTREAM" + then + SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) + REVISIONS=$UPSTREAM...$HEAD + SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD + else + REVISIONS=$ONTO...$HEAD + SHORTREVISIONS=$SHORTHEAD + fi git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse --left-right --topo-order \ - $UPSTREAM...$HEAD | \ + $REVISIONS | \ sed -n "s/^>//p" | while read shortsha1 rest do if test t != "$PRESERVE_MERGES" @@ -627,14 +668,19 @@ first and then run 'git rebase --continue' again." echo "pick $shortsha1 $rest" >> "$TODO" else sha1=$(git rev-parse $shortsha1) - preserve=t - for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-) - do - if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \) - then - preserve=f - fi - done + if test -z "$REBASE_ROOT" + then + preserve=t + for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) + do + if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \) + then + preserve=f + fi + done + else + preserve=f + fi if test f = "$preserve" then touch "$REWRITTEN"/$sha1 @@ -648,11 +694,11 @@ first and then run 'git rebase --continue' again." then mkdir "$DROPPED" # Save all non-cherry-picked changes - git rev-list $UPSTREAM...$HEAD --left-right --cherry-pick | \ + git rev-list $REVISIONS --left-right --cherry-pick | \ sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks # Now all commits and note which ones are missing in # not-cherry-picks and hence being dropped - git rev-list $UPSTREAM..$HEAD | + git rev-list $REVISIONS | while read rev do if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = "" @@ -661,17 +707,18 @@ first and then run 'git rebase --continue' again." # not worthwhile, we don't want to track its multiple heads, # just the history of its first-parent for others that will # be rebasing on top of it - git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$rev + git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO" rm "$REWRITTEN"/$rev fi done fi + test -s "$TODO" || echo noop >> "$TODO" cat >> "$TODO" << EOF -# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO +# Rebase $SHORTREVISIONS onto $SHORTONTO # # Commands: # p, pick = use commit diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 1978512..cbf3414 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -67,6 +67,100 @@ test_expect_success 'pre-rebase got correct input (2)' ' test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2 ' +test_expect_success 'rebase -i --root --onto <newbase>' ' + git checkout -b work3 other && + GIT_EDITOR=: git rebase -i --root --onto master && + git log --pretty=tformat:"%s" > rebased3 && + test_cmp expect rebased3 +' + +test_expect_success 'pre-rebase got correct input (3)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + +test_expect_success 'rebase -i --root --onto <newbase> <branch>' ' + git branch work4 other && + GIT_EDITOR=: git rebase -i --root --onto master work4 && + git log --pretty=tformat:"%s" > rebased4 && + test_cmp expect rebased4 +' + +test_expect_success 'pre-rebase got correct input (4)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work4 +' + +test_expect_success 'rebase -i -p with linear history' ' + git checkout -b work5 other && + GIT_EDITOR=: git rebase -i -p --root --onto master && + git log --pretty=tformat:"%s" > rebased5 && + test_cmp expect rebased5 +' + +test_expect_success 'pre-rebase got correct input (5)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + +test_expect_success 'set up merge history' ' + git checkout other^ && + git checkout -b side && + echo 5 > C && + git add C && + git commit -m 5 && + git checkout other && + git merge side +' + +sed 's/#/ /g' > expect-side <<'EOF' +* Merge branch 'side' into other +|\## +| * 5 +* | 4 +|/## +* 3 +* 2 +* 1 +EOF + +test_expect_success 'rebase -i -p with merge' ' + git checkout -b work6 other && + GIT_EDITOR=: git rebase -i -p --root --onto master && + git log --graph --topo-order --pretty=tformat:"%s" > rebased6 && + test_cmp expect-side rebased6 +' + +test_expect_success 'set up second root and merge' ' + git symbolic-ref HEAD refs/heads/third && + rm .git/index && + rm A B C && + echo 6 > D && + git add D && + git commit -m 6 && + git checkout other && + git merge third +' + +sed 's/#/ /g' > expect-third <<'EOF' +* Merge branch 'third' into other +|\## +| * 6 +* | Merge branch 'side' into other +|\ \## +| * | 5 +* | | 4 +|/ /## +* | 3 +|/## +* 2 +* 1 +EOF + +test_expect_success 'rebase -i -p with two roots' ' + git checkout -b work7 other && + GIT_EDITOR=: git rebase -i -p --root --onto master && + git log --graph --topo-order --pretty=tformat:"%s" > rebased7 && + test_cmp expect-third rebased7 +' + test_expect_success 'setup pre-rebase hook that fails' ' mkdir -p .git/hooks && cat >.git/hooks/pre-rebase <<EOF && @@ -83,4 +177,11 @@ test_expect_success 'pre-rebase hook stops rebase' ' test 0 = $(git rev-list other...stops1 | wc -l) ' +test_expect_success 'pre-rebase hook stops rebase -i' ' + git checkout -b stops2 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 + test 0 = $(git rev-list other...stops2 | wc -l) +' + test_done -- 1.6.1.71.gaaa47.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v3 4/4] rebase: update documentation for --root 2009-01-02 22:28 ` [PATCH v3 3/4] rebase -i: " Thomas Rast @ 2009-01-02 22:28 ` Thomas Rast 2009-01-02 22:41 ` [INTERDIFF v3 3/4] rebase -i: learn to rebase root commit Thomas Rast 1 sibling, 0 replies; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, bss Since the new option depends on --onto and omission of <upstream>, use a separate invocation style, and omit most options to save space. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- Documentation/git-rebase.txt | 21 ++++++++++++++++----- 1 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index c8ad86a..3d6d429 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -8,10 +8,11 @@ git-rebase - Forward-port local commits to the updated upstream head SYNOPSIS -------- [verse] -'git rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] - [-s <strategy> | --strategy=<strategy>] [--no-verify] - [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges] - [--onto <newbase>] <upstream> [<branch>] +'git rebase' [-i | --interactive] [options] [--onto <newbase>] + <upstream> [<branch>] +'git rebase' [-i | --interactive] [options] --onto <newbase> + --root [<branch>] + 'git rebase' --continue | --skip | --abort DESCRIPTION @@ -22,7 +23,8 @@ it remains on the current branch. All changes made by commits in the current branch but that are not in <upstream> are saved to a temporary area. This is the same set -of commits that would be shown by `git log <upstream>..HEAD`. +of commits that would be shown by `git log <upstream>..HEAD` (or +`git log HEAD`, if --root is specified). The current branch is reset to <upstream>, or <newbase> if the --onto option was supplied. This has the exact same effect as @@ -255,6 +257,15 @@ OPTIONS --preserve-merges:: Instead of ignoring merges, try to recreate them. +--root:: + Rebase all commits reachable from <branch>, instead of + limiting them with an <upstream>. This allows you to rebase + the root commit(s) on a branch. Must be used with --onto, and + will skip changes already contained in <newbase> (instead of + <upstream>). When used together with --preserve-merges, 'all' + root commits will be rewritten to have <newbase> as parent + instead. + include::merge-strategies.txt[] NOTES -- 1.6.1.71.gaaa47.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [INTERDIFF v3 3/4] rebase -i: learn to rebase root commit 2009-01-02 22:28 ` [PATCH v3 3/4] rebase -i: " Thomas Rast 2009-01-02 22:28 ` [PATCH v3 4/4] rebase: update documentation for --root Thomas Rast @ 2009-01-02 22:41 ` Thomas Rast 1 sibling, 0 replies; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:41 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, bss This is the interdiff to v2. Some of the changes are noise from 1/4. diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index d6f54eb..14d3e38 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -238,7 +238,9 @@ pick_one_preserving_merges () { if test -f "$DROPPED"/$p then fast_forward=f - pend=" $(cat "$DROPPED"/$p)$pend" + replacement="$(cat "$DROPPED"/$p)" + test -z "$replacement" && replacement=root + pend=" $replacement$pend" else new_parents="$new_parents $p" fi @@ -569,7 +571,6 @@ first and then run 'git rebase --continue' again." ;; --) shift - run_pre_rebase_hook ${1+"$@"} test ! -z "$REBASE_ROOT" -o $# -eq 1 -o $# -eq 2 || usage test -d "$DOTEST" && die "Interactive rebase already started" @@ -577,19 +578,22 @@ first and then run 'git rebase --continue' again." git var GIT_COMMITTER_IDENT >/dev/null || die "You need to set your committer info first" - comment_for_reflog start - - require_clean_work_tree - if test -z "$REBASE_ROOT" then + UPSTREAM_ARG="$1" UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" test -z "$ONTO" && ONTO=$UPSTREAM shift else + UPSTREAM_ARG=--root test -z "$ONTO" && die "You must specify --onto when using --root" fi + run_pre_rebase_hook "$UPSTREAM_ARG" "$@" + + comment_for_reflog start + + require_clean_work_tree if test ! -z "$1" then @@ -651,7 +655,7 @@ first and then run 'git rebase --continue' again." REVISIONS=$UPSTREAM...$HEAD SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD else - REVISIONS=$HEAD + REVISIONS=$ONTO...$HEAD SHORTREVISIONS=$SHORTHEAD fi git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ @@ -710,6 +714,7 @@ first and then run 'git rebase --continue' again." fi done fi + test -s "$TODO" || echo noop >> "$TODO" cat >> "$TODO" << EOF diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index c845dfc..cbf3414 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -15,7 +15,9 @@ test_expect_success 'prepare repository' ' git commit -m 2 && git symbolic-ref HEAD refs/heads/other && rm .git/index && - rm A && + echo 1 > A && + git add A && + git commit -m 1b && echo 3 > B && git add B && git commit -m 3 && @@ -28,6 +30,14 @@ test_expect_success 'rebase --root expects --onto' ' test_must_fail git rebase --root ' +test_expect_success 'setup pre-rebase hook' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +echo "\$1,\$2" >.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' cat > expect <<EOF 4 3 @@ -42,6 +52,10 @@ test_expect_success 'rebase --root --onto <newbase>' ' test_cmp expect rebased ' +test_expect_success 'pre-rebase got correct input (1)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + test_expect_success 'rebase --root --onto <newbase> <branch>' ' git branch work2 other && git rebase --root --onto master work2 && @@ -49,6 +63,10 @@ test_expect_success 'rebase --root --onto <newbase> <branch>' ' test_cmp expect rebased2 ' +test_expect_success 'pre-rebase got correct input (2)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2 +' + test_expect_success 'rebase -i --root --onto <newbase>' ' git checkout -b work3 other && GIT_EDITOR=: git rebase -i --root --onto master && @@ -56,6 +74,10 @@ test_expect_success 'rebase -i --root --onto <newbase>' ' test_cmp expect rebased3 ' +test_expect_success 'pre-rebase got correct input (3)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + test_expect_success 'rebase -i --root --onto <newbase> <branch>' ' git branch work4 other && GIT_EDITOR=: git rebase -i --root --onto master work4 && @@ -63,6 +85,10 @@ test_expect_success 'rebase -i --root --onto <newbase> <branch>' ' test_cmp expect rebased4 ' +test_expect_success 'pre-rebase got correct input (4)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work4 +' + test_expect_success 'rebase -i -p with linear history' ' git checkout -b work5 other && GIT_EDITOR=: git rebase -i -p --root --onto master && @@ -70,6 +96,10 @@ test_expect_success 'rebase -i -p with linear history' ' test_cmp expect rebased5 ' +test_expect_success 'pre-rebase got correct input (5)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + test_expect_success 'set up merge history' ' git checkout other^ && git checkout -b side && @@ -131,4 +161,27 @@ test_expect_success 'rebase -i -p with two roots' ' test_cmp expect-third rebased7 ' +test_expect_success 'setup pre-rebase hook that fails' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +false +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook stops rebase' ' + git checkout -b stops1 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 + test 0 = $(git rev-list other...stops1 | wc -l) +' + +test_expect_success 'pre-rebase hook stops rebase -i' ' + git checkout -b stops2 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2 + test 0 = $(git rev-list other...stops2 | wc -l) +' + test_done -- Thomas Rast trast@{inf,student}.ethz.ch ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [INTERDIFF v3 2/4] rebase: learn to rebase root commit 2009-01-02 22:28 ` [PATCH v3 2/4] rebase: learn to rebase root commit Thomas Rast 2009-01-02 22:28 ` [PATCH v3 3/4] rebase -i: " Thomas Rast @ 2009-01-02 22:41 ` Thomas Rast 2009-01-02 23:06 ` [PATCH " Junio C Hamano 2 siblings, 0 replies; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:41 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, bss The interdiff to v2. diff --git a/git-rebase.sh b/git-rebase.sh index 89de3c4..9437e51 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -354,17 +354,20 @@ if test -z "$rebase_root"; then shift upstream=`git rev-parse --verify "${upstream_name}^0"` || die "invalid upstream $upstream_name" + unset root_flag +else + test -z "$newbase" && die "--root must be used with --onto" + unset upstream_name + unset upstream + root_flag="--root" fi -test ! -z "$rebase_root" -a -z "$newbase" && - die "--root must be used with --onto" - # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -run_pre_rebase_hook ${upstream_name+"$upstream_name"} "$@" +run_pre_rebase_hook $root_flag $upstream_name "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -403,8 +406,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch (or simply $branch -# with --root) on top of $onto +# Now we are rebasing commits $upstream..$branch (or with --root, +# everything leading up to $branch) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -441,17 +444,15 @@ then fi if test ! -z "$rebase_root"; then - revisions="$orig_head" - fp_flag="--root" + revisions="$onto..$orig_head" else revisions="$upstream..$orig_head" - fp_flag="--ignore-if-in-upstream" fi if test -z "$do_merge" then - git format-patch -k --stdout --full-index "$fp_flag" \ - "$revisions" | + git format-patch -k --stdout --full-index --ignore-if-in-upstream \ + $root_flag "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 63ec5e6..1978512 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -15,7 +15,9 @@ test_expect_success 'prepare repository' ' git commit -m 2 && git symbolic-ref HEAD refs/heads/other && rm .git/index && - rm A && + echo 1 > A && + git add A && + git commit -m 1b && echo 3 > B && git add B && git commit -m 3 && @@ -28,6 +30,14 @@ test_expect_success 'rebase --root expects --onto' ' test_must_fail git rebase --root ' +test_expect_success 'setup pre-rebase hook' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +echo "\$1,\$2" >.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' cat > expect <<EOF 4 3 @@ -42,6 +52,10 @@ test_expect_success 'rebase --root --onto <newbase>' ' test_cmp expect rebased ' +test_expect_success 'pre-rebase got correct input (1)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + test_expect_success 'rebase --root --onto <newbase> <branch>' ' git branch work2 other && git rebase --root --onto master work2 && @@ -49,4 +63,24 @@ test_expect_success 'rebase --root --onto <newbase> <branch>' ' test_cmp expect rebased2 ' +test_expect_success 'pre-rebase got correct input (2)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2 +' + +test_expect_success 'setup pre-rebase hook that fails' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +false +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook stops rebase' ' + git checkout -b stops1 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 + test 0 = $(git rev-list other...stops1 | wc -l) +' + test_done -- Thomas Rast trast@{inf,student}.ethz.ch ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v3 2/4] rebase: learn to rebase root commit 2009-01-02 22:28 ` [PATCH v3 2/4] rebase: learn to rebase root commit Thomas Rast 2009-01-02 22:28 ` [PATCH v3 3/4] rebase -i: " Thomas Rast 2009-01-02 22:41 ` [INTERDIFF v3 2/4] rebase: " Thomas Rast @ 2009-01-02 23:06 ` Junio C Hamano 2009-01-02 23:45 ` [PATCH v3.1] " Thomas Rast 2 siblings, 1 reply; 27+ messages in thread From: Junio C Hamano @ 2009-01-02 23:06 UTC (permalink / raw) To: Thomas Rast; +Cc: git, bss > # If a hook exists, give it a chance to interrupt > -run_pre_rebase_hook ${1+"$@"} > +run_pre_rebase_hook $root_flag $upstream_name "$@" You'd have to quote the $upstream_name properly here, because the original command line could well have been: $ git rebase 'master@{3 days ago}' ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v3.1] rebase: learn to rebase root commit 2009-01-02 23:06 ` [PATCH " Junio C Hamano @ 2009-01-02 23:45 ` Thomas Rast 2009-01-05 17:35 ` [PATCH v3.2] " Thomas Rast 0 siblings, 1 reply; 27+ messages in thread From: Thomas Rast @ 2009-01-02 23:45 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Teach git-rebase a new option --root, which instructs it to rebase the entire history leading up to <branch>. This option must be used with --onto <newbase>, and causes commits that already exist in <newbase> to be skipped. (Normal operation skips commits that already exist in <upstream> instead.) The main use-case is with git-svn: suppose you start hacking (perhaps offline) on a new project, but later notice you want to commit this work to SVN. You will have to rebase the entire history, including the root commit, on a (possibly empty) commit coming from git-svn, to establish a history connection. This previously had to be done by cherry-picking the root commit manually. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- Junio C Hamano wrote: > > > # If a hook exists, give it a chance to interrupt > > -run_pre_rebase_hook ${1+"$@"} > > +run_pre_rebase_hook $root_flag $upstream_name "$@" > > You'd have to quote the $upstream_name properly here, because the original > command line could well have been: > > $ git rebase 'master@{3 days ago}' Right, thanks. Let's fix it like this, so I won't have to look up what the various special parameter expansion characters mean. ($root_flag is still used further below.) >> --- a/git-rebase.sh >> +++ b/git-rebase.sh >> @@ -355,11 +355,13 @@ if test -z "$rebase_root"; then >> upstream=`git rev-parse --verify "${upstream_name}^0"` || >> die "invalid upstream $upstream_name" >> unset root_flag >> + upstream_arg="$upstream_name" >> else >> test -z "$newbase" && die "--root must be used with --onto" >> unset upstream_name >> unset upstream >> root_flag="--root" >> + upstream_arg="$root_flag" >> fi >> >> # Make sure the branch to rebase onto is valid. >> @@ -367,7 +369,7 @@ onto_name=${newbase-"$upstream_name"} >> onto=$(git rev-parse --verify "${onto_name}^0") || exit >> >> # If a hook exists, give it a chance to interrupt >> -run_pre_rebase_hook $root_flag $upstream_name "$@" >> +run_pre_rebase_hook "$upstream_arg" "$@" >> >> # If the branch to rebase is given, that is the branch we will rebase >> # $branch_name -- branch being rebased, or HEAD (already detached) git-rebase.sh | 52 ++++++++++++++++++++-------- t/t3412-rebase-root.sh | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 15 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index ebd4df3..d1083f1 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]' +USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -47,6 +47,7 @@ dotest="$GIT_DIR"/rebase-merge prec=4 verbose= git_am_opt= +rebase_root= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -297,6 +298,9 @@ do -C*) git_am_opt="$git_am_opt $1" ;; + --root) + rebase_root=t + ;; -*) usage ;; @@ -344,17 +348,28 @@ case "$diff" in ;; esac +if test -z "$rebase_root"; then # The upstream head must be given. Make sure it is valid. -upstream_name="$1" -upstream=`git rev-parse --verify "${upstream_name}^0"` || - die "invalid upstream $upstream_name" + upstream_name="$1" + shift + upstream=`git rev-parse --verify "${upstream_name}^0"` || + die "invalid upstream $upstream_name" + unset root_flag + upstream_arg="$upstream_name" +else + test -z "$newbase" && die "--root must be used with --onto" + unset upstream_name + unset upstream + root_flag="--root" + upstream_arg="$root_flag" +fi # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -run_pre_rebase_hook ${1+"$@"} +run_pre_rebase_hook "$upstream_arg" "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -362,16 +377,16 @@ run_pre_rebase_hook ${1+"$@"} # $head_name -- refs/heads/<that-branch> or "detached HEAD" switch_to= case "$#" in -2) +1) # Is it "rebase other $branchname" or "rebase other $commit"? - branch_name="$2" - switch_to="$2" + branch_name="$1" + switch_to="$1" - if git show-ref --verify --quiet -- "refs/heads/$2" && - branch=$(git rev-parse -q --verify "refs/heads/$2") + if git show-ref --verify --quiet -- "refs/heads/$1" && + branch=$(git rev-parse -q --verify "refs/heads/$1") then - head_name="refs/heads/$2" - elif branch=$(git rev-parse -q --verify "$2") + head_name="refs/heads/$1" + elif branch=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -393,7 +408,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch on top of $onto +# Now we are rebasing commits $upstream..$branch (or with --root, +# everything leading up to $branch) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -429,10 +445,16 @@ then exit 0 fi +if test ! -z "$rebase_root"; then + revisions="$onto..$orig_head" +else + revisions="$upstream..$orig_head" +fi + if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - "$upstream..$orig_head" | + $root_flag "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -455,7 +477,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` +for cmt in `git rev-list --reverse --no-merges "$revisions"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh new file mode 100755 index 0000000..1978512 --- /dev/null +++ b/t/t3412-rebase-root.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +test_description='git rebase --root + +Tests if git rebase --root --onto <newparent> can rebase the root commit. +' +. ./test-lib.sh + +test_expect_success 'prepare repository' ' + echo 1 > A && + git add A && + git commit -m 1 && + echo 2 > A && + git add A && + git commit -m 2 && + git symbolic-ref HEAD refs/heads/other && + rm .git/index && + echo 1 > A && + git add A && + git commit -m 1b && + echo 3 > B && + git add B && + git commit -m 3 && + echo 4 > B && + git add B && + git commit -m 4 +' + +test_expect_success 'rebase --root expects --onto' ' + test_must_fail git rebase --root +' + +test_expect_success 'setup pre-rebase hook' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +echo "\$1,\$2" >.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' +cat > expect <<EOF +4 +3 +2 +1 +EOF + +test_expect_success 'rebase --root --onto <newbase>' ' + git checkout -b work && + git rebase --root --onto master && + git log --pretty=tformat:"%s" > rebased && + test_cmp expect rebased +' + +test_expect_success 'pre-rebase got correct input (1)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + +test_expect_success 'rebase --root --onto <newbase> <branch>' ' + git branch work2 other && + git rebase --root --onto master work2 && + git log --pretty=tformat:"%s" > rebased2 && + test_cmp expect rebased2 +' + +test_expect_success 'pre-rebase got correct input (2)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2 +' + +test_expect_success 'setup pre-rebase hook that fails' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +false +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook stops rebase' ' + git checkout -b stops1 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 + test 0 = $(git rev-list other...stops1 | wc -l) +' + +test_done -- tg: (cb706e1..) t/rr-normal (depends on: origin/master t/rebase-hook-order) ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v3.2] rebase: learn to rebase root commit 2009-01-02 23:45 ` [PATCH v3.1] " Thomas Rast @ 2009-01-05 17:35 ` Thomas Rast 2009-01-06 8:19 ` Junio C Hamano 0 siblings, 1 reply; 27+ messages in thread From: Thomas Rast @ 2009-01-05 17:35 UTC (permalink / raw) To: Junio C Hamano; +Cc: git Teach git-rebase a new option --root, which instructs it to rebase the entire history leading up to <branch>. This option must be used with --onto <newbase>, and causes commits that already exist in <newbase> to be skipped. (Normal operation skips commits that already exist in <upstream> instead.) The main use-case is with git-svn: suppose you start hacking (perhaps offline) on a new project, but later notice you want to commit this work to SVN. You will have to rebase the entire history, including the root commit, on a (possibly empty) commit coming from git-svn, to establish a history connection. This previously had to be done by cherry-picking the root commit manually. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- Since this is still in 'pu', can you replace it again? The test case I designed for v3, to check that it also skips identical commits, actually makes the _root_ commit a duplicate. I could sleep better at night if the root commit was actually part of the rebase :-) 3/4 is not affected patch-wise, though it of course inherits the same test history. Thanks! Interdiff: >> diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh >> index 1978512..dd91910 100755 >> --- a/t/t3412-rebase-root.sh >> +++ b/t/t3412-rebase-root.sh >> @@ -15,12 +15,12 @@ test_expect_success 'prepare repository' ' >> git commit -m 2 && >> git symbolic-ref HEAD refs/heads/other && >> rm .git/index && >> - echo 1 > A && >> - git add A && >> - git commit -m 1b && >> echo 3 > B && >> git add B && >> git commit -m 3 && >> + echo 1 > A && >> + git add A && >> + git commit -m 1b && >> echo 4 > B && >> git add B && >> git commit -m 4 git-rebase.sh | 52 ++++++++++++++++++++-------- t/t3412-rebase-root.sh | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 15 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index ebd4df3..d1083f1 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] <upstream> [<branch>]' +USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -47,6 +47,7 @@ dotest="$GIT_DIR"/rebase-merge prec=4 verbose= git_am_opt= +rebase_root= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -297,6 +298,9 @@ do -C*) git_am_opt="$git_am_opt $1" ;; + --root) + rebase_root=t + ;; -*) usage ;; @@ -344,17 +348,28 @@ case "$diff" in ;; esac +if test -z "$rebase_root"; then # The upstream head must be given. Make sure it is valid. -upstream_name="$1" -upstream=`git rev-parse --verify "${upstream_name}^0"` || - die "invalid upstream $upstream_name" + upstream_name="$1" + shift + upstream=`git rev-parse --verify "${upstream_name}^0"` || + die "invalid upstream $upstream_name" + unset root_flag + upstream_arg="$upstream_name" +else + test -z "$newbase" && die "--root must be used with --onto" + unset upstream_name + unset upstream + root_flag="--root" + upstream_arg="$root_flag" +fi # Make sure the branch to rebase onto is valid. onto_name=${newbase-"$upstream_name"} onto=$(git rev-parse --verify "${onto_name}^0") || exit # If a hook exists, give it a chance to interrupt -run_pre_rebase_hook ${1+"$@"} +run_pre_rebase_hook "$upstream_arg" "$@" # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) @@ -362,16 +377,16 @@ run_pre_rebase_hook ${1+"$@"} # $head_name -- refs/heads/<that-branch> or "detached HEAD" switch_to= case "$#" in -2) +1) # Is it "rebase other $branchname" or "rebase other $commit"? - branch_name="$2" - switch_to="$2" + branch_name="$1" + switch_to="$1" - if git show-ref --verify --quiet -- "refs/heads/$2" && - branch=$(git rev-parse -q --verify "refs/heads/$2") + if git show-ref --verify --quiet -- "refs/heads/$1" && + branch=$(git rev-parse -q --verify "refs/heads/$1") then - head_name="refs/heads/$2" - elif branch=$(git rev-parse -q --verify "$2") + head_name="refs/heads/$1" + elif branch=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -393,7 +408,8 @@ case "$#" in esac orig_head=$branch -# Now we are rebasing commits $upstream..$branch on top of $onto +# Now we are rebasing commits $upstream..$branch (or with --root, +# everything leading up to $branch) on top of $onto # Check if we are already based on $onto with linear history, # but this should be done only when upstream and onto are the same. @@ -429,10 +445,16 @@ then exit 0 fi +if test ! -z "$rebase_root"; then + revisions="$onto..$orig_head" +else + revisions="$upstream..$orig_head" +fi + if test -z "$do_merge" then git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - "$upstream..$orig_head" | + $root_flag "$revisions" | git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && move_to_original_branch ret=$? @@ -455,7 +477,7 @@ echo "$orig_head" > "$dotest/orig-head" echo "$head_name" > "$dotest/head-name" msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"` +for cmt in `git rev-list --reverse --no-merges "$revisions"` do msgnum=$(($msgnum + 1)) echo "$cmt" > "$dotest/cmt.$msgnum" diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh new file mode 100755 index 0000000..dd91910 --- /dev/null +++ b/t/t3412-rebase-root.sh @@ -0,0 +1,86 @@ +#!/bin/sh + +test_description='git rebase --root + +Tests if git rebase --root --onto <newparent> can rebase the root commit. +' +. ./test-lib.sh + +test_expect_success 'prepare repository' ' + echo 1 > A && + git add A && + git commit -m 1 && + echo 2 > A && + git add A && + git commit -m 2 && + git symbolic-ref HEAD refs/heads/other && + rm .git/index && + echo 3 > B && + git add B && + git commit -m 3 && + echo 1 > A && + git add A && + git commit -m 1b && + echo 4 > B && + git add B && + git commit -m 4 +' + +test_expect_success 'rebase --root expects --onto' ' + test_must_fail git rebase --root +' + +test_expect_success 'setup pre-rebase hook' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +echo "\$1,\$2" >.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' +cat > expect <<EOF +4 +3 +2 +1 +EOF + +test_expect_success 'rebase --root --onto <newbase>' ' + git checkout -b work && + git rebase --root --onto master && + git log --pretty=tformat:"%s" > rebased && + test_cmp expect rebased +' + +test_expect_success 'pre-rebase got correct input (1)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root, +' + +test_expect_success 'rebase --root --onto <newbase> <branch>' ' + git branch work2 other && + git rebase --root --onto master work2 && + git log --pretty=tformat:"%s" > rebased2 && + test_cmp expect rebased2 +' + +test_expect_success 'pre-rebase got correct input (2)' ' + test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2 +' + +test_expect_success 'setup pre-rebase hook that fails' ' + mkdir -p .git/hooks && + cat >.git/hooks/pre-rebase <<EOF && +#!$SHELL_PATH +false +EOF + chmod +x .git/hooks/pre-rebase +' + +test_expect_success 'pre-rebase hook stops rebase' ' + git checkout -b stops1 other && + GIT_EDITOR=: test_must_fail git rebase --root --onto master && + test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1 + test 0 = $(git rev-list other...stops1 | wc -l) +' + +test_done -- tg: (f62b77f..) t/rr-normal (depends on: origin/master t/rebase-hook-order) ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v3.2] rebase: learn to rebase root commit 2009-01-05 17:35 ` [PATCH v3.2] " Thomas Rast @ 2009-01-06 8:19 ` Junio C Hamano 0 siblings, 0 replies; 27+ messages in thread From: Junio C Hamano @ 2009-01-06 8:19 UTC (permalink / raw) To: Thomas Rast; +Cc: git Thomas Rast <trast@student.ethz.ch> writes: > Since this is still in 'pu', can you replace it again? Thanks, done. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit 2009-01-02 22:20 ` Thomas Rast 2009-01-02 22:28 ` [PATCH v3 1/4] rebase -i: execute hook only after argument checking Thomas Rast @ 2009-01-02 22:49 ` Junio C Hamano 2009-01-02 22:54 ` Thomas Rast 1 sibling, 1 reply; 27+ messages in thread From: Junio C Hamano @ 2009-01-02 22:49 UTC (permalink / raw) To: Thomas Rast; +Cc: git Thomas Rast <trast@student.ethz.ch> writes: > I'm still not sure what ${1+"$@"} was about by the way. It is one of the many old-timer's portability idioms that let us work around bugs in some ancient shell implementations. ${1+"$@"} should be equivalent to "$@" in modern Bourne shell variants that are POSIX compliant. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit 2009-01-02 22:49 ` [PATCH v2 1/3] " Junio C Hamano @ 2009-01-02 22:54 ` Thomas Rast 2009-01-02 23:07 ` Junio C Hamano 0 siblings, 1 reply; 27+ messages in thread From: Thomas Rast @ 2009-01-02 22:54 UTC (permalink / raw) To: Junio C Hamano; +Cc: git [-- Attachment #1: Type: text/plain, Size: 477 bytes --] Junio C Hamano wrote: > Thomas Rast <trast@student.ethz.ch> writes: > > > I'm still not sure what ${1+"$@"} was about by the way. > > It is one of the many old-timer's portability idioms that let us work > around bugs in some ancient shell implementations. > > ${1+"$@"} should be equivalent to "$@" in modern Bourne shell variants > that are POSIX compliant. So do you want me to change it back to ${1+"$@"}? -- Thomas Rast trast@{inf,student}.ethz.ch [-- Attachment #2: This is a digitally signed message part. --] [-- Type: application/pgp-signature, Size: 197 bytes --] ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit 2009-01-02 22:54 ` Thomas Rast @ 2009-01-02 23:07 ` Junio C Hamano 0 siblings, 0 replies; 27+ messages in thread From: Junio C Hamano @ 2009-01-02 23:07 UTC (permalink / raw) To: Thomas Rast; +Cc: git Thomas Rast <trast@student.ethz.ch> writes: >> ${1+"$@"} should be equivalent to "$@" in modern Bourne shell variants >> that are POSIX compliant. > > So do you want me to change it back to ${1+"$@"}? No. I was merely answering your question. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: rebase -i: learn to rebase root commit 2008-12-29 21:49 ` Thomas Rast 2008-12-29 22:21 ` Boyd Stephen Smith Jr. @ 2008-12-30 8:22 ` Junio C Hamano 1 sibling, 0 replies; 27+ messages in thread From: Junio C Hamano @ 2008-12-30 8:22 UTC (permalink / raw) To: Thomas Rast; +Cc: git Thomas Rast <trast@student.ethz.ch> writes: > ... is there any sensible > use/interpretation of -p --root that I'm missing? Or should it just > disallow this combination? If --root is about replaying all the history, wouldn't people want to use it together with -p to reconstruct the whole history as a substitute for filter-branch? IOW, I have a suspicion that they most likely should go together. I never felt a need to rebase deep down to --root myself anyway, so I am a bad judge for people's needs on this topic, though. ^ permalink raw reply [flat|nested] 27+ messages in thread
* rebase: update documentation for --root [not found] ` <cover.1230569041.git.trast@student.ethz.ch> 2008-12-29 16:45 ` rebase: learn to rebase root commit Thomas Rast 2008-12-29 16:45 ` rebase -i: " Thomas Rast @ 2008-12-29 16:45 ` Thomas Rast 2 siblings, 0 replies; 27+ messages in thread From: Thomas Rast @ 2008-12-29 16:45 UTC (permalink / raw) To: git; +Cc: Junio C Hamano Since the new option depends on --onto and omission of <upstream>, use a separate invocation style, and omit most options to save space. Signed-off-by: Thomas Rast <trast@student.ethz.ch> --- Documentation/git-rebase.txt | 17 ++++++++++++----- 1 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index c8ad86a..3268cd2 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -8,10 +8,11 @@ git-rebase - Forward-port local commits to the updated upstream head SYNOPSIS -------- [verse] -'git rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] - [-s <strategy> | --strategy=<strategy>] [--no-verify] - [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges] - [--onto <newbase>] <upstream> [<branch>] +'git rebase' [-i | --interactive] [options] [--onto <newbase>] + <upstream> [<branch>] +'git rebase' [-i | --interactive] [options] --onto <newbase> + --root [<branch>] + 'git rebase' --continue | --skip | --abort DESCRIPTION @@ -22,7 +23,8 @@ it remains on the current branch. All changes made by commits in the current branch but that are not in <upstream> are saved to a temporary area. This is the same set -of commits that would be shown by `git log <upstream>..HEAD`. +of commits that would be shown by `git log <upstream>..HEAD` (or +`git log HEAD`, if --root is specified). The current branch is reset to <upstream>, or <newbase> if the --onto option was supplied. This has the exact same effect as @@ -255,6 +257,11 @@ OPTIONS --preserve-merges:: Instead of ignoring merges, try to recreate them. +--root:: + Rebase all commits reachable from <branch>, instead of + limiting them with an <upstream>. This allows you to rebase + the root commit(s) on a branch. Must be used with --onto. + include::merge-strategies.txt[] NOTES -- 1.6.1.1.g4c1d9.dirty ^ permalink raw reply related [flat|nested] 27+ messages in thread
end of thread, other threads:[~2009-01-06 8:21 UTC | newest] Thread overview: 27+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-12-29 16:45 [PATCH 0/3] rebase --root Thomas Rast [not found] ` <cover.1230569041.git.trast@student.ethz.ch> 2008-12-29 16:45 ` rebase: learn to rebase root commit Thomas Rast 2008-12-29 16:45 ` rebase -i: " Thomas Rast 2008-12-29 21:49 ` Thomas Rast 2008-12-29 22:21 ` Boyd Stephen Smith Jr. 2008-12-30 12:23 ` Thomas Rast 2008-12-30 12:29 ` [PATCH v2 1/3] rebase: " Thomas Rast 2008-12-30 12:29 ` [PATCH v2 2/3] rebase -i: " Thomas Rast 2008-12-30 12:29 ` [PATCH v2 3/3] rebase: update documentation for --root Thomas Rast 2009-01-01 21:00 ` [PATCH v2 1/3] rebase: learn to rebase root commit Junio C Hamano 2009-01-02 18:58 ` Johannes Schindelin 2009-01-02 22:20 ` Thomas Rast 2009-01-02 22:28 ` [PATCH v3 1/4] rebase -i: execute hook only after argument checking Thomas Rast 2009-01-02 22:28 ` [PATCH v3 2/4] rebase: learn to rebase root commit Thomas Rast 2009-01-02 22:28 ` [PATCH v3 3/4] rebase -i: " Thomas Rast 2009-01-02 22:28 ` [PATCH v3 4/4] rebase: update documentation for --root Thomas Rast 2009-01-02 22:41 ` [INTERDIFF v3 3/4] rebase -i: learn to rebase root commit Thomas Rast 2009-01-02 22:41 ` [INTERDIFF v3 2/4] rebase: " Thomas Rast 2009-01-02 23:06 ` [PATCH " Junio C Hamano 2009-01-02 23:45 ` [PATCH v3.1] " Thomas Rast 2009-01-05 17:35 ` [PATCH v3.2] " Thomas Rast 2009-01-06 8:19 ` Junio C Hamano 2009-01-02 22:49 ` [PATCH v2 1/3] " Junio C Hamano 2009-01-02 22:54 ` Thomas Rast 2009-01-02 23:07 ` Junio C Hamano 2008-12-30 8:22 ` rebase -i: " Junio C Hamano 2008-12-29 16:45 ` rebase: update documentation for --root Thomas Rast
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).