From mboxrd@z Thu Jan 1 00:00:00 1970 From: Thomas Rast Subject: [PATCH v3.1] rebase: learn to rebase root commit Date: Sat, 3 Jan 2009 00:45:15 +0100 Message-ID: <1230939915-3638-1-git-send-email-trast@student.ethz.ch> References: <7vljttb95u.fsf@gitster.siamese.dyndns.org> Cc: Junio C Hamano To: git@vger.kernel.org X-From: git-owner@vger.kernel.org Sat Jan 03 00:46:33 2009 Return-path: Envelope-to: gcvg-git-2@gmane.org Received: from vger.kernel.org ([209.132.176.167]) by lo.gmane.org with esmtp (Exim 4.50) id 1LItiq-0004Fg-Tj for gcvg-git-2@gmane.org; Sat, 03 Jan 2009 00:46:33 +0100 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758117AbZABXpP (ORCPT ); Fri, 2 Jan 2009 18:45:15 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1757698AbZABXpO (ORCPT ); Fri, 2 Jan 2009 18:45:14 -0500 Received: from xsmtp1.ethz.ch ([82.130.70.13]:6218 "EHLO xsmtp1.ethz.ch" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757503AbZABXpM (ORCPT ); Fri, 2 Jan 2009 18:45:12 -0500 Received: from xfe0.d.ethz.ch ([82.130.124.40]) by xsmtp1.ethz.ch with Microsoft SMTPSVC(6.0.3790.3959); Sat, 3 Jan 2009 00:45:09 +0100 Received: from localhost.localdomain ([77.56.223.244]) by xfe0.d.ethz.ch over TLS secured channel with Microsoft SMTPSVC(6.0.3790.3959); Sat, 3 Jan 2009 00:45:09 +0100 X-Mailer: git-send-email 1.6.1.71.gaaa47.dirty In-Reply-To: <7vljttb95u.fsf@gitster.siamese.dyndns.org> X-OriginalArrivalTime: 02 Jan 2009 23:45:09.0297 (UTC) FILETIME=[25944210:01C96D34] Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Archived-At: Teach git-rebase a new option --root, which instructs it to rebase the entire history leading up to . This option must be used with --onto , and causes commits that already exist in to be skipped. (Normal operation skips commits that already exist in 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 --- 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 ] []' +USAGE='[--interactive | -i] [-v] [--onto ] [|--root] []' LONG_USAGE='git-rebase replaces with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to , otherwise it is equal to @@ -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/ 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 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 <.git/PRE-REBASE-INPUT +EOF + chmod +x .git/hooks/pre-rebase +' +cat > expect <' ' + 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 ' ' + 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 <