Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 31 +++++++++++++++++--------------
1 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..1b2381e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -113,6 +113,22 @@ has_action () {
grep '^[^#]' "$1" >/dev/null
}
+redo_merge() {
+ author_script=$(get_author_ident_from_commit $sha1)
+ eval "$author_script"
+ msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
+ if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
+ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
+ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
+ output git merge $STRATEGY -m "$msg" \
+ $new_parents
+ then
+ git rerere
+ printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
+ die Error redoing merge $sha1
+ fi
+}
+
pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -180,22 +196,9 @@ pick_one_preserving_merges () {
echo $sha1 > "$DOTEST"/current-commit
case "$new_parents" in
' '*' '*)
- # redo merge
- author_script=$(get_author_ident_from_commit $sha1)
- eval "$author_script"
- msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
# No point in merging the first parent, that's HEAD
new_parents=${new_parents# $first_parent}
- if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
- GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
- GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
- output git merge $STRATEGY -m "$msg" \
- $new_parents
- then
- git rerere
- printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
- die Error redoing merge $sha1
- fi
+ redo_merge
;;
*)
output git cherry-pick "$@" ||
--
1.5.4.4
--
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 13 +++++++------
1 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 1b2381e..ffd4823 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -114,14 +114,16 @@ has_action () {
}
redo_merge() {
- author_script=$(get_author_ident_from_commit $sha1)
- eval "$author_script"
+ sha1=$1
+ shift
+
+ eval "$(get_author_ident_from_commit $sha1)"
msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
+
if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
- output git merge $STRATEGY -m "$msg" \
- $new_parents
+ output git merge $STRATEGY -m "$msg" "$@"
then
git rerere
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
@@ -197,8 +199,7 @@ pick_one_preserving_merges () {
case "$new_parents" in
' '*' '*)
# No point in merging the first parent, that's HEAD
- new_parents=${new_parents# $first_parent}
- redo_merge
+ redo_merge $sha1 ${new_parents# $first_parent}
;;
*)
output git cherry-pick "$@" ||
--
1.5.4.4
--
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index ffd4823..94c6827 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -131,6 +131,10 @@ redo_merge() {
fi
}
+parents_of_commit() {
+ git rev-list --parents -1 "$1" | cut -d' ' -f2-
+}
+
pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -166,7 +170,7 @@ pick_one_preserving_merges () {
fast_forward=t
preserve=t
new_parents=
- for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
+ for p in $(parents_of_commit $sha1)
do
if test -f "$REWRITTEN"/$p
then
--
1.5.4.4
--
The option --preserve-merges does not allow to change the order of commits or squash them. The new option --linear-history does support this, but doing so it can only look at the commits reachable with through the first parent of each merge. Joining merge commits with other commits leads to problems, because git merge fails with a dirty index (the case “COMMIT squash MERGE”) and squashing a merge leads to the lost of the parents (case “MERGE squash COMMIT”). Therefore, I've prohibited these cases. Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de> --- Documentation/git-rebase.txt | 8 ++++ git-rebase--interactive.sh | 27 +++++++++++++++- t/t3404-rebase-interactive.sh | 72 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletions(-) I had no better idea for a name of this new option. Propositions are welcome. diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index e0412e0..354b6f0 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -11,6 +11,7 @@ SYNOPSIS 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-s <strategy> | --strategy=<strategy>] [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges] + [-l | --linear-history] [--onto <newbase>] <upstream> [<branch>] 'git-rebase' --continue | --skip | --abort @@ -247,6 +248,13 @@ OPTIONS Instead of ignoring merges, try to recreate them. This option only works in interactive mode. +-l, \--linear-history:: + Use only commits of the branch they are not merged in, i.e. + follow only the first parent of a merge. Merges are part of this + list and they will be redone. It's possible to move merges in the + history forward and backward, but they can't take part on a join + (squash). This option only works in interactive mode. + include::merge-strategies.txt[] NOTES diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 94c6827..a2a61f8 100755 --- ...
Hi Johannes,
Me too, but I think it's not possible to do what I want with -p. -p
misses a definition of the (new) parent of a commit. It tries to preserve
all commits from all branches. But going through the _list_ of commands
couldn't preserve this structure.
o--A--B
\ \
C--D--M--E
How should the graph look like after these commands:
pick A
pick C
squash E
# pick D
pick B
pick M
Should
pick A
pick B
pick C
pick D
pick M
pick E
give a same graph like
pick C
pick A
pick D
pick B
pick M
pick E
Bye, J=F6rg.
--=20
< Mr X.> jo: contact an admin to mount it for you
< jo> The admin is not, well how should I say it, he isn't very familiar wi=
th
the system. What should I tell my admin, what he should do?
< Mr X.> taking a sun solaris administration course.
I am beginning to suspect that the root cause of this is that the todo
language is not expressive enough to reproduce a merge _and_ allow end
user editing.
Let's step back a bit.
If you have this history:
o---o---o---o---o---Z
/
X---Y---A---B
\ \
C---D---M---E
and you want to transplant the history between X..E on top of Z, from the
command line you would say:
$ git rebase --interactive -p --onto Z X E
First let's think what you would do if you want to do this by hand. The
sequence would be:
$ git checkout Z^0 ;# detach at Z
$ git cherry-pick Y
$ git tag new-Y ;# remember it
$ git cherry-pick A
$ git cherry-pick B
$ git tag new-B ;# remember it
$ git checkout new-Y
$ git cherry-pick C
$ git cherry-pick D
$ git merge new-B ;# this reproduces M
$ git cherry-pick E
$ git branch -f $the_branch && git checkout $the_branch
Now how does the todo file before you edit look like?
pick Y
pick A
pick B
pick C
pick D
pick M
pick E
The todo file expects the initial detaching and the final switching back
outside of its control, so it is Ok that the first "checkout Z^0" and the
last "branch && checkout" do not appear, but it should be able to express
the remainder and let you tweak. Is it expressive enough to do so?
Most of the "pick" from the above list literally translate to the
"cherry-pick", and if you change any of them to "edit", that is
"cherry-pick --no-edit" followed by a return of control to you with insn
to "rebase --continue" to resume. There appears nothing magical.
Not really. There already are two gotchas even without talking about
end-user editing.
First, "pick M" is not "cherry-pick M". You do not want to end up with
merging an old parent before rewriting. It has to be something like
"merge rewritten-Y".
Second, before you start picking C, if you ...Junio proposed to add the commands mark, merge and reset to rebase interactive for a better support of rebase with preserve merges. The current format can only cope with flat lineare lists of commits and not with the non
This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.
---
git-rebase--interactive.sh | 36 ++++++++++++++++++++++++++++++++++++
t/t3404-rebase-interactive.sh | 21 +++++++++++++++++++++
2 files changed, 57 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..b001ddf 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -23,6 +23,7 @@ DONE="$DOTEST"/done
MSG="$DOTEST"/message
SQUASH_MSG="$DOTEST"/message-squash
REWRITTEN="$DOTEST"/rewritten
+MARKS="$DOTEST"/marks
PRESERVE_MERGES=
STRATEGY=
VERBOSE=
@@ -240,6 +241,23 @@ peek_next_command () {
sed -n "1s/ .*$//p" < "$TODO"
}
+parse_mark() {
+ local tmp
+ tmp="$*"
+
+ case "$tmp" in
+ '#'[0-9]*)
+ tmp="${tmp#\#}"
+ if test "$tmp" = $(printf %d "$tmp")
+ then
+ echo $tmp
+ return 0
+ fi
+ ;;
+ esac
+ return 1
+}
+
do_next () {
rm -f "$DOTEST"/message "$DOTEST"/author-script \
"$DOTEST"/amend || exit
@@ -317,6 +335,23 @@ do_next () {
die_with_patch $sha1 ""
fi
;;
+ mark|a)
+ if ! mark=$(parse_mark $sha1)
+ then
+ warn "Invalid mark given: $command $sha1 $rest"
+ die_with_patch $sha1 \
+ "Please fix this in the file $TODO."
+ fi
+ mark_action_done
+
+ test -d "$MARKS" || mkdir "$MARKS"
+
+ test -e "$MARKS"/$mark && \
+ warn "mark $mark already exist; overwriting it"
+
+ git rev-parse --verify HEAD > "$MARKS"/$mark || \
+ die "HEAD is invalid"
+ ;;
*)
warn "Unknown command: $command $sha1 $rest"
die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -533,6 +568,7 @@ do
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
+# mark #NUM = mark the current HEAD for later reference
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the ...---
git-rebase--interactive.sh | 20 ++++++++++++++++++++
t/t3404-rebase-interactive.sh | 10 ++++++++++
2 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b001ddf..7dac51b 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -258,6 +258,16 @@ parse_mark() {
return 1
}
+mark_to_sha1() {
+ local mark
+ if mark=$(parse_mark $1)
+ then
+ cat "$MARKS"/$mark
+ else
+ return 1
+ fi
+}
+
do_next () {
rm -f "$DOTEST"/message "$DOTEST"/author-script \
"$DOTEST"/amend || exit
@@ -352,6 +362,15 @@ do_next () {
git rev-parse --verify HEAD > "$MARKS"/$mark || \
die "HEAD is invalid"
;;
+ reset|r)
+ comment_for_reflog reset
+
+ mark_action_done
+ tmp=$(mark_to_sha1 $sha1) || \
+ tmp=$(git rev-parse --verify $sha1) ||
+ die "Invalid parent '$sha1' in $command $sha1 $rest"
+ output git reset --hard $tmp
+ ;;
*)
warn "Unknown command: $command $sha1 $rest"
die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -569,6 +588,7 @@ do
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
# mark #NUM = mark the current HEAD for later reference
+# reset #NUM|commit = reset HEAD to a previous set mark or a commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 4461674..521206f 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -85,6 +85,9 @@ for line in $FAKE_LINES; do
mark*)
echo "mark ${line#mark}"
echo "mark ${line#mark}" >> "$1";;
+ reset*)
+ echo "reset ${line#reset}"
+ echo "reset ${line#reset}" >> "$1";;
*)
echo sed -n "${line}s/^pick/$action/p"
sed -n "${line}p" < "$1".tmp
@@ -210,6 +213,13 @@ test_expect_success 'setting marks works' '
git rebase --abort
'
...This command redoes merges. It's useful if you rebase a branch that
contains merges and you want to preserve these merges. You can also use
it to add new merges.
---
git-rebase--interactive.sh | 25 +++++++++++++++++++++++++
t/t3404-rebase-interactive.sh | 3 +++
2 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 7dac51b..18cdf3d 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -362,6 +362,29 @@ do_next () {
git rev-parse --verify HEAD > "$MARKS"/$mark || \
die "HEAD is invalid"
;;
+ merge|m)
+ comment_for_reflog merge
+
+ if ! git rev-parse --verify $sha1
+ then
+ die "Invalid reference merge '$sha1' in $command $sha1 $rest"
+ fi
+
+ new_parents=
+ for p in $rest
+ do
+ tmp=$(mark_to_sha1 $p) || \
+ tmp=$(git rev-parse --verify $p) ||
+ die "Invalid parent '$sha1' in $command $sha1 $rest"
+ new_parents="$new_parents $tmp"
+ done
+ new_parents="${new_parents# }"
+ test -n "$new_parents" || \
+ die "You forgot to give the parents for the merge"
+
+ mark_action_done
+ redo_merge $sha1 $new_parents
+ ;;
reset|r)
comment_for_reflog reset
@@ -589,6 +612,8 @@ do
# squash = use commit, but meld into previous commit
# mark #NUM = mark the current HEAD for later reference
# reset #NUM|commit = reset HEAD to a previous set mark or a commit
+# merge commit-M #NUM|commit-P ... = redo merge commit-M with the
+# current HEAD and the parents marked with #NUM or the commit-P
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 521206f..892fd5d 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -88,6 +88,9 @@ for line in $FAKE_LINES; do
reset*)
echo "reset ${line#reset}"
echo "reset ${line#reset}" >> ...---
git-rebase--interactive.sh | 31 +++++++++++++++++--------------
1 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 18cdf3d..973770e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -114,6 +114,22 @@ has_action () {
grep '^[^#]' "$1" >/dev/null
}
+redo_merge() {
+ author_script=$(get_author_ident_from_commit $sha1)
+ eval "$author_script"
+ msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
+ if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
+ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
+ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
+ output git merge $STRATEGY -m "$msg" \
+ $new_parents
+ then
+ git rerere
+ printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
+ die Error redoing merge $sha1
+ fi
+}
+
pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -181,22 +197,9 @@ pick_one_preserving_merges () {
echo $sha1 > "$DOTEST"/current-commit
case "$new_parents" in
' '*' '*)
- # redo merge
- author_script=$(get_author_ident_from_commit $sha1)
- eval "$author_script"
- msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
# No point in merging the first parent, that's HEAD
new_parents=${new_parents# $first_parent}
- if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
- GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
- GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
- output git merge $STRATEGY -m "$msg" \
- $new_parents
- then
- git rerere
- printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
- die Error redoing merge $sha1
- fi
+ redo_merge
;;
*)
output git cherry-pick "$@" ||
--
1.5.4.5
--
---
git-rebase--interactive.sh | 14 ++++++++------
1 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 973770e..1698c3e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -115,14 +115,17 @@ has_action () {
}
redo_merge() {
- author_script=$(get_author_ident_from_commit $sha1)
- eval "$author_script"
+ local sha1
+ sha1=$1
+ shift
+
+ eval "$(get_author_ident_from_commit $sha1)"
msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
+
if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
- output git merge $STRATEGY -m "$msg" \
- $new_parents
+ output git merge $STRATEGY -m "$msg" "$@"
then
git rerere
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
@@ -198,8 +201,7 @@ pick_one_preserving_merges () {
case "$new_parents" in
' '*' '*)
# No point in merging the first parent, that's HEAD
- new_parents=${new_parents# $first_parent}
- redo_merge
+ redo_merge $sha1 ${new_parents# $first_parent}
;;
*)
output git cherry-pick "$@" ||
--
1.5.4.5
--
--- git-rebase--interactive.sh | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 1698c3e..060b40f 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -600,9 +600,9 @@ do MERGES_OPTION=--no-merges fi - SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) - SHORTHEAD=$(git rev-parse --short $HEAD) - SHORTONTO=$(git rev-parse --short $ONTO) + SHORTUPSTREAM=$(git rev-parse --short=7 $UPSTREAM) + SHORTHEAD=$(git rev-parse --short=7 $HEAD) + SHORTONTO=$(git rev-parse --short=7 $ONTO) git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse --left-right --cherry-pick \ $UPSTREAM...$HEAD | \ -- 1.5.4.5 --
It's much helpful to see the TODO list generated by rebase in the verbose
output. This makes it easier to check, if the list was not broken by
design.
---
t/t3404-rebase-interactive.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 892fd5d..aa4bb8d 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -73,7 +73,7 @@ esac
test -z "$EXPECT_COUNT" ||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
exit
-test -z "$FAKE_LINES" && exit
+test -z "$FAKE_LINES" && { grep -v '^#' "$1"; exit; }
grep -v '^#' < "$1" > "$1".tmp
rm -f "$1"
cat "$1".tmp
--
1.5.4.5
--
Picked from <7vabko3dm2.fsf@gitster.siamese.dyndns.org> From: gitster@pobox.com (Junio C Hamano) Date: Sun, 23 Mar 2008 22:17:09 -0700 --- git-merge.sh | 24 +++++++++++++----------- 1 files changed, 13 insertions(+), 11 deletions(-) diff --git a/git-merge.sh b/git-merge.sh index 7dbbb1d..bd9699d 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -250,17 +250,19 @@ else # We are invoked directly as the first-class UI. head_arg=HEAD - # All the rest are the commits being merged; prepare - # the standard merge summary message to be appended to - # the given message. If remote is invalid we will die - # later in the common codepath so we discard the error - # in this loop. - merge_name=$(for remote - do - merge_name "$remote" - done | git fmt-merge-msg - ) - merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name" + if test -z "$merge_msg" + then + # All the rest are the commits being merged; prepare + # the standard merge summary message to be appended to + # the given message. If remote is invalid we will die + # later in the common codepath so we discard the error + # in this loop. + merge_msg=$(for remote + do + merge_name "$remote" + done | git fmt-merge-msg + ) + fi fi head=$(git rev-parse --verify "$head_arg"^0) || usage -- 1.5.4.5 --
The old fake-editor selects only lines they start with pick, if you give
the number of a line. With the new commands mark, merge and reset it was
not possible to select such lines for the new TODO list. The new
fake-editor selects all kinds of lines, but replaces only the command
“pick” with a different action.
---
t/t3404-rebase-interactive.sh | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index aa4bb8d..be26a78 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -92,9 +92,8 @@ for line in $FAKE_LINES; do
echo "merge ${line#merge}" | tr / ' '
echo "merge ${line#merge}" | tr / ' ' >> "$1";;
*)
- echo sed -n "${line}s/^pick/$action/p"
- sed -n "${line}p" < "$1".tmp
- sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+ sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
+ sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
action=pick;;
esac
done
--
1.5.4.5
--
---
git-rebase--interactive.sh | 248 ++++++++++++++++++++++++-----------------
t/t3404-rebase-interactive.sh | 34 ++++++
2 files changed, 181 insertions(+), 101 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 060b40f..27bd87e 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -22,12 +22,10 @@ TODO="$DOTEST"/git-rebase-todo
DONE="$DOTEST"/done
MSG="$DOTEST"/message
SQUASH_MSG="$DOTEST"/message-squash
-REWRITTEN="$DOTEST"/rewritten
MARKS="$DOTEST"/marks
PRESERVE_MERGES=
STRATEGY=
VERBOSE=
-test -d "$REWRITTEN" && PRESERVE_MERGES=t
test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
test -f "$DOTEST"/verbose && VERBOSE=t
@@ -137,8 +135,6 @@ pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
- test -d "$REWRITTEN" &&
- pick_one_preserving_merges "$@" && return
parent_sha1=$(git rev-parse --verify $sha1^) ||
die "Could not get the parent of $sha1"
current_sha1=$(git rev-parse --verify HEAD)
@@ -152,66 +148,6 @@ pick_one () {
fi
}
-pick_one_preserving_merges () {
- case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
- sha1=$(git rev-parse $sha1)
-
- if test -f "$DOTEST"/current-commit
- then
- current_commit=$(cat "$DOTEST"/current-commit) &&
- git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
- rm "$DOTEST"/current-commit ||
- die "Cannot write current commit's replacement sha1"
- fi
-
- # rewrite parents; if none were rewritten, we can fast-forward.
- fast_forward=t
- preserve=t
- new_parents=
- for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
- do
- if test -f "$REWRITTEN"/$p
- then
- preserve=f
- new_p=$(cat "$REWRITTEN"/$p)
- test $p != $new_p && fast_forward=f
- case "$new_parents" in
- *$new_p*)
- ;; # do nothing; that parent is already there
- *)
- new_parents="$new_parents ...Lacking is a better justification as to why this is a good change and 7 is a good number. --
Hi Junio,
=E2=80=9CThis makes it easier to test for equality of a commit in the TODO =
list
I don't know why 7 is a good number. It was there from the beginning:
% git show 1b1dce4b:git-rebase--interactive.sh G -C1 \=3D7
git rev-list --no-merges --pretty=3Doneline --abbrev-commit=
\
--abbrev=3D7 --reverse $UPSTREAM..$HEAD | \
sed "s/^/pick /" >> "$TODO"
Bye, J=C3=B6rg.
--=20
Damit das M=C3=B6gliche entsteht, mu=C3=9F immer wieder das Unm=C3=B6gliche=
versucht
werden. (Hermann Hesse)
This cat may become "rev-parse --verify" if we decide to move $MARKS
"to a previously set mark". But I would say upfront "in the todo insn
whenever you need to refer to a commit, in addition to
the usual commit object name, you can use '#num' syntax to refer to a
commit previously marked with the 'mark' insn." and make this line just
read:
# reset commit = reset HEAD to the commit
--
Hi Junio, I don't expect it. tmp is a valid sha1 and reset may fail if the working directory is dirty, but then the previous command should have failed, Does this mean pick, edit and squash should understand marks, too? But how useful is this? You can only set a mark if you've picked a commit and using this commit again, e.g. pick it twice, doesn't sound useful. --=20 $ cat /dev/random #!/usr/bin/perl -WT print "hello world\n";
I wonder if this should live somewhere inside $GIT_DIR/refs namespace, so
that if any object pruning is triggered ever while rebasing interactively
Wouldn't
pick 5cc8f37 (init: show "Reinit" message even in ...)
mark 1
pick 18d077c (quiltimport: fix misquoting of parse...)
mark 2
reset 1
merge #2
be easier for people?
When you reference a commit with mark name, it is reasonable to require it
to be prefixed with '#', which is a character that cannot be either in
refname nor hex representation of a commit object. I think it is Ok to
accept an optional prefix when defining one, e.g. "mark #47", but I do not
I understand that the reason why you did not pick a more obvious 'm' is
because you would want to add 'merge' later and give it 'm', but we do not
have to give a single-letter equivalent to all commands especially when
there is not an appropriate one.
--
Hi Junio,
=E2=80=9CIEEE P1003.2 Draft 11.2 - September 1991=E2=80=9D, page 277:
Local variables within a function were considered and included in Draft
9 (controlled by the special built-in local), but were removed because
they do not fit the simple model developed for the scoping of functions
and there was some opposition to adding yet another new special built-in
from outside existing practice. Implementations should reserve the
identifier local (as well as typeset, as used in the KornShell) in case
this local variable mechanism is adopted in a future version of POSIX.2.
=E2=80=A6 but I didn't found it in =E2=80=9CIEEE Std 1003.1, 2004 Edition=
=E2=80=9Creset 18d077c~2=E2=80=9D or =E2=80=9Creset some-tag=E2=80=9D or =
I don't know. Using the special sign everywhere a mark is used looks more
consistent to me. The only case where it might be omitted is the mark
That sounds useful.
BTW: Should the mark be a number or a string, e.g. 001 =3D=3D 1 or 001 !=3D=
That's fine. I thought it's a requirement.
Bye, J=C3=B6rg.
--=20
Was der Bauer nicht kennt, das frisst er nicht. W=C3=BCrde der St=C3=A4dter=
kennen,
was er frisst, er w=C3=BCrde umgehend Bauer werden.
Oliver Hassencamp
Why not use the mark syntax that fast-import uses? In fast-import we use ":n" anytime we need to refer to a mark, e.g. ":1" or ":5". Its the same idea. We already have a language for it. Heck, the commands above are bordering on a language not too far from the one that fast-import accepts. :-) -- Shawn. --
Hi Shawn,
Currently, I don't restrict the mark to be a number. It can anything that
is a valid ref. Should I restrict it?
And how do you handle the :/ syntax? =E2=80=9Creset :/Bla=E2=80=9D is than =
not valid.
Mmm, I'll add an exception for :/.
Except of this, I prefer to use the colon to be much closer to the syntax
of fast-import.
Bye, J=C3=B6rg.
--=20
Der Wunsch, klug zu erscheinen, verhindert oft, es zu werden.
(Francois de la Rochefoucauld)
In fast-import a mark can *only* be a number. It cannot be a ref string or anything complex like that. This reduces the memory load I think the ':/' syntax came along after fast-import had already started to use ':' as the mark syntax. I forgot to object to this bastard form of looking up a commit when it was introduced by Dscho and now we have a SHA-1 expression syntax that fast-import will confuse with a mark. I originally had chosen to start a mark off with ':' because it is not an allowed character in a ref, due to its use to split src:dst in a fetch Me too, but it looks like in a human edited "TODO" script we may want to be more friendly and allow named marks. Though I'm not sure that is really all that useful. If you are merging something because it used to be merged before the rebase I doubt we'd generate a meaningful mark name when the TODO script is initially formatted. -- Shawn. --
I'd say a small integer is the only thing we would need. The TODO insn sequence would be machine generated then manually tweaked, not the other way around. Regarding colon vs pound, I would say that the 'mark' insn can just say "2" to set the second mark, and then store it in refs/marks/2; the commands that refer to the commit later can use the usual "refs/marks/2" or "marks/2" syntax without colon nor pound nor any other special syntax that way. --
Hi Shawn, Only for the record: fast-import uses numbers and not strings of digits, i.e. 001 =3D=3D 1, and it ignores stuff following digits, i.e. I don't think so. There shouldn't be so much marks that a user can't remember them. Thanks for your comments. J=F6rg. --=20 The UNIX Guru's View of Sex: # unzip ; strip ; touch ; finger ; mount ; fsck ; more ; yes ; umount ; sle= ep
What would be wrong with using the existing tag machinery for this instead? Mike PS: Apologies to anyone who got an html version of this mail. --
Hallo Mike, d? You may have to deal with conflicts if users named tags 03 or 10. But Junio suggested to use a ref, too. I think refs/rebase-marks/ is a good prefix. Bye, J=F6rg. --=20 Der Hase l=E4uft schneller als der Fuchs, denn der Hase l=E4uft um sein Leben.
It's much helpful to see the TODO list generated by rebase in the verbose
output of the test. This makes it easier to check, if the list was not
broken from the beginning.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
t/t3404-rebase-interactive.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 9cf873f..8d29878 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -73,7 +73,7 @@ esac
test -z "$EXPECT_COUNT" ||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
exit
-test -z "$FAKE_LINES" && exit
+test -z "$FAKE_LINES" && { grep -v '^#' "$1"; exit; }
grep -v '^#' < "$1" > "$1".tmp
rm -f "$1"
cat "$1".tmp
--
1.5.5
--
Picked from <7vabko3dm2.fsf@gitster.siamese.dyndns.org> From: gitster@pobox.com (Junio C Hamano) Date: Sun, 23 Mar 2008 22:17:09 -0700 Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de> --- git-merge.sh | 24 +++++++++++++----------- 1 files changed, 13 insertions(+), 11 deletions(-) diff --git a/git-merge.sh b/git-merge.sh index 7dbbb1d..bd9699d 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -250,17 +250,19 @@ else # We are invoked directly as the first-class UI. head_arg=HEAD - # All the rest are the commits being merged; prepare - # the standard merge summary message to be appended to - # the given message. If remote is invalid we will die - # later in the common codepath so we discard the error - # in this loop. - merge_name=$(for remote - do - merge_name "$remote" - done | git fmt-merge-msg - ) - merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name" + if test -z "$merge_msg" + then + # All the rest are the commits being merged; prepare + # the standard merge summary message to be appended to + # the given message. If remote is invalid we will die + # later in the common codepath so we discard the error + # in this loop. + merge_msg=$(for remote + do + merge_name "$remote" + done | git fmt-merge-msg + ) + fi fi head=$(git rev-parse --verify "$head_arg"^0) || usage -- 1.5.5 --
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 10 +++++++---
1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 8aa7371..531ee94 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -104,8 +104,12 @@ die_with_patch () {
die "$2"
}
-die_abort () {
+cleanup_before_quit () {
rm -rf "$DOTEST"
+}
+
+die_abort () {
+ cleanup_before_quit
die "$1"
}
@@ -352,7 +356,7 @@ do_next () {
test ! -f "$DOTEST"/verbose ||
git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
} &&
- rm -rf "$DOTEST" &&
+ cleanup_before_quit &&
git gc --auto &&
warn "Successfully rebased and updated $HEADNAME."
@@ -414,7 +418,7 @@ do
;;
esac &&
output git reset --hard $HEAD &&
- rm -rf "$DOTEST"
+ cleanup_before_quit
exit
;;
--skip)
--
1.5.5
--
This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.
The decision to use references for the marks and not files like done with
the rewritten commits for preserve merges was made to ensure no commit
objects get lost if prune is started while (a long term) rebase is
running. This also unifies the checking of the validity of marks and
references by using rev-parse for it.
The usage of : as the sign for marks conforms with the tag sign of
fast-export and fast-import.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 37 ++++++++++++++++++++++++++++++++++++-
t/t3404-rebase-interactive.sh | 17 +++++++++++++++++
2 files changed, 53 insertions(+), 1 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 531ee94..6ac316a 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,6 +35,8 @@ mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'"
export GIT_CHERRY_PICK_HELP
+mark_prefix=refs/rebase-marks/
+
warn () {
echo "$*" >&2
}
@@ -105,7 +107,13 @@ die_with_patch () {
}
cleanup_before_quit () {
- rm -rf "$DOTEST"
+ rm -rf "$DOTEST" &&
+ for ref in "$GIT_DIR/$mark_prefix"*
+ do
+ test "$ref" = "$GIT_DIR/$mark_prefix*" && continue
+ git update-ref -d "${ref#$GIT_DIR/}" "${ref#$GIT_DIR/}" || \
+ return 1
+ done
}
die_abort () {
@@ -244,6 +252,19 @@ peek_next_command () {
sed -n "1s/ .*$//p" < "$TODO"
}
+mark_to_ref () {
+ case "$1" in
+ :[!/]*)
+ # :/SOMETHING is a reference for the last commit whose
+ # message starts with SOMETHING
+ echo "$mark_prefix${1#:}"
+ ;;
+ *)
+ echo "$1"
+ ;;
+ esac
+}
+
do_next () {
rm -f "$DOTEST"/message "$DOTEST"/author-script \
"$DOTEST"/amend || exit
@@ -321,6 +342,15 @@ do_next () {
die_with_patch $sha1 ""
fi
...This command does a hard reset of the HEAD, i.e. the next operation used
this commit as parent. This is necessary to redo the commits of different
branches they become merged later.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 10 ++++++++++
t/t3404-rebase-interactive.sh | 17 +++++++++++++++++
2 files changed, 27 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 6ac316a..a4b7aad 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -351,6 +351,15 @@ do_next () {
git update-ref "$mark" HEAD || die "update-ref failed"
;;
+ reset|r)
+ comment_for_reflog reset
+
+ tmp=$(git rev-parse --verify "$(mark_to_ref $sha1)") ||
+ die "Invalid parent '$sha1' in $command $sha1 $rest"
+
+ mark_action_done
+ output git reset --hard $tmp
+ ;;
*)
warn "Unknown command: $command $sha1 $rest"
die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -572,6 +581,7 @@ do
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
# mark :mark = mark the current HEAD for later reference
+# reset commit = reset HEAD to the commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index fa3560e..78673a6 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -85,6 +85,9 @@ for line in $FAKE_LINES; do
mark*)
echo "mark ${line#mark}"
echo "mark ${line#mark}" >> "$1";;
+ reset*)
+ echo "reset ${line#reset}"
+ echo "reset ${line#reset}" >> "$1";;
*)
echo sed -n "${line}s/^pick/$action/p"
sed -n "${line}p" < "$1".tmp
@@ -206,6 +209,20 @@ test_expect_success 'setting marks works' '
ls $marks_dir | wc -l | grep -Fx 0
'
+test_expect_success 'reset with nonexistent mark fails' '
+ export ...Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 35 ++++++++++++++++++++---------------
1 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index a4b7aad..19145b1 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -125,6 +125,25 @@ has_action () {
grep '^[^#]' "$1" >/dev/null
}
+redo_merge () {
+ rm_sha1=$1
+ shift
+
+ eval "$(get_author_ident_from_commit $rm_sha1)"
+ msg="$(git cat-file commit $rm_sha1 | sed -e '1,/^$/d')"
+
+ if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
+ GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
+ GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
+ output git merge $STRATEGY -m "$msg" "$@"
+ then
+ git rerere
+ printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
+ die Error redoing merge $rm_sha1
+ fi
+ unset rm_sha1
+}
+
pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
@@ -192,22 +211,8 @@ pick_one_preserving_merges () {
echo $sha1 > "$DOTEST"/current-commit
case "$new_parents" in
' '*' '*)
- # redo merge
- author_script=$(get_author_ident_from_commit $sha1)
- eval "$author_script"
- msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
# No point in merging the first parent, that's HEAD
- new_parents=${new_parents# $first_parent}
- if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
- GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
- GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
- output git merge $STRATEGY -m "$msg" \
- $new_parents
- then
- git rerere
- printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
- die Error redoing merge $sha1
- fi
+ redo_merge $sha1 ${new_parents# $first_parent}
;;
*)
output git cherry-pick "$@" ||
--
1.5.5
--
This command redoes merges. It's useful if you rebase a branch that
contains merges and you want to preserve these merges. You can also use
it to add new merges.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 24 ++++++++++++++++++++++++
t/t3404-rebase-interactive.sh | 13 +++++++++++++
2 files changed, 37 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 19145b1..fd41ca0 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -356,6 +356,28 @@ do_next () {
git update-ref "$mark" HEAD || die "update-ref failed"
;;
+ merge|m)
+ comment_for_reflog merge
+
+ if ! git rev-parse --verify $sha1 > /dev/null
+ then
+ die "Invalid reference merge '$sha1' in"
+ "$command $sha1 $rest"
+ fi
+
+ new_parents=
+ for p in $rest
+ do
+ new_parents="$new_parents $(mark_to_ref $p)"
+ done
+ new_parents="${new_parents# }"
+ test -n "$new_parents" || \
+ die "You forgot to give the parents for the" \
+ "merge $sha1. Please fix it in $TODO"
+
+ mark_action_done
+ redo_merge $sha1 $new_parents
+ ;;
reset|r)
comment_for_reflog reset
@@ -587,6 +609,8 @@ do
# squash = use commit, but meld into previous commit
# mark :mark = mark the current HEAD for later reference
# reset commit = reset HEAD to the commit
+# merge commit-M commit-P ... = redo merge commit-M with the
+# current HEAD and the parents commit-P
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 78673a6..ceb9d74 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -88,6 +88,9 @@ for line in $FAKE_LINES; do
reset*)
echo "reset ${line#reset}"
echo "reset ${line#reset}" >> "$1";;
+ merge*)
+ echo "merge ${line#merge}" | tr / ' '
+ echo "merge ...This makes it easier to test for equality of a commit in the TODO list and one of SHORTUPSTREAM, SHORTHEAD or SHORTONTO. Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de> --- git-rebase--interactive.sh | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index fd41ca0..d0a7e5c 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -588,9 +588,9 @@ do MERGES_OPTION=--no-merges fi - SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) - SHORTHEAD=$(git rev-parse --short $HEAD) - SHORTONTO=$(git rev-parse --short $ONTO) + SHORTUPSTREAM=$(git rev-parse --short=7 $UPSTREAM) + SHORTHEAD=$(git rev-parse --short=7 $HEAD) + SHORTONTO=$(git rev-parse --short=7 $ONTO) git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ --abbrev=7 --reverse --left-right --cherry-pick \ $UPSTREAM...$HEAD | \ -- 1.5.5 --
The old fake-editor selects only lines they start with pick, if you give
the number of a line. With the new commands mark, merge and reset it was
not possible to select such lines for the new TODO list. The new
fake-editor selects all kinds of lines, but replaces only the command
“pick” with a different action.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
t/t3404-rebase-interactive.sh | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ceb9d74..0a8d065 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -92,9 +92,8 @@ for line in $FAKE_LINES; do
echo "merge ${line#merge}" | tr / ' '
echo "merge ${line#merge}" | tr / ' ' >> "$1";;
*)
- echo sed -n "${line}s/^pick/$action/p"
- sed -n "${line}p" < "$1".tmp
- sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+ sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
+ sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
action=pick;;
esac
done
--
1.5.5
--
The current algorithmus used to rebase a branch with merges on top of
another has some drawbacks: it's not possible to squash commits, it's not
possible to change the order of commits, particularly the tip of the
branch can't change.
This new algorithmus uses the idea from Junio to create a TODO list with
the commands mark, merge and reset to represent the nonlinear structure
of merges.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 239 ++++++++++++++++++++++++-----------------
t/t3404-rebase-interactive.sh | 37 +++++++
2 files changed, 175 insertions(+), 101 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index d0a7e5c..d3327a8 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -22,11 +22,9 @@ TODO="$DOTEST"/git-rebase-todo
DONE="$DOTEST"/done
MSG="$DOTEST"/message
SQUASH_MSG="$DOTEST"/message-squash
-REWRITTEN="$DOTEST"/rewritten
PRESERVE_MERGES=
STRATEGY=
VERBOSE=
-test -d "$REWRITTEN" && PRESERVE_MERGES=t
test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
test -f "$DOTEST"/verbose && VERBOSE=t
@@ -148,8 +146,6 @@ pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
- test -d "$REWRITTEN" &&
- pick_one_preserving_merges "$@" && return
parent_sha1=$(git rev-parse --verify $sha1^) ||
die "Could not get the parent of $sha1"
current_sha1=$(git rev-parse --verify HEAD)
@@ -163,66 +159,6 @@ pick_one () {
fi
}
-pick_one_preserving_merges () {
- case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
- sha1=$(git rev-parse $sha1)
-
- if test -f "$DOTEST"/current-commit
- then
- current_commit=$(cat "$DOTEST"/current-commit) &&
- git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
- rm "$DOTEST"/current-commit ||
- die "Cannot write current commit's replacement sha1"
- fi
-
- # rewrite parents; if none were rewritten, we ...With this new option it's possible to narrow the list of commits in the TODO list to only those commits you get following the first parent of each merge, i.e. not those from the merged branches. Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de> --- Documentation/git-rebase.txt | 7 ++++++- git-rebase--interactive.sh | 15 +++++++++++---- t/t3404-rebase-interactive.sh | 12 ++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index e0412e0..9ebbb90 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -11,7 +11,7 @@ SYNOPSIS 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-s <strategy> | --strategy=<strategy>] [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges] - [--onto <newbase>] <upstream> [<branch>] + [-f | --first-parent] [--onto <newbase>] <upstream> [<branch>] 'git-rebase' --continue | --skip | --abort DESCRIPTION @@ -247,6 +247,11 @@ OPTIONS Instead of ignoring merges, try to recreate them. This option only works in interactive mode. +-f, \--first-parent:: + This option implies the option --preserve-merges, but instead of + showing all commits from the merged branches show only the + commits and merges following the first parent of each commit. + include::merge-strategies.txt[] NOTES diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index d3327a8..ea67942 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -10,8 +10,8 @@ # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 -USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose] - [--onto <branch>] <upstream> [<branch>])' +USAGE='(--continue | --abort | --skip | [--preserve-merges] [--first-parent] + [--verbose] [--onto <branch>] <upstream> [<branch>])' OPTIONS_SPEC= . git-sh-setup @@ -565,6 +565,10 @@ do ...
The intent of the tag command is to (re)set tags for commits in the TODO
list. This way it's possible to rebase a commit together with its tag.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 7 +++++++
t/t3404-rebase-interactive.sh | 13 +++++++++++++
2 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index ea67942..c601655 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -323,6 +323,12 @@ do_next () {
mark_action_done
output git reset --hard $tmp
;;
+ tag|t)
+ comment_for_reflog tag
+
+ mark_action_done
+ output git tag -f "$sha1"
+ ;;
*)
warn "Unknown command: $command $sha1 $rest"
die_with_patch $sha1 "Please fix this in the file $TODO."
@@ -655,6 +661,7 @@ do
# reset commit = reset HEAD to the commit
# merge commit-M commit-P ... = redo merge commit-M with the
# current HEAD and the parents commit-P
+# tag = reset tag to the current HEAD
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 8da7829..9901555 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -91,6 +91,9 @@ for line in $FAKE_LINES; do
merge*)
echo "merge ${line#merge}" | tr / ' '
echo "merge ${line#merge}" | tr / ' ' >> "$1";;
+ tag*)
+ echo "tag ${line#tag}"
+ echo "tag ${line#tag}" >> "$1";;
*)
sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp
sed -n "${line}{s/^pick/$action/; p;}" < "$1".tmp >> "$1"
@@ -306,6 +309,16 @@ test_expect_success 'interactive --first-parent gives a linear list' '
test "$head" = "$(git rev-parse HEAD)"
'
+test_expect_success 'tag sets tags' '
+ head=$(git rev-parse HEAD) &&
+ FAKE_LINES="1 2 3 4 5 tagbb-tag1 6 7 8 9 10 11 12 13 14 15 \
+ tagbb-tag2 16 tagbb-tag3a tagbb-tag3b 17 18 19 ...With this new option tags set on commits, they are part of a rebase, are reset to the rebased commits. This way the tags on a branch are kept across rebases. Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de> --- Documentation/git-rebase.txt | 6 ++++- git-rebase--interactive.sh | 42 ++++++++++++++++++++++++++++++++++++++-- t/t3404-rebase-interactive.sh | 10 +++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 9ebbb90..cc4e94f 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -11,7 +11,8 @@ SYNOPSIS 'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-s <strategy> | --strategy=<strategy>] [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges] - [-f | --first-parent] [--onto <newbase>] <upstream> [<branch>] + [-f | --first-parent] [-t | --preserve-tags] + [--onto <newbase>] <upstream> [<branch>] 'git-rebase' --continue | --skip | --abort DESCRIPTION @@ -252,6 +253,9 @@ OPTIONS showing all commits from the merged branches show only the commits and merges following the first parent of each commit. +-t, \--preserve-tags:: + If one of the commits has a tag, reset it to the new commit object. + include::merge-strategies.txt[] NOTES diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index c601655..e874c31 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -11,7 +11,7 @@ # http://article.gmane.org/gmane.comp.version-control.git/22407 USAGE='(--continue | --abort | --skip | [--preserve-merges] [--first-parent] - [--verbose] [--onto <branch>] <upstream> [<branch>])' + [--preserve-tags] [--verbose] [--onto <branch>] <upstream> [<branch>])' OPTIONS_SPEC= . git-sh-setup @@ -397,9 +397,31 @@ insert_value_at_key_into_list () { create_extended_todo_list () { ( + if test t = "${PRESERVE_TAGS:-}" + then + tag_list=$(git show-ref --abbrev=7 --tags | ...
In practice nobody would "run" pack-refs during the rebase session, but I
have to wonder if it can be triggered to run as part of automated gc or
something, in which case this loop does not work as intended. It needs to
What was the conclusion of the mark-syntax discussion?
While I know the bang in ":[!negated]" is POSIX, I wonder if everybody's
shell we care about groks it.
Could people run this with the shell they care about being supported
(Solaris /bin/sh does not count) try this and yell loudly if you get
"matches" please? I know bash and dash are Ok, but I do not have easy
access to various flabours of BSDs (OSX included).
case ":/foo" in
:[!/]*) echo matches ;;
*) echo does not ;;
esac
--
Eh, sorry, I was commenting on a stale one. Disregard this part please. But the "$GIT_DIR/$mark_prefix/*" comment still stands. I've applied the series as is to 'next' so let's fix them up in-tree as needed. --
Hi, On Mon, 21 Apr 2008, Junio C Hamano wrote: > J
Hi Junio,
What do you think about this version:
cleanup_before_quit () {
rm -rf "$DOTEST" &&
for ref in $(git for-each-ref --format=3D'%(refname)' ${mark_prefix%/})
do
git update-ref -d "$ref" "$ref" || return 1
done
Use the same as fast-import and fix fast-import. :)
I've posted a new version
<1208169584-15931-1-git-send-email-joerg@alea.gnuu.de>
Bye, J=F6rg.
--=20
Nutze die Talente, die du hast. Die W=E4lder w=E4ren sehr still,
wenn nur die begabtesten V=F6gel s=E4ngen. (Henry van Dyke)
Yeah, except you would want to dqquote "${mark_prefix%/}" part.
Also this being a "clean-up" phase, I wonder if we want to stop at the
first error (e.g. should unremovable "$DOTEST" leave marks behind? should
unremovable one mark leave other marks that happen to sort after it
behind?).
--
Hi Junio, I think it should be the other way: unremovable marks should leave the DOTEST behind. This way a rebase should refuse to start a new session and stumble accross the old marks and it's possible to run git rebase --abort after manually removing the marks. Bye, J=F6rg. --=20 Damit das M=F6gliche entsteht, mu=DF immer wieder das Unm=F6gliche versucht werden. (Hermann Hesse)
This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.
The decision to use references for the marks and not files like done with
the rewritten commits for preserve merges was made to ensure no commit
objects get lost if prune is started while (a long term) rebase is
running. This also unifies the checking of the validity of marks and
references by using rev-parse for it.
The format of the marks is as close as possible to the format of the
marks used by fast-export and fast-import, i.e. :001 == :1 and “:12a” is
an invalid mark. It differs from the format of fast-import in that point
that the colon is not required after the mark command, i.e. “mark 12” is
the same as “mark :12”. This should ease the writing of commads.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 31 +++++++++++++++++++++++++++++++
t/t3404-rebase-interactive.sh | 17 +++++++++++++++++
2 files changed, 48 insertions(+), 0 deletions(-)
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 531ee94..c0abc01 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,6 +35,8 @@ mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'"
export GIT_CHERRY_PICK_HELP
+mark_prefix=refs/rebase-marks/
+
warn () {
echo "$*" >&2
}
@@ -105,6 +107,10 @@ die_with_patch () {
}
cleanup_before_quit () {
+ for ref in $(git for-each-ref --format='%(refname)' "${mark_prefix%/}")
+ do
+ git update-ref -d "$ref" "$ref" || return 1
+ done
rm -rf "$DOTEST"
}
@@ -244,6 +250,15 @@ peek_next_command () {
sed -n "1s/ .*$//p" < "$TODO"
}
+mark_to_ref () {
+ if expr match "$1" "^:[0-9][0-9]*$" >/dev/null
+ then
+ echo "$mark_prefix$(printf %d ${1#:})"
+ else
+ echo "$1"
+ fi
+}
+
do_next () {
rm -f "$DOTEST"/message "$DOTEST"/author-script \
"$DOTEST"/amend || exit
@@ -321,6 +336,17 ...Thanks; as I already queued the series to 'next', I took the liberty of applying the interdiff as a separate fix-up/improvement commit to the tip of the topic. --
Hallo Junio, That's alright. Bye, J=C3=B6rg. --=20 Je planm=C3=A4=C3=9Figer ein Mensch vorgeht, desto st=C3=A4rker mag ihn der Zufall treffen. Erich Krunau =E2=80=9ADie Physiker=E2=80=98
This new command can be used to set symbolic marks for an commit while
doing a rebase. This symbolic name can later be used for merges or
resets.
The decision to use references for the marks and not files like done with
the rewritten commits for preserve merges was made to ensure no commit
objects get lost if prune is started while (a long term) rebase is
running. This also unifies the checking of the validity of marks and
references by using rev-parse for it.
The format of the marks is as close as possible to the format of the
marks used by fast-export and fast-import, i.e. :001 == :1 and
“:12a” == :12. It differs from the format of fast-import in that point
that it requires a digit after the colon, i.e. “:abc” != :0 and “:-12”
and “:+12” aren't allowed.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
git-rebase--interactive.sh | 35 ++++++++++++++++++++++++++++++++++-
t/t3404-rebase-interactive.sh | 17 +++++++++++++++++
2 files changed, 51 insertions(+), 1 deletions(-)
The difference to the v2 patch is the definition of the mark as
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 531ee94..05d04da 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -35,6 +35,8 @@ mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'"
export GIT_CHERRY_PICK_HELP
+mark_prefix=refs/rebase-marks/
+
warn () {
echo "$*" >&2
}
@@ -105,7 +107,13 @@ die_with_patch () {
}
cleanup_before_quit () {
- rm -rf "$DOTEST"
+ rm -rf "$DOTEST" &&
+ for ref in "$GIT_DIR/$mark_prefix"*
+ do
+ test "$ref" = "$GIT_DIR/$mark_prefix*" && continue
+ git update-ref -d "${ref#$GIT_DIR/}" "${ref#$GIT_DIR/}" || \
+ return 1
+ done
}
die_abort () {
@@ -244,6 +252,17 @@ peek_next_command () {
sed -n "1s/ .*$//p" < "$TODO"
}
+mark_to_ref () {
+ case "$1" in
+ :[0-9]*)
+ echo "$mark_prefix$(printf %d ${1#:} 2>/dev/null)"
+ ;;
+ *)
+ echo "$1"
+ ;;
+ esac
+}
+
do_next () ...Uh, that's a bug in fast-import. ":4abc" is _not_ a mark if you read the language specification. Only ":4" is a mark. So we are accepting crap and reading it in odd ways. Not good. -- Shawn. --
Hallo Shawn,
What about this:
diff --git a/fast-import.c b/fast-import.c
index 73e5439..f60e4ab 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1690,10 +1690,31 @@ static void skip_optional_lf(void)
ungetc(term_char, stdin);
}
=20
+static inline int parse_mark(const const char *str, uintmax_t* mark,
+ char **after_mark)
+{
+ if (!str || str[0] !=3D ':' || !isdigit(str[1]))
+ return 1;
+
+ char *am;
+ const uintmax_t m =3D strtoumax(&str[1], &am, 10);
+ if (errno =3D=3D 0) {
+ *mark =3D m;
+ *after_mark =3D am;
+ return 0;
+ }
+ return 1;
+}
+
static void cmd_mark(void)
{
- if (!prefixcmp(command_buf.buf, "mark :")) {
- next_mark =3D strtoumax(command_buf.buf + 6, NULL, 10);
+ uintmax_t mark =3D 0;
+ char *after_mark =3D NULL;
+
+ if (!prefixcmp(command_buf.buf, "mark ") &&
+ parse_mark(&command_buf.buf[5], &mark, &after_mark) &&
+ *after_mark =3D=3D '\0') {
+ next_mark =3D mark;
read_next_command();
}
else
@@ -1878,7 +1899,10 @@ static void file_change_m(struct branch *b)
=20
if (*p =3D=3D ':') {
char *x;
- oe =3D find_mark(strtoumax(p + 1, &x, 10));
+ uintmax_t m;
+ if (parse_mark(p, &m, &x))
+ die("Invalid mark: %s", p);
+ oe =3D find_mark(m);
hashcpy(sha1, oe->sha1);
p =3D x;
} else if (!prefixcmp(p, "inline")) {
@@ -2045,7 +2069,11 @@ static int cmd_from(struct branch *b)
hashcpy(b->branch_tree.versions[0].sha1, t);
hashcpy(b->branch_tree.versions[1].sha1, t);
} else if (*from =3D=3D ':') {
- uintmax_t idnum =3D strtoumax(from + 1, NULL, 10);
+ char *after_mark;
+ uintmax_t idnum;
+ if (parse_mark(from, &idnum, &after_mark) ||
+ *after_mark !=3D '\0')
+ die("Not a valid mark: %s", from);
struct object_entry *oe =3D find_mark(idnum);
if (oe->type !=3D OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2080,7 +2108,11 @@ static struct hash_list *cmd_merge(unsigned int *cou=
nt)
if (s)
hashcpy(n->sha1, s->sha1);
else if (*from =3D=3D ':') ...Although we conform to mostly C99 style, variables should be Hmm. Shouldn't this be ! parse_mark given that it returns 0 on success and 1 on failure? -- Shawn. --
Hi Shawn,
Yes, you're right. I've checked some other functions and found this
behaviour. Can I use a different behabiour, i.e. return 0 on failure and
!0 on success?
Bye, J=C3=B6rg.
--=20
=E2=80=9EWer im Usenet gelesen werden will, sollte leserorientiert schreibe=
n. Wer nur
f=C3=BCr sich schreiben will, dem ist mit einem Tagebuch vielleicht besser
geholfen. Gelesen zu werden ist kein Recht, sondern ein Privileg.=E2=80=9C
Thore Tams in <90tfv8$49b$1@keks.kruemel.dyndns.org>
Yea, inline is fine. We use "static inline" often in Git when it I wasn't objected to the return values as written, but more to the fact that it seemed like a logic error to me. We use both patterns in Git. Perhaps the best example to follow is get_sha1_hex(); it returns -1 on error and 0 on success. So a common pattern is "!get_sha1_hex()" to ensure a successful conversion of a hex string to an unsigned char array. -- Shawn. --
Hallo Shawn, Thanks for this explanation. This was what I was looking for. Another question: Is :0 a valid mark? In import_marks() is a check for !mark, but I haven't seen it anywhere else. Bye, J=C3=B6rg. --=20 Du hast keine Chance =E2=80=93 also nutze sie.
No, in fast-import ":0" is _not_ a valid mark. We burn the first entry in the marks table (always leaving it empty) as then we can use the idiom "!mark" to say "no mark was requested/given" and "mark" to say "mark was requested/given". Hence we do not need an extra flag to tell us either way. Given that a mark is just a pointer, and that extra flag would likely have been a global "static int have_mark" or some such it works out to be about the same amount of memory - 4 or 8 bytes. No big deal, and the code is probably easier to follow as a result. -- Shawn. --
The current implementation of mark parsing doesn't care for trailing
garbage like in :12a and doesn't check for unsigned numbers, i.e. it
accepts :-12 as a valid mark.
This patch enforces a number follows the colon and there comes nothing
after the bignum.
Signed-off-by: Jörg Sommer <joerg@alea.gnuu.de>
---
fast-import.c | 49 ++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 38 insertions(+), 11 deletions(-)
Then I propose the following patch.
diff --git a/fast-import.c b/fast-import.c
index 73e5439..0c71da8 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1690,12 +1690,31 @@ static void skip_optional_lf(void)
ungetc(term_char, stdin);
}
+static inline uintmax_t parse_mark(const const char *str, char **after_mark)
+{
+ char *am;
+ uintmax_t m;
+
+ if (!str || str[0] != ':' || !isdigit(str[1]))
+ return 0;
+
+ m = strtoumax(&str[1], &am, 10);
+ if (m != UINTMAX_MAX || errno == 0) {
+ *after_mark = am;
+ return m;
+ }
+ return 0;
+}
+
static void cmd_mark(void)
{
- if (!prefixcmp(command_buf.buf, "mark :")) {
- next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
+ uintmax_t mark;
+ char *after_mark = NULL;
+
+ if (!prefixcmp(command_buf.buf, "mark ") &&
+ (next_mark = parse_mark(&command_buf.buf[5], &after_mark)) &&
+ *after_mark == '\0')
read_next_command();
- }
else
next_mark = 0;
}
@@ -1877,8 +1896,8 @@ static void file_change_m(struct branch *b)
}
if (*p == ':') {
- char *x;
- oe = find_mark(strtoumax(p + 1, &x, 10));
+ char *x = NULL;
+ oe = find_mark(parse_mark(p, &x));
hashcpy(sha1, oe->sha1);
p = x;
} else if (!prefixcmp(p, "inline")) {
@@ -2045,7 +2064,10 @@ static int cmd_from(struct branch *b)
hashcpy(b->branch_tree.versions[0].sha1, t);
hashcpy(b->branch_tree.versions[1].sha1, t);
} else if (*from == ':') {
- uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+ char *after_mark;
+ uintmax_t idnum = parse_mark(from, &after_mark);
+ if (!idnum || ...I'd admit that this was taken from my "You could do this" patch, and I am inclined to think that the users would probably want this behaviour of dropping the default merge summary when giving their own message with -m, but I am not absolutely convinced that doing this unconditionally is the right thing to do (iow, some people might have relied on the current behaviour). --
Hello Junio,
What about a new option -M? But I doubt someone expects this behaviour
because the manpage says:
The second syntax (<msg> HEAD <remote>) is supported for historical
reasons. Do not use it from the command line or in new scripts. It is
the same as git merge -m <msg> <remote>.
^^^^ ^^
Currently, it's not the same, but someone might expect it.
Bye, J=F6rg.
--=20
Die Erde ist das einzigste Irrenhaus, das von seinen eigenen Insassen
verwaltet wird.
(U. Schmidt)
Yeah, the manpage is loosely written and does not reflect the reality. I am _VERY_ tempted to apply your change as-is and see if anybody screams. Because git is designed to be very scriptable, people who want the current behaviour can still call fmt-merge-msg themselves. --
Hi Junio,
I would like to extend this example:
o---o---o---o---o---Z
/
X---Y---A-------B
\ \
C---D---N---M---E
/
merge V
This way we can also merge more than two branches. Your idea sounds good.
Bye, J=C3=B6rg.
--=20
Nichts ist so langweilig, wie die Wiederholung seinerselbst.
(Marcel Reich=E2=80=90Ranicki)
I'll extend this topic a bit for the last time, but first a word of
caution. What I am going to draw is probably not what the current -p
implementation does. They illustrate what I think should happen.
Again, starting from this topology:
o---o---o---o---o---Z
/
X---Y---A---B
\ \
C---D---M---E
and the goal is to rebase your development leading to E on top of the
updated mainline Z.
The earlier example was when you want to end up with this topology:
o---o---o---o---o---Z---Y'--A'--B'
/ \ \
X---Y---A---B C'--D'--M'--E'
\ \
C---D---M---E
In this case, "pick M" cannot be "merge B after pick D". It needs to
merge in the rewritten B (which is B').
But if you want to end up with this topology:
o---o---o---o---o---Z---Y'--A'--B'
/ \
X---Y---A---B M'--E'
\ \ /
C---D---M---E /
\ /
`-------------------'
redoing the merge from D when reproducing M' is the right thing to do.
Unfortunately, you cannot express that you would want to rewrite only the
Y--A--B--M--E ancestry from the command line. We would need a syntax to
do this cleanly first if we want to pursue this.
The "first parent" hack can be used in this case (--first-parent X..E),
but it will probably meet with the same resistance at the philosophical
level (i.e. "merge parents are equal") as the --first-parent option was
criticised for. But other than that, a sequence to pick Y A B M E in this
order can be presented in the todo list to be edited, and swapping A and M
(for example) should result in this:
o---o---o---o---o---Z---Y'
/ \
X---Y---A---B M'--A'--B'
\ \ /
...Hallo Johannes, Thanks for answering my question. You are really cooperatively. Bye, J=F6rg. --=20 IRC: Der [Prof. Andreas Pfitzmann, TU Dresden] hat gerade vorgeschlagen, sie sollen doch statt Trojanern die elektromagnetische Abstrahlung nutzen. Das sei nicht massenf=E4hig, ginge ohne Eingriff ins System, sei zielgerichte= t, und, der Hammer, das funktioniere ja bei Wahlcomputern schon sehr gut.
Hi, On Sun, 23 Mar 2008, J
