Merge branch 'jn/update-contrib-example-merge'

* jn/update-contrib-example-merge: (24 commits)
  merge script: learn --[no-]rerere-autoupdate
  merge script: notice @{-1} shorthand
  merge script: handle --no-ff --no-commit correctly
  merge script: --ff-only to disallow true merge
  merge script: handle many-way octopus
  merge script: handle -m --log correctly
  merge script: forbid merge -s index
  merge script: allow custom strategies
  merge script: merge -X<option>
  merge script: improve log message subject
  merge script: refuse to merge during merge
  merge script: tweak unmerged files message to match builtin
  merge script: --squash, --ff from unborn branch are errors
  fmt-merge-msg -m to override merge title
  merge-base --independent to print reduced parent list in a merge
  merge-base --octopus to mimic show-branch --merge-base
  Documentation: add a SEE ALSO section for merge-base
  t6200 (fmt-merge-msg): style nitpicks
  t6010 (merge-base): modernize style
  t7600 (merge): test merge from branch yet to be born
  ...
This commit is contained in:
Junio C Hamano 2010-09-03 09:43:42 -07:00
commit 2b916ffa18
9 changed files with 687 additions and 548 deletions

View File

@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git fmt-merge-msg' [--log | --no-log] <$GIT_DIR/FETCH_HEAD 'git fmt-merge-msg' [-m <message>] [--log | --no-log] <$GIT_DIR/FETCH_HEAD
'git fmt-merge-msg' [--log | --no-log] -F <file> 'git fmt-merge-msg' [-m <message>] [--log | --no-log] -F <file>
DESCRIPTION DESCRIPTION
----------- -----------
@ -38,6 +38,11 @@ OPTIONS
Synonyms to --log and --no-log; these are deprecated and will be Synonyms to --log and --no-log; these are deprecated and will be
removed in the future. removed in the future.
-m <message>::
--message <message>::
Use <message> instead of the branch names for the first line
of the log message. For use with `--log`.
-F <file>:: -F <file>::
--file <file>:: --file <file>::
Take the list of merged objects from <file> instead of Take the list of merged objects from <file> instead of

View File

@ -8,7 +8,9 @@ git-merge-base - Find as good common ancestors as possible for a merge
SYNOPSIS SYNOPSIS
-------- --------
'git merge-base' [-a|--all] <commit> <commit>... [verse]
'git merge-base' [-a|--all] [--octopus] <commit> <commit>...
'git merge-base' --independent <commit>...
DESCRIPTION DESCRIPTION
----------- -----------
@ -20,12 +22,12 @@ that does not have any better common ancestor is a 'best common
ancestor', i.e. a 'merge base'. Note that there can be more than one ancestor', i.e. a 'merge base'. Note that there can be more than one
merge base for a pair of commits. merge base for a pair of commits.
Among the two commits to compute the merge base from, one is specified by Unless `--octopus` is given, among the two commits to compute the merge
the first commit argument on the command line; the other commit is a base from, one is specified by the first commit argument on the command
(possibly hypothetical) commit that is a merge across all the remaining line; the other commit is a (possibly hypothetical) commit that is a merge
commits on the command line. As the most common special case, specifying only across all the remaining commits on the command line. As the most common
two commits on the command line means computing the merge base between special case, specifying only two commits on the command line means
the given two commits. computing the merge base between the given two commits.
As a consequence, the 'merge base' is not necessarily contained in each of the As a consequence, the 'merge base' is not necessarily contained in each of the
commit arguments if more than two commits are specified. This is different commit arguments if more than two commits are specified. This is different
@ -37,6 +39,18 @@ OPTIONS
--all:: --all::
Output all merge bases for the commits, instead of just one. Output all merge bases for the commits, instead of just one.
--octopus::
Compute the best common ancestors of all supplied commits,
in preparation for an n-way merge. This mimics the behavior
of 'git show-branch --merge-base'.
--independent::
Instead of printing merge bases, print a minimal subset of
the supplied commits with the same ancestors. In other words,
among the commits given, list those which cannot be reached
from any other. This mimics the behavior of 'git show-branch
--independent'.
DISCUSSION DISCUSSION
---------- ----------
@ -96,6 +110,12 @@ Documentation
-------------- --------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
See also
--------
linkgit:git-rev-list[1],
linkgit:git-show-branch[1],
linkgit:git-merge[1]
GIT GIT
--- ---
Part of the linkgit:git[1] suite Part of the linkgit:git[1] suite

View File

@ -7,7 +7,7 @@
#include "string-list.h" #include "string-list.h"
static const char * const fmt_merge_msg_usage[] = { static const char * const fmt_merge_msg_usage[] = {
"git fmt-merge-msg [--log|--no-log] [--file <file>]", "git fmt-merge-msg [-m <message>] [--log|--no-log] [--file <file>]",
NULL NULL
}; };
@ -319,11 +319,14 @@ int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
{ {
const char *inpath = NULL; const char *inpath = NULL;
const char *message = NULL;
struct option options[] = { struct option options[] = {
OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"), OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"),
{ OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL, { OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL,
"alias for --log (deprecated)", "alias for --log (deprecated)",
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
OPT_STRING('m', "message", &message, "text",
"use <text> as start of message"),
OPT_FILENAME('F', "file", &inpath, "file to read from"), OPT_FILENAME('F', "file", &inpath, "file to read from"),
OPT_END() OPT_END()
}; };
@ -337,6 +340,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
0); 0);
if (argc > 0) if (argc > 0)
usage_with_options(fmt_merge_msg_usage, options); usage_with_options(fmt_merge_msg_usage, options);
if (message && !merge_summary) {
char nl = '\n';
write_in_full(STDOUT_FILENO, message, strlen(message));
write_in_full(STDOUT_FILENO, &nl, 1);
return 0;
}
if (inpath && strcmp(inpath, "-")) { if (inpath && strcmp(inpath, "-")) {
in = fopen(inpath, "r"); in = fopen(inpath, "r");
@ -346,7 +355,12 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
if (strbuf_read(&input, fileno(in), 0) < 0) if (strbuf_read(&input, fileno(in), 0) < 0)
die_errno("could not read input file"); die_errno("could not read input file");
ret = fmt_merge_msg(merge_summary, &input, &output); if (message) {
strbuf_addstr(&output, message);
ret = fmt_merge_msg_shortlog(&input, &output);
} else {
ret = fmt_merge_msg(merge_summary, &input, &output);
}
if (ret) if (ret)
return ret; return ret;
write_in_full(STDOUT_FILENO, output.buf, output.len); write_in_full(STDOUT_FILENO, output.buf, output.len);

View File

@ -23,7 +23,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
} }
static const char * const merge_base_usage[] = { static const char * const merge_base_usage[] = {
"git merge-base [-a|--all] <commit> <commit>...", "git merge-base [-a|--all] [--octopus] <commit> <commit>...",
"git merge-base --independent <commit>...",
NULL NULL
}; };
@ -41,21 +42,58 @@ static struct commit *get_commit_reference(const char *arg)
return r; return r;
} }
static int handle_octopus(int count, const char **args, int reduce, int show_all)
{
struct commit_list *revs = NULL;
struct commit_list *result;
int i;
if (reduce)
show_all = 1;
for (i = count - 1; i >= 0; i--)
commit_list_insert(get_commit_reference(args[i]), &revs);
result = reduce ? reduce_heads(revs) : get_octopus_merge_bases(revs);
if (!result)
return 1;
while (result) {
printf("%s\n", sha1_to_hex(result->item->object.sha1));
if (!show_all)
return 0;
result = result->next;
}
return 0;
}
int cmd_merge_base(int argc, const char **argv, const char *prefix) int cmd_merge_base(int argc, const char **argv, const char *prefix)
{ {
struct commit **rev; struct commit **rev;
int rev_nr = 0; int rev_nr = 0;
int show_all = 0; int show_all = 0;
int octopus = 0;
int reduce = 0;
struct option options[] = { struct option options[] = {
OPT_BOOLEAN('a', "all", &show_all, "outputs all common ancestors"), OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"),
OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"),
OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"),
OPT_END() OPT_END()
}; };
git_config(git_default_config, NULL); git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
if (argc < 2) if (!octopus && !reduce && argc < 2)
usage_with_options(merge_base_usage, options); usage_with_options(merge_base_usage, options);
if (reduce && (show_all || octopus))
die("--independent cannot be used with other options");
if (octopus || reduce)
return handle_octopus(argc, argv, reduce, show_all);
rev = xmalloc(argc * sizeof(*rev)); rev = xmalloc(argc * sizeof(*rev));
while (argc-- > 0) while (argc-- > 0)
rev[rev_nr++] = get_commit_reference(*argv++); rev[rev_nr++] = get_commit_reference(*argv++);

View File

@ -438,7 +438,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, "refs/heads/");
strbuf_addstr(&truname, remote); strbuf_addstr(&truname, remote);
strbuf_setlen(&truname, truname.len - len); strbuf_setlen(&truname, truname.len - len);
if (resolve_ref(truname.buf, buf_sha, 0, NULL)) { if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
strbuf_addf(msg, strbuf_addf(msg,
"%s\t\tbranch '%s'%s of .\n", "%s\t\tbranch '%s'%s of .\n",
sha1_to_hex(remote_head->sha1), sha1_to_hex(remote_head->sha1),

View File

@ -15,7 +15,10 @@ log add list of one-line log to merge commit message
squash create a single commit instead of doing a merge squash create a single commit instead of doing a merge
commit perform a commit if the merge succeeds (default) commit perform a commit if the merge succeeds (default)
ff allow fast-forward (default) ff allow fast-forward (default)
ff-only abort if fast-forward is not possible
rerere-autoupdate update index with any reused conflict resolution
s,strategy= merge strategy to use s,strategy= merge strategy to use
X= option for selected merge strategy
m,message= message to be used for the merge commit (if any) m,message= message to be used for the merge commit (if any)
" "
@ -25,26 +28,32 @@ require_work_tree
cd_to_toplevel cd_to_toplevel
test -z "$(git ls-files -u)" || test -z "$(git ls-files -u)" ||
die "You are in the middle of a conflicted merge." die "Merge is not possible because you have unmerged files."
! test -e "$GIT_DIR/MERGE_HEAD" ||
die 'You have not concluded your merge (MERGE_HEAD exists).'
LF=' LF='
' '
all_strategies='recur recursive octopus resolve stupid ours subtree' all_strategies='recur recursive octopus resolve stupid ours subtree'
all_strategies="$all_strategies recursive-ours recursive-theirs" all_strategies="$all_strategies recursive-ours recursive-theirs"
not_strategies='base file index tree'
default_twohead_strategies='recursive' default_twohead_strategies='recursive'
default_octopus_strategies='octopus' default_octopus_strategies='octopus'
no_fast_forward_strategies='subtree ours' no_fast_forward_strategies='subtree ours'
no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs' no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
use_strategies= use_strategies=
xopt=
allow_fast_forward=t allow_fast_forward=t
fast_forward_only=
allow_trivial_merge=t allow_trivial_merge=t
squash= no_commit= log_arg= squash= no_commit= log_arg= rr_arg=
dropsave() { dropsave() {
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
"$GIT_DIR/MERGE_STASH" || exit 1 "$GIT_DIR/MERGE_STASH" "$GIT_DIR/MERGE_MODE" || exit 1
} }
savestate() { savestate() {
@ -131,21 +140,34 @@ finish () {
merge_name () { merge_name () {
remote="$1" remote="$1"
rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null) if truname=$(expr "$remote" : '\(.*\)~[0-9]*$') &&
if test "$rh" = "$bh"
then
echo "$rh branch '$remote' of ."
elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
git show-ref -q --verify "refs/heads/$truname" 2>/dev/null git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
then then
echo "$rh branch '$truname' (early part) of ." echo "$rh branch '$truname' (early part) of ."
elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD" return
fi
if found_ref=$(git rev-parse --symbolic-full-name --verify \
"$remote" 2>/dev/null)
then
expanded=$(git check-ref-format --branch "$remote") ||
exit
if test "${found_ref#refs/heads/}" != "$found_ref"
then
echo "$rh branch '$expanded' of ."
return
elif test "${found_ref#refs/remotes/}" != "$found_ref"
then
echo "$rh remote branch '$expanded' of ."
return
fi
fi
if test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
then then
sed -e 's/ not-for-merge / /' -e 1q \ sed -e 's/ not-for-merge / /' -e 1q \
"$GIT_DIR/FETCH_HEAD" "$GIT_DIR/FETCH_HEAD"
else return
echo "$rh commit '$remote'"
fi fi
echo "$rh commit '$remote'"
} }
parse_config () { parse_config () {
@ -172,16 +194,36 @@ parse_config () {
--no-ff) --no-ff)
test "$squash" != t || test "$squash" != t ||
die "You cannot combine --squash with --no-ff." die "You cannot combine --squash with --no-ff."
test "$fast_forward_only" != t ||
die "You cannot combine --ff-only with --no-ff."
allow_fast_forward=f ;; allow_fast_forward=f ;;
--ff-only)
test "$allow_fast_forward" != f ||
die "You cannot combine --ff-only with --no-ff."
fast_forward_only=t ;;
--rerere-autoupdate|--no-rerere-autoupdate)
rr_arg=$1 ;;
-s|--strategy) -s|--strategy)
shift shift
case " $all_strategies " in case " $all_strategies " in
*" $1 "*) *" $1 "*)
use_strategies="$use_strategies$1 " ;; use_strategies="$use_strategies$1 "
;;
*) *)
die "available strategies are: $all_strategies" ;; case " $not_strategies " in
*" $1 "*)
false
esac &&
type "git-merge-$1" >/dev/null 2>&1 ||
die "available strategies are: $all_strategies"
use_strategies="$use_strategies$1 "
;;
esac esac
;; ;;
-X)
shift
xopt="${xopt:+$xopt }$(git rev-parse --sq-quote "--$1")"
;;
-m|--message) -m|--message)
shift shift
merge_msg="$1" merge_msg="$1"
@ -245,6 +287,10 @@ then
exit 1 exit 1
fi fi
test "$squash" != t ||
die "Squash commit into empty head not supported yet"
test "$allow_fast_forward" = t ||
die "Non-fast-forward into an empty head does not make sense"
rh=$(git rev-parse --verify "$1^0") || rh=$(git rev-parse --verify "$1^0") ||
die "$1 - not something we can merge" die "$1 - not something we can merge"
@ -261,12 +307,18 @@ else
# the given message. If remote is invalid we will die # the given message. If remote is invalid we will die
# later in the common codepath so we discard the error # later in the common codepath so we discard the error
# in this loop. # in this loop.
merge_name=$(for remote merge_msg="$(
for remote
do do
merge_name "$remote" merge_name "$remote"
done | git fmt-merge-msg $log_arg done |
) if test "$have_message" = t
merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name" then
git fmt-merge-msg -m "$merge_msg" $log_arg
else
git fmt-merge-msg $log_arg
fi
)"
fi fi
head=$(git rev-parse --verify "$head_arg"^0) || usage head=$(git rev-parse --verify "$head_arg"^0) || usage
@ -335,7 +387,7 @@ case "$#" in
common=$(git merge-base --all $head "$@") common=$(git merge-base --all $head "$@")
;; ;;
*) *)
common=$(git show-branch --merge-base $head "$@") common=$(git merge-base --all --octopus $head "$@")
;; ;;
esac esac
echo "$head" >"$GIT_DIR/ORIG_HEAD" echo "$head" >"$GIT_DIR/ORIG_HEAD"
@ -373,8 +425,8 @@ t,1,"$head",*)
# We are not doing octopus, not fast-forward, and have only # We are not doing octopus, not fast-forward, and have only
# one common. # one common.
git update-index --refresh 2>/dev/null git update-index --refresh 2>/dev/null
case "$allow_trivial_merge" in case "$allow_trivial_merge,$fast_forward_only" in
t) t,)
# See if it is really trivial. # See if it is really trivial.
git var GIT_COMMITTER_IDENT >/dev/null || exit git var GIT_COMMITTER_IDENT >/dev/null || exit
echo "Trying really trivial in-index merge..." echo "Trying really trivial in-index merge..."
@ -413,6 +465,11 @@ t,1,"$head",*)
;; ;;
esac esac
if test "$fast_forward_only" = t
then
die "Not possible to fast-forward, aborting."
fi
# We are going to make a new commit. # We are going to make a new commit.
git var GIT_COMMITTER_IDENT >/dev/null || exit git var GIT_COMMITTER_IDENT >/dev/null || exit
@ -451,7 +508,7 @@ do
# Remember which strategy left the state in the working tree # Remember which strategy left the state in the working tree
wt_strategy=$strategy wt_strategy=$strategy
git-merge-$strategy $common -- "$head_arg" "$@" eval 'git-merge-$strategy '"$xopt"' $common -- "$head_arg" "$@"'
exit=$? exit=$?
if test "$no_commit" = t && test "$exit" = 0 if test "$no_commit" = t && test "$exit" = 0
then then
@ -489,9 +546,9 @@ if test '' != "$result_tree"
then then
if test "$allow_fast_forward" = "t" if test "$allow_fast_forward" = "t"
then then
parents=$(git show-branch --independent "$head" "$@") parents=$(git merge-base --independent "$head" "$@")
else else
parents=$(git rev-parse "$head" "$@") parents=$(git rev-parse "$head" "$@")
fi fi
parents=$(echo "$parents" | sed -e 's/^/-p /') parents=$(echo "$parents" | sed -e 's/^/-p /')
result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
@ -533,7 +590,15 @@ else
do do
echo $remote echo $remote
done >"$GIT_DIR/MERGE_HEAD" done >"$GIT_DIR/MERGE_HEAD"
printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" ||
die "Could not write to $GIT_DIR/MERGE_MSG"
if test "$allow_fast_forward" != t
then
printf "%s" no-ff
else
:
fi >"$GIT_DIR/MERGE_MODE" ||
die "Could not write to $GIT_DIR/MERGE_MODE"
fi fi
if test "$merge_was_ok" = t if test "$merge_was_ok" = t
@ -550,6 +615,6 @@ Conflicts:
sed -e 's/^[^ ]* / /' | sed -e 's/^[^ ]* / /' |
uniq uniq
} >>"$GIT_DIR/MERGE_MSG" } >>"$GIT_DIR/MERGE_MSG"
git rerere git rerere $rr_arg
die "Automatic merge failed; fix conflicts and then commit the result." die "Automatic merge failed; fix conflicts and then commit the result."
fi fi

View File

@ -3,175 +3,231 @@
# Copyright (c) 2005 Junio C Hamano # Copyright (c) 2005 Junio C Hamano
# #
test_description='Merge base computation. test_description='Merge base and parent list computation.
' '
. ./test-lib.sh . ./test-lib.sh
T=$(git write-tree) test_expect_success 'setup' '
T=$(git write-tree) &&
M=1130000000 M=1130000000 &&
Z=+0000 Z=+0000 &&
GIT_COMMITTER_EMAIL=git@comm.iter.xz GIT_COMMITTER_EMAIL=git@comm.iter.xz &&
GIT_COMMITTER_NAME='C O Mmiter' GIT_COMMITTER_NAME="C O Mmiter" &&
GIT_AUTHOR_NAME='A U Thor' GIT_AUTHOR_NAME="A U Thor" &&
GIT_AUTHOR_EMAIL=git@au.thor.xz GIT_AUTHOR_EMAIL=git@au.thor.xz &&
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
doit() { doit() {
OFFSET=$1; shift OFFSET=$1 &&
NAME=$1; shift NAME=$2 &&
PARENTS= shift 2 &&
for P
do
PARENTS="${PARENTS}-p $P "
done
GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
commit=$(echo $NAME | git commit-tree $T $PARENTS)
echo $commit >.git/refs/tags/$NAME
echo $commit
}
# E---D---C---B---A PARENTS= &&
# \'-_ \ \ for P
# \ `---------G \ do
# \ \ PARENTS="${PARENTS}-p $P "
# F----------------H done &&
# Setup... GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z" &&
E=$(doit 5 E) GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE &&
D=$(doit 4 D $E) export GIT_COMMITTER_DATE GIT_AUTHOR_DATE &&
F=$(doit 6 F $E)
C=$(doit 3 C $D)
B=$(doit 2 B $C)
A=$(doit 1 A $B)
G=$(doit 7 G $B $E)
H=$(doit 8 H $A $F)
test_expect_success 'compute merge-base (single)' \ commit=$(echo $NAME | git commit-tree $T $PARENTS) &&
'MB=$(git merge-base G H) &&
expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
test_expect_success 'compute merge-base (all)' \ echo $commit >.git/refs/tags/$NAME &&
'MB=$(git merge-base --all G H) && echo $commit
expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"' }
'
test_expect_success 'compute merge-base with show-branch' \ test_expect_success 'set up G and H' '
'MB=$(git show-branch --merge-base G H) && # E---D---C---B---A
expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"' # \"-_ \ \
# \ `---------G \
# \ \
# F----------------H
E=$(doit 5 E) &&
D=$(doit 4 D $E) &&
F=$(doit 6 F $E) &&
C=$(doit 3 C $D) &&
B=$(doit 2 B $C) &&
A=$(doit 1 A $B) &&
G=$(doit 7 G $B $E) &&
H=$(doit 8 H $A $F)
'
# Setup for second test to demonstrate that relying on timestamps in a test_expect_success 'merge-base G H' '
# distributed SCM to provide a _consistent_ partial ordering of commits git name-rev $B >expected &&
# leads to insanity.
#
# Relative
# Structure timestamps
#
# PL PR +4 +4
# / \/ \ / \/ \
# L2 C2 R2 +3 -1 +3
# | | | | | |
# L1 C1 R1 +2 -2 +2
# | | | | | |
# L0 C0 R0 +1 -3 +1
# \ | / \ | /
# S 0
#
# The left and right chains of commits can be of any length and complexity as
# long as all of the timestamps are greater than that of S.
S=$(doit 0 S) MB=$(git merge-base G H) &&
git name-rev "$MB" >actual.single &&
C0=$(doit -3 C0 $S) MB=$(git merge-base --all G H) &&
C1=$(doit -2 C1 $C0) git name-rev "$MB" >actual.all &&
C2=$(doit -1 C2 $C1)
L0=$(doit 1 L0 $S) MB=$(git show-branch --merge-base G H) &&
L1=$(doit 2 L1 $L0) git name-rev "$MB" >actual.sb &&
L2=$(doit 3 L2 $L1)
R0=$(doit 1 R0 $S) test_cmp expected actual.single &&
R1=$(doit 2 R1 $R0) test_cmp expected actual.all &&
R2=$(doit 3 R2 $R1) test_cmp expected actual.sb
'
PL=$(doit 4 PL $L2 $C2) test_expect_success 'merge-base/show-branch --independent' '
PR=$(doit 4 PR $C2 $R2) git name-rev "$H" >expected1 &&
git name-rev "$H" "$G" >expected2 &&
test_expect_success 'compute merge-base (single)' \ parents=$(git merge-base --independent H) &&
'MB=$(git merge-base PL PR) && git name-rev $parents >actual1.mb &&
expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"' parents=$(git merge-base --independent A H G) &&
git name-rev $parents >actual2.mb &&
test_expect_success 'compute merge-base (all)' \ parents=$(git show-branch --independent H) &&
'MB=$(git merge-base --all PL PR) && git name-rev $parents >actual1.sb &&
expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"' parents=$(git show-branch --independent A H G) &&
git name-rev $parents >actual2.sb &&
# Another set to demonstrate base between one commit and a merge test_cmp expected1 actual1.mb &&
# in the documentation. test_cmp expected2 actual2.mb &&
# test_cmp expected1 actual1.sb &&
# * C (MMC) * B (MMB) * A (MMA) test_cmp expected2 actual2.sb
# * o * o * o '
# * o * o * o
# * o * o * o
# * o | _______/
# | |/
# | * 1 (MM1)
# | _______/
# |/
# * root (MMR)
test_expect_success 'unsynchronized clocks' '
# This test is to demonstrate that relying on timestamps in a distributed
# SCM to provide a _consistent_ partial ordering of commits leads to
# insanity.
#
# Relative
# Structure timestamps
#
# PL PR +4 +4
# / \/ \ / \/ \
# L2 C2 R2 +3 -1 +3
# | | | | | |
# L1 C1 R1 +2 -2 +2
# | | | | | |
# L0 C0 R0 +1 -3 +1
# \ | / \ | /
# S 0
#
# The left and right chains of commits can be of any length and complexity as
# long as all of the timestamps are greater than that of S.
S=$(doit 0 S) &&
C0=$(doit -3 C0 $S) &&
C1=$(doit -2 C1 $C0) &&
C2=$(doit -1 C2 $C1) &&
L0=$(doit 1 L0 $S) &&
L1=$(doit 2 L1 $L0) &&
L2=$(doit 3 L2 $L1) &&
R0=$(doit 1 R0 $S) &&
R1=$(doit 2 R1 $R0) &&
R2=$(doit 3 R2 $R1) &&
PL=$(doit 4 PL $L2 $C2) &&
PR=$(doit 4 PR $C2 $R2)
git name-rev $C2 >expected &&
MB=$(git merge-base PL PR) &&
git name-rev "$MB" >actual.single &&
MB=$(git merge-base --all PL PR) &&
git name-rev "$MB" >actual.all &&
test_cmp expected actual.single &&
test_cmp expected actual.all
'
test_expect_success '--independent with unsynchronized clocks' '
IB=$(doit 0 IB) &&
I1=$(doit -10 I1 $IB) &&
I2=$(doit -9 I2 $I1) &&
I3=$(doit -8 I3 $I2) &&
I4=$(doit -7 I4 $I3) &&
I5=$(doit -6 I5 $I4) &&
I6=$(doit -5 I6 $I5) &&
I7=$(doit -4 I7 $I6) &&
I8=$(doit -3 I8 $I7) &&
IH=$(doit -2 IH $I8) &&
echo $IH >expected &&
git merge-base --independent IB IH >actual &&
test_cmp expected actual
'
test_expect_success 'merge-base for octopus-step (setup)' ' test_expect_success 'merge-base for octopus-step (setup)' '
test_tick && git commit --allow-empty -m root && git tag MMR && # Another set to demonstrate base between one commit and a merge
test_tick && git commit --allow-empty -m 1 && git tag MM1 && # in the documentation.
test_tick && git commit --allow-empty -m o && #
test_tick && git commit --allow-empty -m o && # * C (MMC) * B (MMB) * A (MMA)
test_tick && git commit --allow-empty -m o && # * o * o * o
test_tick && git commit --allow-empty -m A && git tag MMA && # * o * o * o
# * o * o * o
# * o | _______/
# | |/
# | * 1 (MM1)
# | _______/
# |/
# * root (MMR)
test_commit MMR &&
test_commit MM1 &&
test_commit MM-o &&
test_commit MM-p &&
test_commit MM-q &&
test_commit MMA &&
git checkout MM1 && git checkout MM1 &&
test_tick && git commit --allow-empty -m o && test_commit MM-r &&
test_tick && git commit --allow-empty -m o && test_commit MM-s &&
test_tick && git commit --allow-empty -m o && test_commit MM-t &&
test_tick && git commit --allow-empty -m B && git tag MMB && test_commit MMB &&
git checkout MMR && git checkout MMR &&
test_tick && git commit --allow-empty -m o && test_commit MM-u &&
test_tick && git commit --allow-empty -m o && test_commit MM-v &&
test_tick && git commit --allow-empty -m o && test_commit MM-w &&
test_tick && git commit --allow-empty -m o && test_commit MM-x &&
test_tick && git commit --allow-empty -m C && git tag MMC test_commit MMC
' '
test_expect_success 'merge-base A B C' ' test_expect_success 'merge-base A B C' '
MB=$(git merge-base --all MMA MMB MMC) && git rev-parse --verify MM1 >expected &&
MM1=$(git rev-parse --verify MM1) && git rev-parse --verify MMR >expected.sb &&
test "$MM1" = "$MB"
git merge-base --all MMA MMB MMC >actual &&
git merge-base --all --octopus MMA MMB MMC >actual.common &&
git show-branch --merge-base MMA MMB MMC >actual.sb &&
test_cmp expected actual &&
test_cmp expected.sb actual.common &&
test_cmp expected.sb actual.sb
' '
test_expect_success 'merge-base A B C using show-branch' ' test_expect_success 'criss-cross merge-base for octopus-step' '
MB=$(git show-branch --merge-base MMA MMB MMC) &&
MMR=$(git rev-parse --verify MMR) &&
test "$MMR" = "$MB"
'
test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
git reset --hard MMR && git reset --hard MMR &&
test_tick && git commit --allow-empty -m 1 && git tag CC1 && test_commit CC1 &&
git reset --hard E && git reset --hard E &&
test_tick && git commit --allow-empty -m 2 && git tag CC2 && test_commit CC2 &&
test_tick && git merge -s ours CC1 && test_tick &&
test_tick && git commit --allow-empty -m o && git merge -s ours CC1 &&
test_tick && git commit --allow-empty -m B && git tag CCB && test_commit CC-o &&
test_commit CCB &&
git reset --hard CC1 && git reset --hard CC1 &&
test_tick && git merge -s ours CC2 && git merge -s ours CC2 &&
test_tick && git commit --allow-empty -m A && git tag CCA test_commit CCA &&
'
test_expect_success 'merge-base B A^^ A^^2' ' git rev-parse CC1 CC2 >expected &&
MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) && git merge-base --all CCB CCA^^ CCA^^2 >actual &&
MB1=$(git rev-parse CC1 CC2 | sort) &&
test "$MB0" = "$MB1" sort expected >expected.sorted &&
sort actual >actual.sorted &&
test_cmp expected.sorted actual.sorted
' '
test_done test_done

