* Re: [PATCH v2 1/3] rebase: learn to rebase root commit
From: Thomas Rast @ 2009-01-02 22:54 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vzli9b9y6.fsf@gitster.siamese.dyndns.org>
[-- 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
* Re: [PATCH rfc v2] git-sh-setup: Fix scripts whose PWD is a symlink to a work-dir on OS X
From: Marcel Koeppen @ 2009-01-02 22:53 UTC (permalink / raw)
To: git
In-Reply-To: <1230649824-1893-1-git-send-email-marcel@oak.homeunix.org>
Hi,
Am 30.12.2008 um 16:10 schrieb Marcel M. Cary:
> I sent the first rev of this patch to just Brian. It didn't have
> either of the unit test changes. He said it fixed all but t2300.3,
> where cd_to_toplevel doesn't actually "cd", so I made the same change
> to the unit test itself. Can someone with OS X try running the test
> suite with v2 of this patch? I don't have OS X readily available.
the patch fixes t2300-cd-to-toplevel and t5521-pull-symlink for me.
Marcel
[I don't know why my replies get lost, so I dropped all individual
recipients on this third try...]
^ permalink raw reply
* Re: how to track the history of a line in a file
From: david @ 2009-01-02 23:56 UTC (permalink / raw)
To: Jeff King; +Cc: git
In-Reply-To: <alpine.DEB.1.10.0901021544580.21567@asgard.lang.hm>
On Fri, 2 Jan 2009, david@lang.hm wrote:
> On Fri, 2 Jan 2009, david@lang.hm wrote:
>
>> On Fri, 2 Jan 2009, Jeff King wrote:
>>
>>> The tricky thing here is what is "this line"? Using the line number
>>> isn't right, since it will change based on other content coming in and
>>> out of the file. You can keep drilling down by reblaming parent commits,
>>> but remember that each time you do that you are manually looking at the
>>> content and saying "Oh, this is the line I am still interested in." So I
>>> a script would have to correlate the old version and new version of the
>>> line and realize how to follow the "interesting" thing.
>>>
>>> In your case, I think you want to see any commit in Makefile which
>>> changed a line with SUBLEVEL in it. Which is maybe easiest done as:
>>>
>>> git log -z -p Makefile |
>>> perl -0ne 'print if /\n[+-]SUBLEVEL/' |
>>> tr '\0' '\n'
>>>
>>> and is pretty fast. But obviously we're leveraging some content-specific
>>> knowledge about what's in the Makefile.
>>
>> Ok, I hacked togeather a quick bash script to try this
>>
> <SNIP>
>> the problem that this has is that line 3 of $COMMIT may not be line 3 of
>> $COMMIT^, and if they aren't it ends up hunting down the wrong data
>>
>> either that or I am not understanding the output of git blame properly
>> (also very possible)
>
> I was misunderstanding git blame
>
> new script is
>
> #!/bin/bash
> line=`git blame -n -b -l -L /$1/,+1 -M $2`
> echo "-$line"
> foundCOMMIT=`echo "$line" |cut -c -40`
> foundline=`echo "$line" |cut -c 42- |cut -f 1 -d " "`
> while [ "$foundCOMMIT" != " " ] ;do
> #git diff -U0 $foundCOMMIT..$foundCOMMIT^ $2
> line=`git blame -n -b -l -L $foundline,+1 -M $2 $foundCOMMIT^`
> echo "-$line"
> foundCOMMIT=`echo "$line" |cut -c -40`
> foundline=`echo "$line" |cut -c 42- |cut -f 1 -d " "`
> done
>
> this seems to be working for me now.
not quite, it works as long as the line doesn't move in the commit where
it changes.
David Lang
^ permalink raw reply
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit
From: Junio C Hamano @ 2009-01-02 22:49 UTC (permalink / raw)
To: Thomas Rast; +Cc: git
In-Reply-To: <200901022320.14055.trast@student.ethz.ch>
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
* Re: how to track the history of a line in a file
From: david @ 2009-01-02 23:48 UTC (permalink / raw)
To: Jeff King; +Cc: git
In-Reply-To: <alpine.DEB.1.10.0901021459480.21567@asgard.lang.hm>
On Fri, 2 Jan 2009, david@lang.hm wrote:
> On Fri, 2 Jan 2009, Jeff King wrote:
>
>> The tricky thing here is what is "this line"? Using the line number
>> isn't right, since it will change based on other content coming in and
>> out of the file. You can keep drilling down by reblaming parent commits,
>> but remember that each time you do that you are manually looking at the
>> content and saying "Oh, this is the line I am still interested in." So I
>> a script would have to correlate the old version and new version of the
>> line and realize how to follow the "interesting" thing.
>>
>> In your case, I think you want to see any commit in Makefile which
>> changed a line with SUBLEVEL in it. Which is maybe easiest done as:
>>
>> git log -z -p Makefile |
>> perl -0ne 'print if /\n[+-]SUBLEVEL/' |
>> tr '\0' '\n'
>>
>> and is pretty fast. But obviously we're leveraging some content-specific
>> knowledge about what's in the Makefile.
>
> Ok, I hacked togeather a quick bash script to try this
>
<SNIP>
> the problem that this has is that line 3 of $COMMIT may not be line 3 of
> $COMMIT^, and if they aren't it ends up hunting down the wrong data
>
> either that or I am not understanding the output of git blame properly (also
> very possible)
I was misunderstanding git blame
new script is
#!/bin/bash
line=`git blame -n -b -l -L /$1/,+1 -M $2`
echo "-$line"
foundCOMMIT=`echo "$line" |cut -c -40`
foundline=`echo "$line" |cut -c 42- |cut -f 1 -d " "`
while [ "$foundCOMMIT" != " " ] ;do
#git diff -U0 $foundCOMMIT..$foundCOMMIT^ $2
line=`git blame -n -b -l -L $foundline,+1 -M $2 $foundCOMMIT^`
echo "-$line"
foundCOMMIT=`echo "$line" |cut -c -40`
foundline=`echo "$line" |cut -c 42- |cut -f 1 -d " "`
done
this seems to be working for me now.
David Lang
^ permalink raw reply
* [INTERDIFF v3 3/4] rebase -i: learn to rebase root commit
From: Thomas Rast @ 2009-01-02 22:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, bss
In-Reply-To: <43e09eaf2b9a9a3805b9262957ece32190ae4c32.1230935095.git.trast@student.ethz.ch>
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
* [INTERDIFF v3 2/4] rebase: learn to rebase root commit
From: Thomas Rast @ 2009-01-02 22:41 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, bss
In-Reply-To: <7c74d8be216b4667f470e34644c4aa26dcfe0cfb.1230935095.git.trast@student.ethz.ch>
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
* Re: "git reset --hard" == "git checkout HEAD" == "git reset --hard HEAD" ???
From: Junio C Hamano @ 2009-01-02 22:36 UTC (permalink / raw)
To: chris; +Cc: git
In-Reply-To: <20090102195724.GA23119@seberino.org>
chris@seberino.org writes:
> Does "git reset --hard" == "git checkout HEAD" == "git reset --hard HEAD" ???
No, but "reset --hard" without a commit-ish defaults to HEAD so the first
one and the last one are equivalent.
> It seems we have 2 ways to blow away work we haven't checked in yet then right?
Wrong.
^ permalink raw reply
* [PATCH v3 3/4] rebase -i: learn to rebase root commit
From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, bss
In-Reply-To: <6a754e4198413c4051a6085c5e5baab163835463.1230935095.git.trast@student.ethz.ch>
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
* [PATCH v3 2/4] rebase: learn to rebase root commit
From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, bss
In-Reply-To: <6a754e4198413c4051a6085c5e5baab163835463.1230935095.git.trast@student.ethz.ch>
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
* [PATCH v3 4/4] rebase: update documentation for --root
From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, bss
In-Reply-To: <6a754e4198413c4051a6085c5e5baab163835463.1230935095.git.trast@student.ethz.ch>
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
* [PATCH v3 1/4] rebase -i: execute hook only after argument checking
From: Thomas Rast @ 2009-01-02 22:28 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, bss
In-Reply-To: <200901022320.14055.trast@student.ethz.ch>
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
* Re: [PATCH v2 1/3] rebase: learn to rebase root commit
From: Thomas Rast @ 2009-01-02 22:20 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7v4p0iivwh.fsf@gitster.siamese.dyndns.org>
[-- 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
* Re: [PATCH] Documentation/git-bundle.txt: Dumping contents of any bundle
From: jidanni @ 2009-01-02 22:03 UTC (permalink / raw)
To: peff; +Cc: spearce, johannes.schindelin, nico, gitster, mdl123, git
In-Reply-To: <20090102082709.GA3498@coredump.intra.peff.net>
Some options are:
1) just add a line or two to my man page patch showing
what recovery can and can't presently be done. (No need for my
temporary file, use a pipe too.)
2) Also implement that step where everything is uncompressed and put
into lost+found, and document that they should expect to just see a
lot of connector markings, and if there are useful strings in there
then they are just lucky. We did the job asked: recovered to the best
extent of what they gave us.
JK> So I am inclined to leave it as-is: a patch in the list archive. If and
JK> when the day comes when somebody loses some super-important data and
JK> somehow matches all of these criteria, then they can consult whatever
JK> aged and senile git gurus still exist to pull the patch out and see if
JK> anything can be recovered.
I've read too many cases in RISKS Digest, news:comp.risks, about years
later organizations trying to recover some weird format or media.
Therefore I urge you to strike while the iron is hot and hook up the
function into the code.
Maybe some have never tried to recover data, but for those that one
day might, they will be thanking you over and over for taking this
opportunity to give them a chance. In many cases the few shreds they
can recover might be all they need.
Also one can see the innards of git -- no more black box.
If I were creating a new binary format, I would be sure to also
provide decoder tools. Otherwise it is just like it requires its own
proprietary environment to reveal any of its innards. Sure, you can
say well that data is mainly useless... but it is better than nothing
-- we did the best with what they gave us.
^ permalink raw reply
* Re: [PATCH 2/3] unpack-trees: fix path search bug in verify_absent
From: Johannes Schindelin @ 2009-01-02 21:59 UTC (permalink / raw)
To: Clemens Buchacher; +Cc: git, gitster
In-Reply-To: <1230843273-11056-3-git-send-email-drizzd@aon.at>
Hi,
On Thu, 1 Jan 2009, Clemens Buchacher wrote:
> Commit 0cf73755 (unpack-trees.c: assume submodules are clean during
> check-out) changed an argument to verify_absent from 'path' to 'ce',
> which is however shadowed by a local variable of the same name.
This explanation makes sense. However, this:
> @@ -289,7 +289,8 @@ static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmas
> return 0;
> }
>
> -static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
> +static int unpack_callback(int n, unsigned long mask, unsigned long dirmask,
> + struct name_entry *names, struct traverse_info *info)
> {
> struct cache_entry *src[5] = { NULL, };
> struct unpack_trees_options *o = info->data;
... is distracting during review, and this:
> @@ -517,22 +518,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
> namelen = strlen(ce->name);
> pos = index_name_pos(o->src_index, ce->name, namelen);
> if (0 <= pos)
> - return cnt; /* we have it as nondirectory */
> + return 0; /* we have it as nondirectory */
> pos = -pos - 1;
> for (i = pos; i < o->src_index->cache_nr; i++) {
... is not accounted for in the commit message. Intended or not, that is
the question.
Ciao,
Dscho "whether 'tis noble"
^ permalink raw reply
* Re: [PATCH 2/3] unpack-trees: fix path search bug in verify_absent
From: Johannes Schindelin @ 2009-01-02 21:59 UTC (permalink / raw)
To: Clemens Buchacher; +Cc: git, Miklos Vajna, gitster
In-Reply-To: <1230843273-11056-3-git-send-email-drizzd@aon.at>
On Thu, 1 Jan 2009, Clemens Buchacher wrote:
> Commit 0cf73755 (unpack-trees.c: assume submodules are clean during
> check-out) changed an argument to verify_absent from 'path' to 'ce',
> which is however shadowed by a local variable of the same name.
>
> The bug triggers if verify_absent is used on a tree entry, for which
> the index contains one or more subsequent directories of the same
> length. The affected subdirectories are removed from the index. The
> testcase included in this commit bisects to 55218834 (checkout: do not
> lose staged removal), which reveals the bug in this case, but is
> otherwise unrelated.
> ---
Sign-off?
Just for the record, this patch fixes the testcase Miklos reported
earlier.
Ciao,
Dscho
^ permalink raw reply
* Re: [PATCH 1/3 v2] Implement the patience diff algorithm
From: Johannes Schindelin @ 2009-01-02 21:59 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Pierre Habouzit, davidel, Francis Galiegue, Git ML
In-Reply-To: <alpine.DEB.1.00.0901022220380.27818@racer>
Hi,
The interdiff between v1 and v2 of PATCH 1/3. As you can see, I also
added a cleanup of an intermediate xdlenv.
Ciao,
Dscho
xdiff/xpatience.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index 6687940..d01cbdd 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -309,6 +309,8 @@ static int fall_back_to_classic_diff(struct hashmap *map,
memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+ xdl_free_env(&env);
+
return 0;
}
@@ -368,6 +370,15 @@ int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
if (xdl_prepare_env(file1, file2, xpp, env) < 0)
return -1;
+ /*
+ * It is a pity that xdl_cleanup_records() not only marks those
+ * lines as changes that are only present in one file, but also
+ * lines that have multiple matches and happen to be in a "run
+ * of discardable lines" that patience diff happens to split
+ * differently.
+ */
+ memset(env->xdf1.rchg, 0, env->xdf1.nrec);
+ memset(env->xdf2.rchg, 0, env->xdf2.nrec);
/* environment is cleaned up in xdl_diff() */
return patience_diff(file1, file2, xpp, env,
1, env->xdf1.nrec, 1, env->xdf2.nrec);
--
1.6.1.rc3.224.g95ac9
^ permalink raw reply related
* [PATCH 1/3 v2] Implement the patience diff algorithm
From: Johannes Schindelin @ 2009-01-02 21:59 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Pierre Habouzit, davidel, Francis Galiegue, Git ML
In-Reply-To: <alpine.LFD.2.00.0901011151440.5086@localhost.localdomain>
The patience diff algorithm produces slightly more intuitive output
than the classic Myers algorithm, as it does not try to minimize the
number of +/- lines first, but tries to preserve the lines that are
unique.
To this end, it first determines lines that are unique in both files,
then the maximal sequence which preserves the order (relative to both
files) is extracted.
Starting from this initial set of common lines, the rest of the lines
is handled recursively, with Myers' algorithm as a fallback when
the patience algorithm fails (due to no common unique lines).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
I did not realize that xdl_prepare_env() initializes the arrays in
rchg (which tell which lines are not common). Unfortunately, there
are ambiguities, e.g. with empty lines, and my implementation wanted
to take other common lines, disagreeing with the previous
initialization.
Interdiff follows.
xdiff/xdiff.h | 1 +
xdiff/xdiffi.c | 3 +
xdiff/xdiffi.h | 2 +
xdiff/xpatience.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 391 insertions(+), 0 deletions(-)
create mode 100644 xdiff/xpatience.c
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 361f802..4da052a 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -32,6 +32,7 @@ extern "C" {
#define XDF_IGNORE_WHITESPACE (1 << 2)
#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
+#define XDF_PATIENCE_DIFF (1 << 5)
#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
#define XDL_PATCH_NORMAL '-'
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index 9d0324a..3e97462 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -329,6 +329,9 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdalgoenv_t xenv;
diffdata_t dd1, dd2;
+ if (xpp->flags & XDF_PATIENCE_DIFF)
+ return xdl_do_patience_diff(mf1, mf2, xpp, xe);
+
if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
return -1;
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 3e099dc..ad033a8 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -55,5 +55,7 @@ int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
void xdl_free_script(xdchange_t *xscr);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
+int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *env);
#endif /* #if !defined(XDIFFI_H) */
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
new file mode 100644
index 0000000..d01cbdd
--- /dev/null
+++ b/xdiff/xpatience.c
@@ -0,0 +1,385 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+/*
+ * The basic idea of patience diff is to find lines that are unique in
+ * both files. These are intuitively the ones that we want to see as
+ * common lines.
+ *
+ * The maximal ordered sequence of such line pairs (where ordered means
+ * that the order in the sequence agrees with the order of the lines in
+ * both files) naturally defines an initial set of common lines.
+ *
+ * Now, the algorithm tries to extend the set of common lines by growing
+ * the line ranges where the files have identical lines.
+ *
+ * Between those common lines, the patience diff algorithm is applied
+ * recursively, until no unique line pairs can be found; these line ranges
+ * are handled by the well-known Myers algorithm.
+ */
+
+#define NON_UNIQUE ULONG_MAX
+
+/*
+ * This is a hash mapping from line hash to line numbers in the first and
+ * second file.
+ */
+struct hashmap {
+ int nr, alloc;
+ struct entry {
+ unsigned long hash;
+ /*
+ * 0 = unused entry, 1 = first line, 2 = second, etc.
+ * line2 is NON_UNIQUE if the line is not unique
+ * in either the first or the second file.
+ */
+ unsigned long line1, line2;
+ /*
+ * "next" & "previous" are used for the longest common
+ * sequence;
+ * initially, "next" reflects only the order in file1.
+ */
+ struct entry *next, *previous;
+ } *entries, *first, *last;
+ /* were common records found? */
+ unsigned long has_matches;
+ mmfile_t *file1, *file2;
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+/* The argument "pass" is 1 for the first file, 2 for the second. */
+static void insert_record(int line, struct hashmap *map, int pass)
+{
+ xrecord_t **records = pass == 1 ?
+ map->env->xdf1.recs : map->env->xdf2.recs;
+ xrecord_t *record = records[line - 1], *other;
+ /*
+ * After xdl_prepare_env() (or more precisely, due to
+ * xdl_classify_record()), the "ha" member of the records (AKA lines)
+ * is _not_ the hash anymore, but a linearized version of it. In
+ * other words, the "ha" member is guaranteed to start with 0 and
+ * the second record's ha can only be 0 or 1, etc.
+ *
+ * So we multiply ha by 2 in the hope that the hashing was
+ * "unique enough".
+ */
+ int index = (int)((record->ha << 1) % map->alloc);
+
+ while (map->entries[index].line1) {
+ other = map->env->xdf1.recs[map->entries[index].line1 - 1];
+ if (map->entries[index].hash != record->ha ||
+ !xdl_recmatch(record->ptr, record->size,
+ other->ptr, other->size,
+ map->xpp->flags)) {
+ if (++index >= map->alloc)
+ index = 0;
+ continue;
+ }
+ if (pass == 2)
+ map->has_matches = 1;
+ if (pass == 1 || map->entries[index].line2)
+ map->entries[index].line2 = NON_UNIQUE;
+ else
+ map->entries[index].line2 = line;
+ return;
+ }
+ if (pass == 2)
+ return;
+ map->entries[index].line1 = line;
+ map->entries[index].hash = record->ha;
+ if (!map->first)
+ map->first = map->entries + index;
+ if (map->last) {
+ map->last->next = map->entries + index;
+ map->entries[index].previous = map->last;
+ }
+ map->last = map->entries + index;
+ map->nr++;
+}
+
+/*
+ * This function has to be called for each recursion into the inter-hunk
+ * parts, as previously non-unique lines can become unique when being
+ * restricted to a smaller part of the files.
+ *
+ * It is assumed that env has been prepared using xdl_prepare().
+ */
+static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ struct hashmap *result,
+ int line1, int count1, int line2, int count2)
+{
+ result->file1 = file1;
+ result->file2 = file2;
+ result->xpp = xpp;
+ result->env = env;
+
+ /* We know exactly how large we want the hash map */
+ result->alloc = count1 * 2;
+ result->entries = (struct entry *)
+ xdl_malloc(result->alloc * sizeof(struct entry));
+ if (!result->entries)
+ return -1;
+ memset(result->entries, 0, result->alloc * sizeof(struct entry));
+
+ /* First, fill with entries from the first file */
+ while (count1--)
+ insert_record(line1++, result, 1);
+
+ /* Then search for matches in the second file */
+ while (count2--)
+ insert_record(line2++, result, 2);
+
+ return 0;
+}
+
+/*
+ * Find the longest sequence with a smaller last element (meaning a smaller
+ * line2, as we construct the sequence with entries ordered by line1).
+ */
+static int binary_search(struct entry **sequence, int longest,
+ struct entry *entry)
+{
+ int left = -1, right = longest;
+
+ while (left + 1 < right) {
+ int middle = (left + right) / 2;
+ /* by construction, no two entries can be equal */
+ if (sequence[middle]->line2 > entry->line2)
+ right = middle;
+ else
+ left = middle;
+ }
+ /* return the index in "sequence", _not_ the sequence length */
+ return left;
+}
+
+/*
+ * The idea is to start with the list of common unique lines sorted by
+ * the order in file1. For each of these pairs, the longest (partial)
+ * sequence whose last element's line2 is smaller is determined.
+ *
+ * For efficiency, the sequences are kept in a list containing exactly one
+ * item per sequence length: the sequence with the smallest last
+ * element (in terms of line2).
+ */
+static struct entry *find_longest_common_sequence(struct hashmap *map)
+{
+ struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
+ int longest = 0, i;
+ struct entry *entry;
+
+ for (entry = map->first; entry; entry = entry->next) {
+ if (!entry->line2 || entry->line2 == NON_UNIQUE)
+ continue;
+ i = binary_search(sequence, longest, entry);
+ entry->previous = i < 0 ? NULL : sequence[i];
+ sequence[++i] = entry;
+ if (i == longest)
+ longest++;
+ }
+
+ /* No common unique lines were found */
+ if (!longest)
+ return NULL;
+
+ /* Iterate starting at the last element, adjusting the "next" members */
+ entry = sequence[longest - 1];
+ entry->next = NULL;
+ while (entry->previous) {
+ entry->previous->next = entry;
+ entry = entry->previous;
+ }
+ return entry;
+}
+
+static int match(struct hashmap *map, int line1, int line2)
+{
+ xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
+ xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
+ return xdl_recmatch(record1->ptr, record1->size,
+ record2->ptr, record2->size, map->xpp->flags);
+}
+
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2);
+
+static int walk_common_sequence(struct hashmap *map, struct entry *first,
+ int line1, int count1, int line2, int count2)
+{
+ int end1 = line1 + count1, end2 = line2 + count2;
+ int next1, next2;
+
+ for (;;) {
+ /* Try to grow the line ranges of common lines */
+ if (first) {
+ next1 = first->line1;
+ next2 = first->line2;
+ while (next1 > line1 && next2 > line2 &&
+ match(map, next1 - 1, next2 - 1)) {
+ next1--;
+ next2--;
+ }
+ } else {
+ next1 = end1;
+ next2 = end2;
+ }
+ while (line1 < next1 && line2 < next2 &&
+ match(map, line1, line2)) {
+ line1++;
+ line2++;
+ }
+
+ /* Recurse */
+ if (next1 > line1 || next2 > line2) {
+ struct hashmap submap;
+
+ memset(&submap, 0, sizeof(submap));
+ if (patience_diff(map->file1, map->file2,
+ map->xpp, map->env,
+ line1, next1 - line1,
+ line2, next2 - line2))
+ return -1;
+ }
+
+ if (!first)
+ return 0;
+
+ while (first->next &&
+ first->next->line1 == first->line1 + 1 &&
+ first->next->line2 == first->line2 + 1)
+ first = first->next;
+
+ line1 = first->line1 + 1;
+ line2 = first->line2 + 1;
+
+ first = first->next;
+ }
+}
+
+static int fall_back_to_classic_diff(struct hashmap *map,
+ int line1, int count1, int line2, int count2)
+{
+ /*
+ * This probably does not work outside Git, since
+ * we have a very simple mmfile structure.
+ *
+ * Note: ideally, we would reuse the prepared environment, but
+ * the libxdiff interface does not (yet) allow for diffing only
+ * ranges of lines instead of the whole files.
+ */
+ mmfile_t subfile1, subfile2;
+ xpparam_t xpp;
+ xdfenv_t env;
+
+ subfile1.ptr = (char *)map->env->xdf1.recs[line1 - 1]->ptr;
+ subfile1.size = map->env->xdf1.recs[line1 + count1 - 2]->ptr +
+ map->env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+ subfile2.ptr = (char *)map->env->xdf2.recs[line2 - 1]->ptr;
+ subfile2.size = map->env->xdf2.recs[line2 + count2 - 2]->ptr +
+ map->env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+ xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+ if (xdl_do_diff(&subfile1, &subfile2, &xpp, &env) < 0)
+ return -1;
+
+ memcpy(map->env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+ memcpy(map->env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+ xdl_free_env(&env);
+
+ return 0;
+}
+
+/*
+ * Recursively find the longest common sequence of unique lines,
+ * and if none was found, ask xdl_do_diff() to do the job.
+ *
+ * This function assumes that env was prepared with xdl_prepare_env().
+ */
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ struct hashmap map;
+ struct entry *first;
+ int result = 0;
+
+ /* trivial case: one side is empty */
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&map, 0, sizeof(map));
+ if (fill_hashmap(file1, file2, xpp, env, &map,
+ line1, count1, line2, count2))
+ return -1;
+
+ /* are there any matching lines at all? */
+ if (!map.has_matches) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ }
+
+ first = find_longest_common_sequence(&map);
+ if (first)
+ result = walk_common_sequence(&map, first,
+ line1, count1, line2, count2);
+ else
+ result = fall_back_to_classic_diff(&map,
+ line1, count1, line2, count2);
+
+ return result;
+}
+
+int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env)
+{
+ if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+ return -1;
+
+ /*
+ * It is a pity that xdl_cleanup_records() not only marks those
+ * lines as changes that are only present in one file, but also
+ * lines that have multiple matches and happen to be in a "run
+ * of discardable lines" that patience diff happens to split
+ * differently.
+ */
+ memset(env->xdf1.rchg, 0, env->xdf1.nrec);
+ memset(env->xdf2.rchg, 0, env->xdf2.nrec);
+ /* environment is cleaned up in xdl_diff() */
+ return patience_diff(file1, file2, xpp, env,
+ 1, env->xdf1.nrec, 1, env->xdf2.nrec);
+}
--
1.6.1.rc3.224.g95ac9
^ permalink raw reply related
* Re: how to track the history of a line in a file
From: david @ 2009-01-02 23:01 UTC (permalink / raw)
To: Jeff King; +Cc: git
In-Reply-To: <20090102212655.GA24082@coredump.intra.peff.net>
On Fri, 2 Jan 2009, Jeff King wrote:
> The tricky thing here is what is "this line"? Using the line number
> isn't right, since it will change based on other content coming in and
> out of the file. You can keep drilling down by reblaming parent commits,
> but remember that each time you do that you are manually looking at the
> content and saying "Oh, this is the line I am still interested in." So I
> a script would have to correlate the old version and new version of the
> line and realize how to follow the "interesting" thing.
>
> In your case, I think you want to see any commit in Makefile which
> changed a line with SUBLEVEL in it. Which is maybe easiest done as:
>
> git log -z -p Makefile |
> perl -0ne 'print if /\n[+-]SUBLEVEL/' |
> tr '\0' '\n'
>
> and is pretty fast. But obviously we're leveraging some content-specific
> knowledge about what's in the Makefile.
Ok, I hacked togeather a quick bash script to try this
#!/bin/bash
line=`git blame -L /$1/,+1 -M $2`
COMMIT=`echo $line |cut -f 1 -d " "`
foundline=`echo $line |cut -f 6 -d " "|sed s/")"//`
echo "$foundline $COMMIT"
echo "$line"
while [ "$COMMIT" != "" ] ;do
echo "git blame -L $foundline,+1 -M $2 $COMMIT^"
line=`git blame -L $foundline,+1 -M $2 $COMMIT^`
COMMIT=`echo $line |cut -f 1 -d " "`
foundline=`echo $line |cut -f 6 -d " "|sed s/")"//`
echo "$line"
done
the problem that this has is that line 3 of $COMMIT may not be line 3 of
$COMMIT^, and if they aren't it ends up hunting down the wrong data
either that or I am not understanding the output of git blame properly
(also very possible)
David Lang
^ permalink raw reply
* Re: [PATCH 0/3] Teach Git about the patience diff algorithm
From: Johannes Schindelin @ 2009-01-02 21:59 UTC (permalink / raw)
To: Linus Torvalds
Cc: Clemens Buchacher, Adeodato Simó, Pierre Habouzit, davidel,
Francis Galiegue, Git ML
In-Reply-To: <alpine.LFD.2.00.0901020833000.5086@localhost.localdomain>
Hi,
On Fri, 2 Jan 2009, Linus Torvalds wrote:
> So I was hoping for something else than a single "in this case patience
> diff works really well". I was hoping to see what it does in real life.
Funnily, I think the test case you sent me is a pretty good example. Look
at this hunk (without patience diff):
@@ -4205,25 +4205,25 @@ out:
*/
static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_se
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
-
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_setattr(&xdr, res);
- if (status)
- goto out;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
+
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
... and then it goes on with the whole reindented function. Compare this
to the same hunk _with_ patience diff:
@@ -4205,25 +4205,25 @@ out:
*/
static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_se
{
- struct xdr_stream xdr;
- struct compound_hdr hdr;
- int status;
+ struct xdr_stream xdr;
+ struct compound_hdr hdr;
+ int status;
- xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
- status = decode_compound_hdr(&xdr, &hdr);
- if (status)
- goto out;
- status = decode_putfh(&xdr);
- if (status)
- goto out;
- status = decode_setattr(&xdr, res);
- if (status)
- goto out;
+ xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+ status = decode_compound_hdr(&xdr, &hdr);
... and again the rest is reindented code.
The difference? The common empty line. I actually find it more readable
to have the separation between the declarations and the code also in the
diff.
This is just a very feeble example, but you get the idea from there.
Oh, you might object that the empty line is not unique. But actually it
is, because the patience diff recurses into ever smaller line ranges until
it finally comes to such a small range that the empty line _is_ unique.
And in my analysis of the complexity, I stupidly left out that recursion
part. So: patience diff is _substantially_ more expensive than Myers'.
> But when I tried it on the kernel archive, I get a core dump.
I also got this trying on the git.git repository, with commit
be3cfa85([PATCH] Diff-tree-helper take two.) Funnily, I almost got there
trying the same before sending the first revision, but I got impatient and
stopped early. Tsk, tsk.
Now I tried with my complete clone of git.git together with all of my
topic branches, and it runs through without segmentation fault. Patch
follows.
Ciao,
Dscho
^ permalink raw reply
* Re: how to track the history of a line in a file
From: david @ 2009-01-02 22:58 UTC (permalink / raw)
To: Jeff Whiteside; +Cc: Jeff King, git
In-Reply-To: <3ab397d0901021349h4ebae0c1g460a0c8abd4ec072@mail.gmail.com>
On Fri, 2 Jan 2009, Jeff Whiteside wrote:
>> -oldline
>> +newline
>>
>> it's a 1-1 correspondence
>>
>> if it's instead
>> -oldline1
>> -oldline2
>> +newline1
>> +newline2
>
> what a neat idea. i'm going to start malloc'ing 10,000 lines x 120
> chars for each file i add, and edit them so that no new lines replace
> removed lines unless it's intended that they were the same line.
I don't understand your comment. that isn't nessasary to do the tracking
that I'm needing (you don't have to look at every line, only some of the
lines that are in the same hunk of the patch as the line(s) you are
interested in)
in my situation the use case is config files. in them each line is usually
edited independantly of other lines (with stuff being added, usually, but
not always on the end)
David Lang
^ permalink raw reply
* Re: how to track the history of a line in a file
From: Jeff Whiteside @ 2009-01-02 21:49 UTC (permalink / raw)
To: david; +Cc: Jeff King, git
In-Reply-To: <alpine.DEB.1.10.0901021439080.21567@asgard.lang.hm>
> -oldline
> +newline
>
> it's a 1-1 correspondence
>
> if it's instead
> -oldline1
> -oldline2
> +newline1
> +newline2
what a neat idea. i'm going to start malloc'ing 10,000 lines x 120
chars for each file i add, and edit them so that no new lines replace
removed lines unless it's intended that they were the same line.
^ permalink raw reply
* Re: git checkout does not warn about tags without corresponding commits
From: Junio C Hamano @ 2009-01-02 21:44 UTC (permalink / raw)
To: Henrik Austad; +Cc: git
In-Reply-To: <200901021325.58049.henrik@austad.us>
Henrik Austad <henrik@austad.us> writes:
> I recently tried to do a checkout of (what I thought was the first) inux
> kernel in the linux git repo.
>
> git checkout -b 2.6.11 v2.6.11
This should have barfed, and indeed I think it is a regression around
v1.5.5. v1.5.4 and older git definitely fails to check out a tree object
like that.
^ permalink raw reply
* Re: how to track the history of a line in a file
From: david @ 2009-01-02 22:43 UTC (permalink / raw)
To: Jeff King; +Cc: git
In-Reply-To: <20090102212655.GA24082@coredump.intra.peff.net>
On Fri, 2 Jan 2009, Jeff King wrote:
> On Fri, Jan 02, 2009 at 02:13:32PM -0800, david@lang.hm wrote:
>
>> I have a need to setup a repository where I'm storing config files, and I
>> need to be able to search the history of a particular line, not just when
>> the last edit of the line was (which is what I see from git blame)
>
> As you figured out, the "manual" way is to just keep reblaming from the
> parent of each blame. Recent versions of "git gui blame" have a "reblame
> from parent" option in the context menu which makes this a lot less
> painful.
unfortunantly I am needing to do this from the command line.
>> 57f8f7b6 (Linus Torvalds 2008-10-23 20:06:52 -0700 3) SUBLEVEL = 28
>>
>> what I would want it to show would be a list of the commits that have
>> changed this line.
>
> The tricky thing here is what is "this line"? Using the line number
> isn't right, since it will change based on other content coming in and
> out of the file. You can keep drilling down by reblaming parent commits,
> but remember that each time you do that you are manually looking at the
> content and saying "Oh, this is the line I am still interested in." So I
> a script would have to correlate the old version and new version of the
> line and realize how to follow the "interesting" thing.
>
> In your case, I think you want to see any commit in Makefile which
> changed a line with SUBLEVEL in it. Which is maybe easiest done as:
>
> git log -z -p Makefile |
> perl -0ne 'print if /\n[+-]SUBLEVEL/' |
> tr '\0' '\n'
>
> and is pretty fast. But obviously we're leveraging some content-specific
> knowledge about what's in the Makefile.
using the line number shouldn't be _that_ hard becouse git knows what
lines came and went from the file, so it can calculate the new line number
(and does with the -M option)
In my case I would consider 'the same line' to be any lines in the diff
that were taken out when this line was put in
so in the usual case (for me) of
-oldline
+newline
it's a 1-1 correspondence
if it's instead
-oldline1
-oldline2
+newline1
+newline2
I can't know for sure which oldline corresponds to the newline, but the
odds are very good that they are related, so if I widen the search to
cover each of the lines I am probably good.
David Lang
^ permalink raw reply
* Re: why still no empty directory support in git
From: Asheesh Laroia @ 2009-01-02 21:31 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <alpine.DEB.1.00.0901021954410.30769@pacific.mpi-cbg.de>
On Fri, 2 Jan 2009, Johannes Schindelin wrote:
> Hi,
Hi
*wipes the egg off his face*
> On Thu, 1 Jan 2009, Jeff King wrote:
>
>> On Tue, Dec 30, 2008 at 03:58:46AM -0500, Asheesh Laroia wrote:
>>
>>> So, let's say I take your suggestion.
>>>
>>> $ touch ~/Maildir/new/.exists
>>> $ git add ~/Maildir/new/.exists && git commit -m "La di da"
>>>
>>> Now a spec-compliant Maildir user agent will attempt to deliver this new
>>> "email message" of zero bytes into the mail spool and assign it a message
>>> UID. Doing so will remove it from Maildir/new.
>>
>> No. The maildir spec says:
>>
>> A unique name can be anything that doesn't contain a colon (or slash)
>> and doesn't start with a dot.
Oops. I never actually tried this...
> For the record, I am using Git to manage my mails, and never had any
> problems after installing a hook which marks new empty directories with
> .gitignore.
I'll give that a shot, and my apologies for the noise on the list with
regard to this particular example.
I do still believe that git shouldn't rmdir() empty directories behind the
user's back, but with this particular use case gone I'm no longer as
adamant as before.
My apologies for not having tested this earlier; I will test it shortly,
but there's every reason to think that Johannes and Jeff are right!
-- Asheesh.
--
It's interesting to think that many quite distinguished people have
bodies similar to yours.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox