Merge branch 'jc/merge'
"git merge FETCH_HEAD" learned that the previous "git fetch" could be to create an Octopus merge, i.e. recording multiple branches that are not marked as "not-for-merge"; this allows us to lose an old style invocation "git merge <msg> HEAD $commits..." in the implementation of "git pull" script; the old style syntax can now be deprecated. * jc/merge: merge: deprecate 'git merge <message> HEAD <commit>' syntax merge: handle FETCH_HEAD internally merge: decide if we auto-generate the message early in collect_parents() merge: make collect_parents() auto-generate the merge message merge: extract prepare_merge_message() logic out merge: narrow scope of merge_names merge: split reduce_parents() out of collect_parents() merge: clarify collect_parents() logic merge: small leakfix and code simplification merge: do not check argc to determine number of remote heads merge: clarify "pulling into void" special case t5520: test pulling an octopus into an unborn branch t5520: style fixes merge: simplify code flow merge: test the top-level merge driver
This commit is contained in:
commit
bcd1ecd08a
@ -104,6 +104,10 @@ commit or stash your changes before running 'git merge'.
|
|||||||
If no commit is given from the command line, merge the remote-tracking
|
If no commit is given from the command line, merge the remote-tracking
|
||||||
branches that the current branch is configured to use as its upstream.
|
branches that the current branch is configured to use as its upstream.
|
||||||
See also the configuration section of this manual page.
|
See also the configuration section of this manual page.
|
||||||
|
+
|
||||||
|
When `FETCH_HEAD` (and no other commit) is specified, the branches
|
||||||
|
recorded in the `.git/FETCH_HEAD` file by the previous invocation
|
||||||
|
of `git fetch` for merging are merged to the current branch.
|
||||||
|
|
||||||
|
|
||||||
PRE-MERGE CHECKS
|
PRE-MERGE CHECKS
|
||||||
|
258
builtin/merge.c
258
builtin/merge.c
@ -492,8 +492,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
|
|||||||
}
|
}
|
||||||
if (len) {
|
if (len) {
|
||||||
struct strbuf truname = STRBUF_INIT;
|
struct strbuf truname = STRBUF_INIT;
|
||||||
strbuf_addstr(&truname, "refs/heads/");
|
strbuf_addf(&truname, "refs/heads/%s", remote);
|
||||||
strbuf_addstr(&truname, remote);
|
|
||||||
strbuf_setlen(&truname, truname.len - len);
|
strbuf_setlen(&truname, truname.len - len);
|
||||||
if (ref_exists(truname.buf)) {
|
if (ref_exists(truname.buf)) {
|
||||||
strbuf_addf(msg,
|
strbuf_addf(msg,
|
||||||
@ -504,28 +503,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
|
|||||||
strbuf_release(&truname);
|
strbuf_release(&truname);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
strbuf_release(&truname);
|
||||||
|
|
||||||
if (!strcmp(remote, "FETCH_HEAD") &&
|
|
||||||
!access(git_path("FETCH_HEAD"), R_OK)) {
|
|
||||||
const char *filename;
|
|
||||||
FILE *fp;
|
|
||||||
struct strbuf line = STRBUF_INIT;
|
|
||||||
char *ptr;
|
|
||||||
|
|
||||||
filename = git_path("FETCH_HEAD");
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
if (!fp)
|
|
||||||
die_errno(_("could not open '%s' for reading"),
|
|
||||||
filename);
|
|
||||||
strbuf_getline(&line, fp, '\n');
|
|
||||||
fclose(fp);
|
|
||||||
ptr = strstr(line.buf, "\tnot-for-merge\t");
|
|
||||||
if (ptr)
|
|
||||||
strbuf_remove(&line, ptr-line.buf+1, 13);
|
|
||||||
strbuf_addbuf(msg, &line);
|
|
||||||
strbuf_release(&line);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remote_head->util) {
|
if (remote_head->util) {
|
||||||
@ -1037,28 +1015,24 @@ static int default_edit_option(void)
|
|||||||
st_stdin.st_mode == st_stdout.st_mode);
|
st_stdin.st_mode == st_stdout.st_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct commit_list *collect_parents(struct commit *head_commit,
|
static struct commit_list *reduce_parents(struct commit *head_commit,
|
||||||
int *head_subsumed,
|
int *head_subsumed,
|
||||||
int argc, const char **argv)
|
struct commit_list *remoteheads)
|
||||||
{
|
{
|
||||||
int i;
|
struct commit_list *parents, *next, **remotes = &remoteheads;
|
||||||
struct commit_list *remoteheads = NULL, *parents, *next;
|
|
||||||
struct commit_list **remotes = &remoteheads;
|
|
||||||
|
|
||||||
if (head_commit)
|
/*
|
||||||
remotes = &commit_list_insert(head_commit, remotes)->next;
|
* Is the current HEAD reachable from another commit being
|
||||||
for (i = 0; i < argc; i++) {
|
* merged? If so we do not want to record it as a parent of
|
||||||
struct commit *commit = get_merge_parent(argv[i]);
|
* the resulting merge, unless --no-ff is given. We will flip
|
||||||
if (!commit)
|
* this variable to 0 when we find HEAD among the independent
|
||||||
help_unknown_ref(argv[i], "merge",
|
* tips being merged.
|
||||||
"not something we can merge");
|
*/
|
||||||
remotes = &commit_list_insert(commit, remotes)->next;
|
*head_subsumed = 1;
|
||||||
}
|
|
||||||
*remotes = NULL;
|
|
||||||
|
|
||||||
|
/* Find what parents to record by checking independent ones. */
|
||||||
parents = reduce_heads(remoteheads);
|
parents = reduce_heads(remoteheads);
|
||||||
|
|
||||||
*head_subsumed = 1; /* we will flip this to 0 when we find it */
|
|
||||||
for (remoteheads = NULL, remotes = &remoteheads;
|
for (remoteheads = NULL, remotes = &remoteheads;
|
||||||
parents;
|
parents;
|
||||||
parents = next) {
|
parents = next) {
|
||||||
@ -1068,10 +1042,122 @@ static struct commit_list *collect_parents(struct commit *head_commit,
|
|||||||
*head_subsumed = 0;
|
*head_subsumed = 0;
|
||||||
else
|
else
|
||||||
remotes = &commit_list_insert(commit, remotes)->next;
|
remotes = &commit_list_insert(commit, remotes)->next;
|
||||||
|
free(parents);
|
||||||
}
|
}
|
||||||
return remoteheads;
|
return remoteheads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg)
|
||||||
|
{
|
||||||
|
struct fmt_merge_msg_opts opts;
|
||||||
|
|
||||||
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
opts.add_title = !have_message;
|
||||||
|
opts.shortlog_len = shortlog_len;
|
||||||
|
opts.credit_people = (0 < option_edit);
|
||||||
|
|
||||||
|
fmt_merge_msg(merge_names, merge_msg, &opts);
|
||||||
|
if (merge_msg->len)
|
||||||
|
strbuf_setlen(merge_msg, merge_msg->len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names)
|
||||||
|
{
|
||||||
|
const char *filename;
|
||||||
|
int fd, pos, npos;
|
||||||
|
struct strbuf fetch_head_file = STRBUF_INIT;
|
||||||
|
|
||||||
|
if (!merge_names)
|
||||||
|
merge_names = &fetch_head_file;
|
||||||
|
|
||||||
|
filename = git_path("FETCH_HEAD");
|
||||||
|
fd = open(filename, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
die_errno(_("could not open '%s' for reading"), filename);
|
||||||
|
|
||||||
|
if (strbuf_read(merge_names, fd, 0) < 0)
|
||||||
|
die_errno(_("could not read '%s'"), filename);
|
||||||
|
if (close(fd) < 0)
|
||||||
|
die_errno(_("could not close '%s'"), filename);
|
||||||
|
|
||||||
|
for (pos = 0; pos < merge_names->len; pos = npos) {
|
||||||
|
unsigned char sha1[20];
|
||||||
|
char *ptr;
|
||||||
|
struct commit *commit;
|
||||||
|
|
||||||
|
ptr = strchr(merge_names->buf + pos, '\n');
|
||||||
|
if (ptr)
|
||||||
|
npos = ptr - merge_names->buf + 1;
|
||||||
|
else
|
||||||
|
npos = merge_names->len;
|
||||||
|
|
||||||
|
if (npos - pos < 40 + 2 ||
|
||||||
|
get_sha1_hex(merge_names->buf + pos, sha1))
|
||||||
|
commit = NULL; /* bad */
|
||||||
|
else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
|
||||||
|
continue; /* not-for-merge */
|
||||||
|
else {
|
||||||
|
char saved = merge_names->buf[pos + 40];
|
||||||
|
merge_names->buf[pos + 40] = '\0';
|
||||||
|
commit = get_merge_parent(merge_names->buf + pos);
|
||||||
|
merge_names->buf[pos + 40] = saved;
|
||||||
|
}
|
||||||
|
if (!commit) {
|
||||||
|
if (ptr)
|
||||||
|
*ptr = '\0';
|
||||||
|
die("not something we can merge in %s: %s",
|
||||||
|
filename, merge_names->buf + pos);
|
||||||
|
}
|
||||||
|
remotes = &commit_list_insert(commit, remotes)->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (merge_names == &fetch_head_file)
|
||||||
|
strbuf_release(&fetch_head_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct commit_list *collect_parents(struct commit *head_commit,
|
||||||
|
int *head_subsumed,
|
||||||
|
int argc, const char **argv,
|
||||||
|
struct strbuf *merge_msg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct commit_list *remoteheads = NULL;
|
||||||
|
struct commit_list **remotes = &remoteheads;
|
||||||
|
struct strbuf merge_names = STRBUF_INIT, *autogen = NULL;
|
||||||
|
|
||||||
|
if (merge_msg && (!have_message || shortlog_len))
|
||||||
|
autogen = &merge_names;
|
||||||
|
|
||||||
|
if (head_commit)
|
||||||
|
remotes = &commit_list_insert(head_commit, remotes)->next;
|
||||||
|
|
||||||
|
if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) {
|
||||||
|
handle_fetch_head(remotes, autogen);
|
||||||
|
remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
struct commit *commit = get_merge_parent(argv[i]);
|
||||||
|
if (!commit)
|
||||||
|
help_unknown_ref(argv[i], "merge",
|
||||||
|
"not something we can merge");
|
||||||
|
remotes = &commit_list_insert(commit, remotes)->next;
|
||||||
|
}
|
||||||
|
remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads);
|
||||||
|
if (autogen) {
|
||||||
|
struct commit_list *p;
|
||||||
|
for (p = remoteheads; p; p = p->next)
|
||||||
|
merge_name(merge_remote_util(p->item)->name, autogen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autogen) {
|
||||||
|
prepare_merge_message(autogen, merge_msg);
|
||||||
|
strbuf_release(autogen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteheads;
|
||||||
|
}
|
||||||
|
|
||||||
int cmd_merge(int argc, const char **argv, const char *prefix)
|
int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
unsigned char result_tree[20];
|
unsigned char result_tree[20];
|
||||||
@ -1158,19 +1244,44 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||||||
option_commit = 0;
|
option_commit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!abort_current_merge) {
|
if (!argc) {
|
||||||
if (!argc) {
|
if (default_to_upstream)
|
||||||
if (default_to_upstream)
|
argc = setup_with_upstream(&argv);
|
||||||
argc = setup_with_upstream(&argv);
|
else
|
||||||
else
|
die(_("No commit specified and merge.defaultToUpstream not set."));
|
||||||
die(_("No commit specified and merge.defaultToUpstream not set."));
|
} else if (argc == 1 && !strcmp(argv[0], "-")) {
|
||||||
} else if (argc == 1 && !strcmp(argv[0], "-"))
|
argv[0] = "@{-1}";
|
||||||
argv[0] = "@{-1}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argc)
|
if (!argc)
|
||||||
usage_with_options(builtin_merge_usage,
|
usage_with_options(builtin_merge_usage,
|
||||||
builtin_merge_options);
|
builtin_merge_options);
|
||||||
|
|
||||||
|
if (!head_commit) {
|
||||||
|
struct commit *remote_head;
|
||||||
|
/*
|
||||||
|
* If the merged head is a valid one there is no reason
|
||||||
|
* to forbid "git merge" into a branch yet to be born.
|
||||||
|
* We do the same for "git pull".
|
||||||
|
*/
|
||||||
|
if (squash)
|
||||||
|
die(_("Squash commit into empty head not supported yet"));
|
||||||
|
if (fast_forward == FF_NO)
|
||||||
|
die(_("Non-fast-forward commit does not make sense into "
|
||||||
|
"an empty head"));
|
||||||
|
remoteheads = collect_parents(head_commit, &head_subsumed,
|
||||||
|
argc, argv, NULL);
|
||||||
|
remote_head = remoteheads->item;
|
||||||
|
if (!remote_head)
|
||||||
|
die(_("%s - not something we can merge"), argv[0]);
|
||||||
|
if (remoteheads->next)
|
||||||
|
die(_("Can merge only exactly one commit into empty head"));
|
||||||
|
read_empty(remote_head->object.sha1, 0);
|
||||||
|
update_ref("initial pull", "HEAD", remote_head->object.sha1,
|
||||||
|
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This could be traditional "merge <msg> HEAD <commit>..." and
|
* This could be traditional "merge <msg> HEAD <commit>..." and
|
||||||
* the way we can tell it is to see if the second token is HEAD,
|
* the way we can tell it is to see if the second token is HEAD,
|
||||||
@ -1179,40 +1290,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||||||
* Traditional format never would have "-m" so it is an
|
* Traditional format never would have "-m" so it is an
|
||||||
* additional safety measure to check for it.
|
* additional safety measure to check for it.
|
||||||
*/
|
*/
|
||||||
|
if (!have_message &&
|
||||||
if (!have_message && head_commit &&
|
|
||||||
is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
|
is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
|
||||||
|
warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
|
||||||
strbuf_addstr(&merge_msg, argv[0]);
|
strbuf_addstr(&merge_msg, argv[0]);
|
||||||
head_arg = argv[1];
|
head_arg = argv[1];
|
||||||
argv += 2;
|
argv += 2;
|
||||||
argc -= 2;
|
argc -= 2;
|
||||||
remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
|
remoteheads = collect_parents(head_commit, &head_subsumed,
|
||||||
} else if (!head_commit) {
|
argc, argv, NULL);
|
||||||
struct commit *remote_head;
|
|
||||||
/*
|
|
||||||
* If the merged head is a valid one there is no reason
|
|
||||||
* to forbid "git merge" into a branch yet to be born.
|
|
||||||
* We do the same for "git pull".
|
|
||||||
*/
|
|
||||||
if (argc != 1)
|
|
||||||
die(_("Can merge only exactly one commit into "
|
|
||||||
"empty head"));
|
|
||||||
if (squash)
|
|
||||||
die(_("Squash commit into empty head not supported yet"));
|
|
||||||
if (fast_forward == FF_NO)
|
|
||||||
die(_("Non-fast-forward commit does not make sense into "
|
|
||||||
"an empty head"));
|
|
||||||
remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
|
|
||||||
remote_head = remoteheads->item;
|
|
||||||
if (!remote_head)
|
|
||||||
die(_("%s - not something we can merge"), argv[0]);
|
|
||||||
read_empty(remote_head->object.sha1, 0);
|
|
||||||
update_ref("initial pull", "HEAD", remote_head->object.sha1,
|
|
||||||
NULL, 0, UPDATE_REFS_DIE_ON_ERR);
|
|
||||||
goto done;
|
|
||||||
} else {
|
} else {
|
||||||
struct strbuf merge_names = STRBUF_INIT;
|
|
||||||
|
|
||||||
/* We are invoked directly as the first-class UI. */
|
/* We are invoked directly as the first-class UI. */
|
||||||
head_arg = "HEAD";
|
head_arg = "HEAD";
|
||||||
|
|
||||||
@ -1221,21 +1308,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||||||
* the standard merge summary message to be appended
|
* the standard merge summary message to be appended
|
||||||
* to the given message.
|
* to the given message.
|
||||||
*/
|
*/
|
||||||
remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
|
remoteheads = collect_parents(head_commit, &head_subsumed,
|
||||||
for (p = remoteheads; p; p = p->next)
|
argc, argv, &merge_msg);
|
||||||
merge_name(merge_remote_util(p->item)->name, &merge_names);
|
|
||||||
|
|
||||||
if (!have_message || shortlog_len) {
|
|
||||||
struct fmt_merge_msg_opts opts;
|
|
||||||
memset(&opts, 0, sizeof(opts));
|
|
||||||
opts.add_title = !have_message;
|
|
||||||
opts.shortlog_len = shortlog_len;
|
|
||||||
opts.credit_people = (0 < option_edit);
|
|
||||||
|
|
||||||
fmt_merge_msg(&merge_names, &merge_msg, &opts);
|
|
||||||
if (merge_msg.len)
|
|
||||||
strbuf_setlen(&merge_msg, merge_msg.len - 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!head_commit || !argc)
|
if (!head_commit || !argc)
|
||||||
|
@ -1162,7 +1162,7 @@ if ($orig_branch) {
|
|||||||
die "Fast-forward update failed: $?\n" if $?;
|
die "Fast-forward update failed: $?\n" if $?;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
|
system(qw(git merge -m cvsimport), "$remote/$opt_o");
|
||||||
die "Could not merge $opt_o into the current branch.\n" if $?;
|
die "Could not merge $opt_o into the current branch.\n" if $?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -323,7 +323,6 @@ then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
|
|
||||||
case "$rebase" in
|
case "$rebase" in
|
||||||
true)
|
true)
|
||||||
eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
|
eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
|
||||||
@ -334,7 +333,7 @@ true)
|
|||||||
eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
|
eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
|
||||||
eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
|
eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
|
||||||
eval="$eval $gpg_sign_args"
|
eval="$eval $gpg_sign_args"
|
||||||
eval="$eval \"\$merge_name\" HEAD $merge_head"
|
eval="$eval FETCH_HEAD"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
eval "exec $eval"
|
eval "exec $eval"
|
||||||
|
136
t/t3033-merge-toplevel.sh
Executable file
136
t/t3033-merge-toplevel.sh
Executable file
@ -0,0 +1,136 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='"git merge" top-level frontend'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
t3033_reset () {
|
||||||
|
git checkout -B master two &&
|
||||||
|
git branch -f left three &&
|
||||||
|
git branch -f right four
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success setup '
|
||||||
|
test_commit one &&
|
||||||
|
git branch left &&
|
||||||
|
git branch right &&
|
||||||
|
test_commit two &&
|
||||||
|
git checkout left &&
|
||||||
|
test_commit three &&
|
||||||
|
git checkout right &&
|
||||||
|
test_commit four &&
|
||||||
|
git checkout master
|
||||||
|
'
|
||||||
|
|
||||||
|
# Local branches
|
||||||
|
|
||||||
|
test_expect_success 'merge an octopus into void' '
|
||||||
|
t3033_reset &&
|
||||||
|
git checkout --orphan test &&
|
||||||
|
git rm -fr . &&
|
||||||
|
test_must_fail git merge left right &&
|
||||||
|
test_must_fail git rev-parse --verify HEAD &&
|
||||||
|
git diff --quiet &&
|
||||||
|
test_must_fail git rev-parse HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge an octopus, fast-forward (ff)' '
|
||||||
|
t3033_reset &&
|
||||||
|
git reset --hard one &&
|
||||||
|
git merge left right &&
|
||||||
|
# one is ancestor of three (left) and four (right)
|
||||||
|
test_must_fail git rev-parse --verify HEAD^3 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 | sort >actual &&
|
||||||
|
git rev-parse three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge octopus, non-fast-forward (ff)' '
|
||||||
|
t3033_reset &&
|
||||||
|
git reset --hard one &&
|
||||||
|
git merge --no-ff left right &&
|
||||||
|
# one is ancestor of three (left) and four (right)
|
||||||
|
test_must_fail git rev-parse --verify HEAD^4 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
|
||||||
|
git rev-parse one three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge octopus, fast-forward (does not ff)' '
|
||||||
|
t3033_reset &&
|
||||||
|
git merge left right &&
|
||||||
|
# two (master) is not an ancestor of three (left) and four (right)
|
||||||
|
test_must_fail git rev-parse --verify HEAD^4 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
|
||||||
|
git rev-parse two three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge octopus, non-fast-forward' '
|
||||||
|
t3033_reset &&
|
||||||
|
git merge --no-ff left right &&
|
||||||
|
test_must_fail git rev-parse --verify HEAD^4 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
|
||||||
|
git rev-parse two three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
# The same set with FETCH_HEAD
|
||||||
|
|
||||||
|
test_expect_success 'merge FETCH_HEAD octopus into void' '
|
||||||
|
t3033_reset &&
|
||||||
|
git checkout --orphan test &&
|
||||||
|
git rm -fr . &&
|
||||||
|
git fetch . left right &&
|
||||||
|
test_must_fail git merge FETCH_HEAD &&
|
||||||
|
test_must_fail git rev-parse --verify HEAD &&
|
||||||
|
git diff --quiet &&
|
||||||
|
test_must_fail git rev-parse HEAD
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge FETCH_HEAD octopus fast-forward (ff)' '
|
||||||
|
t3033_reset &&
|
||||||
|
git reset --hard one &&
|
||||||
|
git fetch . left right &&
|
||||||
|
git merge FETCH_HEAD &&
|
||||||
|
# one is ancestor of three (left) and four (right)
|
||||||
|
test_must_fail git rev-parse --verify HEAD^3 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 | sort >actual &&
|
||||||
|
git rev-parse three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge FETCH_HEAD octopus non-fast-forward (ff)' '
|
||||||
|
t3033_reset &&
|
||||||
|
git reset --hard one &&
|
||||||
|
git fetch . left right &&
|
||||||
|
git merge --no-ff FETCH_HEAD &&
|
||||||
|
# one is ancestor of three (left) and four (right)
|
||||||
|
test_must_fail git rev-parse --verify HEAD^4 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
|
||||||
|
git rev-parse one three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge FETCH_HEAD octopus fast-forward (does not ff)' '
|
||||||
|
t3033_reset &&
|
||||||
|
git fetch . left right &&
|
||||||
|
git merge FETCH_HEAD &&
|
||||||
|
# two (master) is not an ancestor of three (left) and four (right)
|
||||||
|
test_must_fail git rev-parse --verify HEAD^4 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
|
||||||
|
git rev-parse two three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'merge FETCH_HEAD octopus non-fast-forward' '
|
||||||
|
t3033_reset &&
|
||||||
|
git fetch . left right &&
|
||||||
|
git merge --no-ff FETCH_HEAD &&
|
||||||
|
test_must_fail git rev-parse --verify HEAD^4 &&
|
||||||
|
git rev-parse HEAD^1 HEAD^2 HEAD^3 | sort >actual &&
|
||||||
|
git rev-parse two three four | sort >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
@ -47,7 +47,7 @@ test_expect_success setup '
|
|||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'reference merge' '
|
test_expect_success 'reference merge' '
|
||||||
git merge -s recursive "reference merge" HEAD master
|
git merge -s recursive -m "reference merge" master
|
||||||
'
|
'
|
||||||
|
|
||||||
PRE_REBASE=$(git rev-parse test-rebase)
|
PRE_REBASE=$(git rev-parse test-rebase)
|
||||||
|
@ -9,36 +9,27 @@ modify () {
|
|||||||
mv "$2.x" "$2"
|
mv "$2.x" "$2"
|
||||||
}
|
}
|
||||||
|
|
||||||
D=`pwd`
|
|
||||||
|
|
||||||
test_expect_success setup '
|
test_expect_success setup '
|
||||||
|
|
||||||
echo file >file &&
|
echo file >file &&
|
||||||
git add file &&
|
git add file &&
|
||||||
git commit -a -m original
|
git commit -a -m original
|
||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'pulling into void' '
|
test_expect_success 'pulling into void' '
|
||||||
mkdir cloned &&
|
git init cloned &&
|
||||||
cd cloned &&
|
(
|
||||||
git init &&
|
cd cloned &&
|
||||||
git pull ..
|
git pull ..
|
||||||
'
|
) &&
|
||||||
|
|
||||||
cd "$D"
|
|
||||||
|
|
||||||
test_expect_success 'checking the results' '
|
|
||||||
test -f file &&
|
test -f file &&
|
||||||
test -f cloned/file &&
|
test -f cloned/file &&
|
||||||
test_cmp file cloned/file
|
test_cmp file cloned/file
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'pulling into void using master:master' '
|
test_expect_success 'pulling into void using master:master' '
|
||||||
mkdir cloned-uho &&
|
git init cloned-uho &&
|
||||||
(
|
(
|
||||||
cd cloned-uho &&
|
cd cloned-uho &&
|
||||||
git init &&
|
|
||||||
git pull .. master:master
|
git pull .. master:master
|
||||||
) &&
|
) &&
|
||||||
test -f file &&
|
test -f file &&
|
||||||
@ -71,7 +62,6 @@ test_expect_success 'pulling into void does not overwrite staged files' '
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
||||||
test_expect_success 'pulling into void does not remove new staged files' '
|
test_expect_success 'pulling into void does not remove new staged files' '
|
||||||
git init cloned-staged-new &&
|
git init cloned-staged-new &&
|
||||||
(
|
(
|
||||||
@ -86,6 +76,15 @@ test_expect_success 'pulling into void does not remove new staged files' '
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'pulling into void must not create an octopus' '
|
||||||
|
git init cloned-octopus &&
|
||||||
|
(
|
||||||
|
cd cloned-octopus &&
|
||||||
|
test_must_fail git pull .. master master &&
|
||||||
|
! test -f file
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'test . as a remote' '
|
test_expect_success 'test . as a remote' '
|
||||||
|
|
||||||
git branch copy master &&
|
git branch copy master &&
|
||||||
|
@ -24,7 +24,7 @@ test_expect_success 'prepare repository' '
|
|||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'Merge with d/f conflicts' '
|
test_expect_success 'Merge with d/f conflicts' '
|
||||||
test_expect_code 1 git merge "merge msg" B master
|
test_expect_code 1 git merge -m "merge msg" master
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'F/D conflict' '
|
test_expect_success 'F/D conflict' '
|
||||||
|
@ -48,7 +48,7 @@ echo "1
|
|||||||
" > file &&
|
" > file &&
|
||||||
git commit -m "C3" file &&
|
git commit -m "C3" file &&
|
||||||
git branch C3 &&
|
git branch C3 &&
|
||||||
git merge "pre E3 merge" B A &&
|
git merge -m "pre E3 merge" A &&
|
||||||
echo "1
|
echo "1
|
||||||
2
|
2
|
||||||
3 changed in E3, branch B. New file size
|
3 changed in E3, branch B. New file size
|
||||||
@ -61,7 +61,7 @@ echo "1
|
|||||||
" > file &&
|
" > file &&
|
||||||
git commit -m "E3" file &&
|
git commit -m "E3" file &&
|
||||||
git checkout A &&
|
git checkout A &&
|
||||||
git merge "pre D8 merge" A C3 &&
|
git merge -m "pre D8 merge" C3 &&
|
||||||
echo "1
|
echo "1
|
||||||
2
|
2
|
||||||
3 changed in C3, branch B
|
3 changed in C3, branch B
|
||||||
@ -73,7 +73,7 @@ echo "1
|
|||||||
9" > file &&
|
9" > file &&
|
||||||
git commit -m D8 file'
|
git commit -m D8 file'
|
||||||
|
|
||||||
test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
|
test_expect_success 'Criss-cross merge' 'git merge -m "final merge" B'
|
||||||
|
|
||||||
cat > file-expect <<EOF
|
cat > file-expect <<EOF
|
||||||
1
|
1
|
||||||
|
@ -496,7 +496,7 @@ test_expect_success 'check [cvswork3] diff' '
|
|||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'merge early [cvswork3] b3 with b1' '
|
test_expect_success 'merge early [cvswork3] b3 with b1' '
|
||||||
( cd gitwork3 && git merge "message" HEAD b1 ) &&
|
( cd gitwork3 && git merge -m "message" b1 ) &&
|
||||||
git fetch gitwork3 b3:b3 &&
|
git fetch gitwork3 b3:b3 &&
|
||||||
git tag v3merged b3 &&
|
git tag v3merged b3 &&
|
||||||
git push --tags gitcvs.git b3:b3
|
git push --tags gitcvs.git b3:b3
|
||||||
|
Loading…
Reference in New Issue
Block a user