View File

@ -70,14 +70,13 @@ test_expect_success setup '
i=$(($i+1)) i=$(($i+1))
done && done &&
git show-branch git show-branch &&
apos="'\''"
' '
cat >expected <<\EOF test_expect_success 'message for merging local branch' '
Merge branch 'left' echo "Merge branch ${apos}left${apos}" >expected &&
EOF
test_expect_success 'merge-msg test #1' '
git checkout master && git checkout master &&
git fetch . left && git fetch . left &&
@ -86,11 +85,8 @@ test_expect_success 'merge-msg test #1' '
test_cmp expected actual test_cmp expected actual
' '
cat >expected <<EOF test_expect_success 'message for merging external branch' '
Merge branch 'left' of $(pwd) echo "Merge branch ${apos}left${apos} of $(pwd)" >expected &&
EOF
test_expect_success 'merge-msg test #2' '
git checkout master && git checkout master &&
git fetch "$(pwd)" left && git fetch "$(pwd)" left &&
@ -99,139 +95,140 @@ test_expect_success 'merge-msg test #2' '
test_cmp expected actual test_cmp expected actual
' '
cat >expected <<\EOF test_expect_success '[merge] summary/log configuration' '
Merge branch 'left' cat >expected <<-EOF &&
Merge branch ${apos}left${apos}
* left: * left:
Left #5 Left #5
Left #4 Left #4
Left #3 Left #3
Common #2 Common #2
Common #1 Common #1
EOF EOF
test_expect_success 'merge-msg test #3-1' '
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.log true && git config merge.log true &&
test_might_fail git config --unset-all merge.summary &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left && git fetch . left &&
git fmt-merge-msg <.git/FETCH_HEAD >actual && git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
test_cmp expected actual
'
test_expect_success 'merge-msg test #3-2' ' test_might_fail git config --unset-all merge.log &&
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.summary true && git config merge.summary true &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left && git fetch . left &&
git fmt-merge-msg <.git/FETCH_HEAD >actual && git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
test_cmp expected actual
test_cmp expected actual1 &&
test_cmp expected actual2
' '
cat >expected <<\EOF test_expect_success 'fmt-merge-msg -m' '
Merge branches 'left' and 'right' echo "Sync with left" >expected &&
cat >expected.log <<-EOF &&
Sync with left
* left: * ${apos}left${apos} of $(pwd):
Left #5 Left #5
Left #4 Left #4
Left #3 Left #3
Common #2 Common #2
Common #1 Common #1
EOF
* right: test_might_fail git config --unset merge.log &&
Right #5 test_might_fail git config --unset merge.summary &&
Right #4 git checkout master &&
Right #3 git fetch "$(pwd)" left &&
Common #2 git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
Common #1 git fmt-merge-msg --log -m "Sync with left" \
EOF <.git/FETCH_HEAD >actual.log &&
test_expect_success 'merge-msg test #4-1' '
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.log true && git config merge.log true &&
git fmt-merge-msg -m "Sync with left" \
<.git/FETCH_HEAD >actual.log-config &&
git fmt-merge-msg --no-log -m "Sync with left" \
<.git/FETCH_HEAD >actual.nolog &&
test_cmp expected actual &&
test_cmp expected.log actual.log &&
test_cmp expected.log actual.log-config &&
test_cmp expected actual.nolog
'
test_expect_success 'setup: expected shortlog for two branches' '
cat >expected <<-EOF
Merge branches ${apos}left${apos} and ${apos}right${apos}
* left:
Left #5
Left #4
Left #3
Common #2
Common #1
* right:
Right #5
Right #4
Right #3
Common #2
Common #1
EOF
'
test_expect_success 'shortlog for two branches' '
git config merge.log true &&
test_might_fail git config --unset-all merge.summary &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left right && git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
git fmt-merge-msg <.git/FETCH_HEAD >actual && test_might_fail git config --unset-all merge.log &&
test_cmp expected actual
'
test_expect_success 'merge-msg test #4-2' '
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.summary true && git config merge.summary true &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left right && git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
test_cmp expected actual
'
test_expect_success 'merge-msg test #5-1' '
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.log yes && git config merge.log yes &&
test_might_fail git config --unset-all merge.summary &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left right && git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
git fmt-merge-msg <.git/FETCH_HEAD >actual && test_might_fail git config --unset-all merge.log &&
test_cmp expected actual
'
test_expect_success 'merge-msg test #5-2' '
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.summary yes && git config merge.summary yes &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left right && git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual4 &&
git fmt-merge-msg <.git/FETCH_HEAD >actual && test_cmp expected actual1 &&
test_cmp expected actual test_cmp expected actual2 &&
test_cmp expected actual3 &&
test_cmp expected actual4
' '
test_expect_success 'merge-msg -F' ' test_expect_success 'merge-msg -F' '
test_might_fail git config --unset-all merge.log &&
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.summary yes && git config merge.summary yes &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left right && git fetch . left right &&
git fmt-merge-msg -F .git/FETCH_HEAD >actual && git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
test_cmp expected actual test_cmp expected actual
' '
test_expect_success 'merge-msg -F in subdirectory' ' test_expect_success 'merge-msg -F in subdirectory' '
test_might_fail git config --unset-all merge.log &&
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.summary yes && git config merge.summary yes &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . left right && git fetch . left right &&
@ -245,11 +242,11 @@ test_expect_success 'merge-msg -F in subdirectory' '
' '
test_expect_success 'merge-msg with nothing to merge' ' test_expect_success 'merge-msg with nothing to merge' '
test_might_fail git config --unset-all merge.log &&
git config --unset-all merge.log
git config --unset-all merge.summary
git config merge.summary yes && git config merge.summary yes &&
>empty &&
( (
cd remote && cd remote &&
git checkout -b unrelated && git checkout -b unrelated &&
@ -258,22 +255,20 @@ test_expect_success 'merge-msg with nothing to merge' '
git fmt-merge-msg <.git/FETCH_HEAD >../actual git fmt-merge-msg <.git/FETCH_HEAD >../actual
) && ) &&
test_cmp /dev/null actual test_cmp empty actual
' '
cat >expected <<\EOF
Merge tag 'tag-r3'
* tag 'tag-r3':
Right #3
Common #2
Common #1
EOF
test_expect_success 'merge-msg tag' ' test_expect_success 'merge-msg tag' '
cat >expected <<-EOF &&
Merge tag ${apos}tag-r3${apos}
git config --unset-all merge.log * tag ${apos}tag-r3${apos}:
git config --unset-all merge.summary Right #3
Common #2
Common #1
EOF
test_might_fail git config --unset-all merge.log &&
git config merge.summary yes && git config merge.summary yes &&
git checkout master && git checkout master &&
@ -284,26 +279,24 @@ test_expect_success 'merge-msg tag' '
test_cmp expected actual test_cmp expected actual
' '
cat >expected <<\EOF
Merge tags 'tag-r3' and 'tag-l5'
* tag 'tag-r3':
Right #3
Common #2
Common #1
* tag 'tag-l5':
Left #5
Left #4
Left #3
Common #2
Common #1
EOF
test_expect_success 'merge-msg two tags' ' test_expect_success 'merge-msg two tags' '
cat >expected <<-EOF &&
Merge tags ${apos}tag-r3${apos} and ${apos}tag-l5${apos}
git config --unset-all merge.log * tag ${apos}tag-r3${apos}:
git config --unset-all merge.summary Right #3
Common #2
Common #1
* tag ${apos}tag-l5${apos}:
Left #5
Left #4
Left #3
Common #2
Common #1
EOF
test_might_fail git config --unset-all merge.log &&
git config merge.summary yes && git config merge.summary yes &&
git checkout master && git checkout master &&
@ -314,26 +307,24 @@ test_expect_success 'merge-msg two tags' '
test_cmp expected actual test_cmp expected actual
' '
cat >expected <<\EOF
Merge branch 'left', tag 'tag-r3'
* tag 'tag-r3':
Right #3
Common #2
Common #1
* left:
Left #5
Left #4
Left #3
Common #2
Common #1
EOF
test_expect_success 'merge-msg tag and branch' ' test_expect_success 'merge-msg tag and branch' '
cat >expected <<-EOF &&
Merge branch ${apos}left${apos}, tag ${apos}tag-r3${apos}
git config --unset-all merge.log * tag ${apos}tag-r3${apos}:
git config --unset-all merge.summary Right #3
Common #2
Common #1
* left:
Left #5
Left #4
Left #3
Common #2
Common #1
EOF
test_might_fail git config --unset-all merge.log &&
git config merge.summary yes && git config merge.summary yes &&
git checkout master && git checkout master &&
@ -344,26 +335,27 @@ test_expect_success 'merge-msg tag and branch' '
test_cmp expected actual test_cmp expected actual
' '
cat >expected <<\EOF
Merge branch 'long'
* long: (35 commits)
EOF
test_expect_success 'merge-msg lots of commits' ' test_expect_success 'merge-msg lots of commits' '
{
cat <<-EOF &&
Merge branch ${apos}long${apos}
* long: (35 commits)
EOF
i=29 &&
while test $i -gt 9
do
echo " $i" &&
i=$(($i-1))
done &&
echo " ..."
} >expected &&
git checkout master && git checkout master &&
test_tick && test_tick &&
git fetch . long && git fetch . long &&
i=29 &&
while test $i -gt 9
do
echo " $i" &&
i=$(($i-1))
done >>expected &&
echo " ..." >>expected
git fmt-merge-msg <.git/FETCH_HEAD >actual && git fmt-merge-msg <.git/FETCH_HEAD >actual &&
test_cmp expected actual test_cmp expected actual
' '

View File

@ -5,189 +5,103 @@
test_description='git merge test_description='git merge
Testing basic merge operations/option parsing.' Testing basic merge operations/option parsing.
! [c0] commit 0
! [c1] commit 1
! [c2] commit 2
! [c3] commit 3
! [c4] c4
! [c5] c5
! [c6] c6
* [master] Merge commit 'c1'
--------
- [master] Merge commit 'c1'
+ * [c1] commit 1
+ [c6] c6
+ [c5] c5
++ [c4] c4
++++ [c3] commit 3
+ [c2] commit 2
+++++++* [c0] commit 0
'
. ./test-lib.sh . ./test-lib.sh
cat >file <<EOF test_expect_success 'set up test data and helpers' '
1 printf "%s\n" 1 2 3 4 5 6 7 8 9 >file &&
2 printf "%s\n" "1 X" 2 3 4 5 6 7 8 9 >file.1 &&
3 printf "%s\n" 1 2 3 4 "5 X" 6 7 8 9 >file.5 &&
4 printf "%s\n" 1 2 3 4 5 6 7 8 "9 X" >file.9 &&
5 printf "%s\n" "1 X" 2 3 4 5 6 7 8 9 >result.1 &&
6 printf "%s\n" "1 X" 2 3 4 "5 X" 6 7 8 9 >result.1-5 &&
7 printf "%s\n" "1 X" 2 3 4 "5 X" 6 7 8 "9 X" >result.1-5-9 &&
8
9
EOF
cat >file.1 <<EOF create_merge_msgs() {
1 X echo "Merge commit '\''c2'\''" >msg.1-5 &&
2 echo "Merge commit '\''c2'\''; commit '\''c3'\''" >msg.1-5-9 &&
3 {
4 echo "Squashed commit of the following:" &&
5 echo &&
6 git log --no-merges ^HEAD c1
7 } >squash.1 &&
8 {
9 echo "Squashed commit of the following:" &&
EOF echo &&
git log --no-merges ^HEAD c2
} >squash.1-5 &&
{
echo "Squashed commit of the following:" &&
echo &&
git log --no-merges ^HEAD c2 c3
} >squash.1-5-9 &&
echo >msg.nolog &&
{
echo "* commit '\''c3'\'':" &&
echo " commit 3" &&
echo
} >msg.log
} &&
cat >file.5 <<EOF verify_merge() {
1 test_cmp "$2" "$1" &&
2 git update-index --refresh &&
3 git diff --exit-code &&
4 if test -n "$3"
5 X
6
7
8
9
EOF
cat >file.9 <<EOF
1
2
3
4
5
6
7
8
9 X
EOF
cat >result.1 <<EOF
1 X
2
3
4
5
6
7
8
9
EOF
cat >result.1-5 <<EOF
1 X
2
3
4
5 X
6
7
8
9
EOF
cat >result.1-5-9 <<EOF
1 X
2
3
4
5 X
6
7
8
9 X
EOF
create_merge_msgs() {
echo "Merge commit 'c2'" >msg.1-5 &&
echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 &&
echo "Squashed commit of the following:" >squash.1 &&
echo >>squash.1 &&
git log --no-merges ^HEAD c1 >>squash.1 &&
echo "Squashed commit of the following:" >squash.1-5 &&
echo >>squash.1-5 &&
git log --no-merges ^HEAD c2 >>squash.1-5 &&
echo "Squashed commit of the following:" >squash.1-5-9 &&
echo >>squash.1-5-9 &&
git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 &&
echo > msg.nolog &&
echo "* commit 'c3':" >msg.log &&
echo " commit 3" >>msg.log &&
echo >>msg.log
}
verify_diff() {
if ! test_cmp "$1" "$2"
then
echo "$3"
false
fi
}
verify_merge() {
verify_diff "$2" "$1" "[OOPS] bad merge result" &&
if test $(git ls-files -u | wc -l) -gt 0
then
echo "[OOPS] unmerged files"
false
fi &&
if test_must_fail git diff --exit-code
then
echo "[OOPS] working tree != index"
false
fi &&
if test -n "$3"
then
git show -s --pretty=format:%s HEAD >msg.act &&
verify_diff "$3" msg.act "[OOPS] bad merge message"
fi
}
verify_head() {
if test "$1" != "$(git rev-parse HEAD)"
then
echo "[OOPS] HEAD != $1"
false
fi
}
verify_parents() {
i=1
while test $# -gt 0
do
if test "$1" != "$(git rev-parse HEAD^$i)"
then then
echo "[OOPS] HEAD^$i != $1" git show -s --pretty=format:%s HEAD >msg.act &&
return 1 test_cmp "$3" msg.act
fi fi
i=$(expr $i + 1) } &&
shift
done
}
verify_mergeheads() { verify_head() {
i=1 echo "$1" >head.expected &&
if ! test -f .git/MERGE_HEAD git rev-parse HEAD >head.actual &&
then test_cmp head.expected head.actual
echo "[OOPS] MERGE_HEAD is missing" } &&
false
fi && verify_parents() {
while test $# -gt 0 printf "%s\n" "$@" >parents.expected &&
do >parents.actual &&
head=$(head -n $i .git/MERGE_HEAD | sed -ne \$p) i=1 &&
if test "$1" != "$head" while test $i -le $#
then do
echo "[OOPS] MERGE_HEAD $i != $1" git rev-parse HEAD^$i >>parents.actual &&
i=$(expr $i + 1) ||
return 1 return 1
fi done &&
i=$(expr $i + 1) test_cmp parents.expected parents.actual
shift } &&
done
}
verify_no_mergehead() { verify_mergeheads() {
if test -f .git/MERGE_HEAD printf "%s\n" "$@" >mergehead.expected &&
then test_cmp mergehead.expected .git/MERGE_HEAD
echo "[OOPS] MERGE_HEAD exists" } &&
false
fi
}
verify_no_mergehead() {
! test -e .git/MERGE_HEAD
}
'
test_expect_success 'setup' ' test_expect_success 'setup' '
git add file && git add file &&
@ -219,7 +133,7 @@ test_expect_success 'setup' '
create_merge_msgs create_merge_msgs
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'test option parsing' ' test_expect_success 'test option parsing' '
test_must_fail git merge -$ c1 && test_must_fail git merge -$ c1 &&
@ -235,13 +149,19 @@ test_expect_success 'reject non-strategy with a git-merge-foo name' '
' '
test_expect_success 'merge c0 with c1' ' test_expect_success 'merge c0 with c1' '
echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected &&
git reset --hard c0 && git reset --hard c0 &&
git merge c1 && git merge c1 &&
verify_merge file result.1 && verify_merge file result.1 &&
verify_head "$c1" verify_head "$c1" &&
git reflog -1 >reflog.actual &&
sed "s/$_x05[0-9a-f]*/OBJID/g" reflog.actual >reflog.fuzzy &&
test_cmp reflog.expected reflog.fuzzy
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 with --ff-only' ' test_expect_success 'merge c0 with c1 with --ff-only' '
git reset --hard c0 && git reset --hard c0 &&
@ -251,7 +171,28 @@ test_expect_success 'merge c0 with c1 with --ff-only' '
verify_head "$c1" verify_head "$c1"
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge from unborn branch' '
git checkout -f master &&
test_might_fail git branch -D kid &&
echo "OBJID HEAD@{0}: initial pull" >reflog.expected &&
git checkout --orphan kid &&
test_when_finished "git checkout -f master" &&
git rm -fr . &&
test_tick &&
git merge --ff-only c1 &&
verify_merge file result.1 &&
verify_head "$c1" &&
git reflog -1 >reflog.actual &&
sed "s/$_x05[0-9a-f][0-9a-f]/OBJID/g" reflog.actual >reflog.fuzzy &&
test_cmp reflog.expected reflog.fuzzy
'
test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2' ' test_expect_success 'merge c1 with c2' '
git reset --hard c1 && git reset --hard c1 &&
@ -261,7 +202,7 @@ test_expect_success 'merge c1 with c2' '
verify_parents $c1 $c2 verify_parents $c1 $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 and c3' ' test_expect_success 'merge c1 with c2 and c3' '
git reset --hard c1 && git reset --hard c1 &&
@ -271,7 +212,7 @@ test_expect_success 'merge c1 with c2 and c3' '
verify_parents $c1 $c2 $c3 verify_parents $c1 $c2 $c3
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'failing merges with --ff-only' ' test_expect_success 'failing merges with --ff-only' '
git reset --hard c1 && git reset --hard c1 &&
@ -288,7 +229,7 @@ test_expect_success 'merge c0 with c1 (no-commit)' '
verify_head $c1 verify_head $c1
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (no-commit)' ' test_expect_success 'merge c1 with c2 (no-commit)' '
git reset --hard c1 && git reset --hard c1 &&
@ -298,7 +239,7 @@ test_expect_success 'merge c1 with c2 (no-commit)' '
verify_mergeheads $c2 verify_mergeheads $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 and c3 (no-commit)' ' test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
git reset --hard c1 && git reset --hard c1 &&
@ -308,7 +249,7 @@ test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
verify_mergeheads $c2 $c3 verify_mergeheads $c2 $c3
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 (squash)' ' test_expect_success 'merge c0 with c1 (squash)' '
git reset --hard c0 && git reset --hard c0 &&
@ -316,10 +257,10 @@ test_expect_success 'merge c0 with c1 (squash)' '
verify_merge file result.1 && verify_merge file result.1 &&
verify_head $c0 && verify_head $c0 &&
verify_no_mergehead && verify_no_mergehead &&
verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" test_cmp squash.1 .git/SQUASH_MSG
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 (squash, ff-only)' ' test_expect_success 'merge c0 with c1 (squash, ff-only)' '
git reset --hard c0 && git reset --hard c0 &&
@ -327,10 +268,10 @@ test_expect_success 'merge c0 with c1 (squash, ff-only)' '
verify_merge file result.1 && verify_merge file result.1 &&
verify_head $c0 && verify_head $c0 &&
verify_no_mergehead && verify_no_mergehead &&
verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" test_cmp squash.1 .git/SQUASH_MSG
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (squash)' ' test_expect_success 'merge c1 with c2 (squash)' '
git reset --hard c1 && git reset --hard c1 &&
@ -338,17 +279,17 @@ test_expect_success 'merge c1 with c2 (squash)' '
verify_merge file result.1-5 && verify_merge file result.1-5 &&
verify_head $c1 && verify_head $c1 &&
verify_no_mergehead && verify_no_mergehead &&
verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message" test_cmp squash.1-5 .git/SQUASH_MSG
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' ' test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
git reset --hard c1 && git reset --hard c1 &&
test_must_fail git merge --squash --ff-only c2 test_must_fail git merge --squash --ff-only c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 and c3 (squash)' ' test_expect_success 'merge c1 with c2 and c3 (squash)' '
git reset --hard c1 && git reset --hard c1 &&
@ -356,10 +297,10 @@ test_expect_success 'merge c1 with c2 and c3 (squash)' '
verify_merge file result.1-5-9 && verify_merge file result.1-5-9 &&
verify_head $c1 && verify_head $c1 &&
verify_no_mergehead && verify_no_mergehead &&
verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message" test_cmp squash.1-5-9 .git/SQUASH_MSG
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (no-commit in config)' ' test_expect_success 'merge c1 with c2 (no-commit in config)' '
git reset --hard c1 && git reset --hard c1 &&
@ -370,7 +311,7 @@ test_expect_success 'merge c1 with c2 (no-commit in config)' '
verify_mergeheads $c2 verify_mergeheads $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (squash in config)' ' test_expect_success 'merge c1 with c2 (squash in config)' '
git reset --hard c1 && git reset --hard c1 &&
@ -379,10 +320,10 @@ test_expect_success 'merge c1 with c2 (squash in config)' '
verify_merge file result.1-5 && verify_merge file result.1-5 &&
verify_head $c1 && verify_head $c1 &&
verify_no_mergehead && verify_no_mergehead &&
verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message" test_cmp squash.1-5 .git/SQUASH_MSG
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'override config option -n with --summary' ' test_expect_success 'override config option -n with --summary' '
git reset --hard c1 && git reset --hard c1 &&
@ -412,7 +353,7 @@ test_expect_success 'override config option -n with --stat' '
fi fi
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'override config option --stat' ' test_expect_success 'override config option --stat' '
git reset --hard c1 && git reset --hard c1 &&
@ -428,7 +369,7 @@ test_expect_success 'override config option --stat' '
fi fi
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (override --no-commit)' ' test_expect_success 'merge c1 with c2 (override --no-commit)' '
git reset --hard c1 && git reset --hard c1 &&
@ -439,7 +380,7 @@ test_expect_success 'merge c1 with c2 (override --no-commit)' '
verify_parents $c1 $c2 verify_parents $c1 $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c2 (override --squash)' ' test_expect_success 'merge c1 with c2 (override --squash)' '
git reset --hard c1 && git reset --hard c1 &&
@ -450,7 +391,7 @@ test_expect_success 'merge c1 with c2 (override --squash)' '
verify_parents $c1 $c2 verify_parents $c1 $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c0 with c1 (no-ff)' ' test_expect_success 'merge c0 with c1 (no-ff)' '
git reset --hard c0 && git reset --hard c0 &&
@ -461,7 +402,7 @@ test_expect_success 'merge c0 with c1 (no-ff)' '
verify_parents $c0 $c1 verify_parents $c0 $c1
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'combining --squash and --no-ff is refused' ' test_expect_success 'combining --squash and --no-ff is refused' '
test_must_fail git merge --squash --no-ff c1 && test_must_fail git merge --squash --no-ff c1 &&
@ -485,20 +426,20 @@ test_expect_success 'merge log message' '
git reset --hard c0 && git reset --hard c0 &&
git merge --no-log c2 && git merge --no-log c2 &&
git show -s --pretty=format:%b HEAD >msg.act && git show -s --pretty=format:%b HEAD >msg.act &&
verify_diff msg.nolog msg.act "[OOPS] bad merge log message" && test_cmp msg.nolog msg.act &&
git merge --log c3 && git merge --log c3 &&
git show -s --pretty=format:%b HEAD >msg.act && git show -s --pretty=format:%b HEAD >msg.act &&
verify_diff msg.log msg.act "[OOPS] bad merge log message" && test_cmp msg.log msg.act &&
git reset --hard HEAD^ && git reset --hard HEAD^ &&
git config merge.log yes && git config merge.log yes &&
git merge c3 && git merge c3 &&
git show -s --pretty=format:%b HEAD >msg.act && git show -s --pretty=format:%b HEAD >msg.act &&
verify_diff msg.log msg.act "[OOPS] bad merge log message" test_cmp msg.log msg.act
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c0, c2, c0, and c1' ' test_expect_success 'merge c1 with c0, c2, c0, and c1' '
git reset --hard c1 && git reset --hard c1 &&
@ -509,7 +450,7 @@ test_expect_success 'merge c1 with c0, c2, c0, and c1' '
verify_parents $c1 $c2 verify_parents $c1 $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c0, c2, c0, and c1' ' test_expect_success 'merge c1 with c0, c2, c0, and c1' '
git reset --hard c1 && git reset --hard c1 &&
@ -520,7 +461,7 @@ test_expect_success 'merge c1 with c0, c2, c0, and c1' '
verify_parents $c1 $c2 verify_parents $c1 $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge c1 with c1 and c2' ' test_expect_success 'merge c1 with c1 and c2' '
git reset --hard c1 && git reset --hard c1 &&
@ -531,7 +472,7 @@ test_expect_success 'merge c1 with c1 and c2' '
verify_parents $c1 $c2 verify_parents $c1 $c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge fast-forward in a dirty tree' ' test_expect_success 'merge fast-forward in a dirty tree' '
git reset --hard c0 && git reset --hard c0 &&
@ -541,16 +482,16 @@ test_expect_success 'merge fast-forward in a dirty tree' '
git merge c2 git merge c2
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'in-index merge' ' test_expect_success 'in-index merge' '
git reset --hard c0 && git reset --hard c0 &&
git merge --no-ff -s resolve c1 > out && git merge --no-ff -s resolve c1 >out &&
grep "Wonderful." out && grep "Wonderful." out &&
verify_parents $c0 $c1 verify_parents $c0 $c1
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'refresh the index before merging' ' test_expect_success 'refresh the index before merging' '
git reset --hard c1 && git reset --hard c1 &&
@ -558,31 +499,39 @@ test_expect_success 'refresh the index before merging' '
git merge c3 git merge c3
' '
cat >expected <<EOF cat >expected.branch <<\EOF
Merge branch 'c5' (early part) Merge branch 'c5-branch' (early part)
EOF
cat >expected.tag <<\EOF
Merge commit 'c5~1'
EOF EOF
test_expect_success 'merge early part of c2' ' test_expect_success 'merge early part of c2' '
git reset --hard c3 && git reset --hard c3 &&
echo c4 > c4.c && echo c4 >c4.c &&
git add c4.c && git add c4.c &&
git commit -m c4 && git commit -m c4 &&
git tag c4 && git tag c4 &&
echo c5 > c5.c && echo c5 >c5.c &&
git add c5.c && git add c5.c &&
git commit -m c5 && git commit -m c5 &&
git tag c5 && git tag c5 &&
git reset --hard c3 && git reset --hard c3 &&
echo c6 > c6.c && echo c6 >c6.c &&
git add c6.c && git add c6.c &&
git commit -m c6 && git commit -m c6 &&
git tag c6 && git tag c6 &&
git branch -f c5-branch c5 &&
git merge c5-branch~1 &&
git show -s --pretty=format:%s HEAD >actual.branch &&
git reset --keep HEAD^ &&
git merge c5~1 && git merge c5~1 &&
git show -s --pretty=format:%s HEAD > actual && git show -s --pretty=format:%s HEAD >actual.tag &&
test_cmp actual expected test_cmp expected.branch actual.branch &&
test_cmp expected.tag actual.tag
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'merge --no-ff --no-commit && commit' ' test_expect_success 'merge --no-ff --no-commit && commit' '
git reset --hard c0 && git reset --hard c0 &&
@ -591,13 +540,13 @@ test_expect_success 'merge --no-ff --no-commit && commit' '
verify_parents $c0 $c1 verify_parents $c0 $c1
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_expect_success 'amending no-ff merge commit' ' test_expect_success 'amending no-ff merge commit' '
EDITOR=: git commit --amend && EDITOR=: git commit --amend &&
verify_parents $c0 $c1 verify_parents $c0 $c1
' '
test_debug 'gitk --all' test_debug 'git log --graph --decorate --oneline --all'
test_done test_done