Merge branch 'mv/merge-in-c'
* mv/merge-in-c: reduce_heads(): protect from duplicate input reduce_heads(): thinkofix Add a new test for git-merge-resolve t6021: add a new test for git-merge-resolve Teach merge.log to "git-merge" again Build in merge Fix t7601-merge-pull-config.sh on AIX git-commit-tree: make it usable from other builtins Add new test case to ensure git-merge prepends the custom merge message Add new test case to ensure git-merge reduces octopus parents when possible Introduce reduce_heads() Introduce get_merge_bases_many() Add new test to ensure git-merge handles more than 25 refs. Introduce get_octopus_merge_bases() in commit.c git-fmt-merge-msg: make it usable from other builtins Move read_cache_unmerged() to read-cache.c Add new test to ensure git-merge handles pull.twohead and pull.octopus Move parse-options's skip_prefix() to git-compat-util.h Move commit_list_count() to commit.c Move split_cmdline() to alias.c Conflicts: Makefile parse-options.c
This commit is contained in:
commit
fcab40a389
2
Makefile
2
Makefile
@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh
|
||||
SCRIPT_SH += git-merge-octopus.sh
|
||||
SCRIPT_SH += git-merge-one-file.sh
|
||||
SCRIPT_SH += git-merge-resolve.sh
|
||||
SCRIPT_SH += git-merge.sh
|
||||
SCRIPT_SH += git-mergetool.sh
|
||||
SCRIPT_SH += git-parse-remote.sh
|
||||
SCRIPT_SH += git-pull.sh
|
||||
@ -515,6 +514,7 @@ BUILTIN_OBJS += builtin-ls-remote.o
|
||||
BUILTIN_OBJS += builtin-ls-tree.o
|
||||
BUILTIN_OBJS += builtin-mailinfo.o
|
||||
BUILTIN_OBJS += builtin-mailsplit.o
|
||||
BUILTIN_OBJS += builtin-merge.o
|
||||
BUILTIN_OBJS += builtin-merge-base.o
|
||||
BUILTIN_OBJS += builtin-merge-file.o
|
||||
BUILTIN_OBJS += builtin-merge-ours.o
|
||||
|
54
alias.c
54
alias.c
@ -21,3 +21,57 @@ char *alias_lookup(const char *alias)
|
||||
git_config(alias_lookup_cb, NULL);
|
||||
return alias_val;
|
||||
}
|
||||
|
||||
int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
char quoted = 0;
|
||||
|
||||
*argv = xmalloc(sizeof(char*) * size);
|
||||
|
||||
/* split alias_string */
|
||||
(*argv)[count++] = cmdline;
|
||||
for (src = dst = 0; cmdline[src];) {
|
||||
char c = cmdline[src];
|
||||
if (!quoted && isspace(c)) {
|
||||
cmdline[dst++] = 0;
|
||||
while (cmdline[++src]
|
||||
&& isspace(cmdline[src]))
|
||||
; /* skip */
|
||||
if (count >= size) {
|
||||
size += 16;
|
||||
*argv = xrealloc(*argv, sizeof(char*) * size);
|
||||
}
|
||||
(*argv)[count++] = cmdline + dst;
|
||||
} else if (!quoted && (c == '\'' || c == '"')) {
|
||||
quoted = c;
|
||||
src++;
|
||||
} else if (c == quoted) {
|
||||
quoted = 0;
|
||||
src++;
|
||||
} else {
|
||||
if (c == '\\' && quoted != '\'') {
|
||||
src++;
|
||||
c = cmdline[src];
|
||||
if (!c) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("cmdline ends with \\");
|
||||
}
|
||||
}
|
||||
cmdline[dst++] = c;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[dst] = 0;
|
||||
|
||||
if (quoted) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("unclosed quote");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -45,41 +45,19 @@ static const char commit_utf8_warn[] =
|
||||
"You may want to amend it after fixing the message, or set the config\n"
|
||||
"variable i18n.commitencoding to the encoding your project uses.\n";
|
||||
|
||||
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
int commit_tree(const char *msg, unsigned char *tree,
|
||||
struct commit_list *parents, unsigned char *ret)
|
||||
{
|
||||
int i;
|
||||
struct commit_list *parents = NULL;
|
||||
unsigned char tree_sha1[20];
|
||||
unsigned char commit_sha1[20];
|
||||
struct strbuf buffer;
|
||||
int encoding_is_utf8;
|
||||
struct strbuf buffer;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
if (argc < 2)
|
||||
usage(commit_tree_usage);
|
||||
if (get_sha1(argv[1], tree_sha1))
|
||||
die("Not a valid object name %s", argv[1]);
|
||||
|
||||
check_valid(tree_sha1, OBJ_TREE);
|
||||
for (i = 2; i < argc; i += 2) {
|
||||
unsigned char sha1[20];
|
||||
const char *a, *b;
|
||||
a = argv[i]; b = argv[i+1];
|
||||
if (!b || strcmp(a, "-p"))
|
||||
usage(commit_tree_usage);
|
||||
|
||||
if (get_sha1(b, sha1))
|
||||
die("Not a valid object name %s", b);
|
||||
check_valid(sha1, OBJ_COMMIT);
|
||||
new_parent(lookup_commit(sha1), &parents);
|
||||
}
|
||||
check_valid(tree, OBJ_TREE);
|
||||
|
||||
/* Not having i18n.commitencoding is the same as having utf-8 */
|
||||
encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
|
||||
|
||||
strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
|
||||
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1));
|
||||
strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
|
||||
|
||||
/*
|
||||
* NOTE! This ordering means that the same exact tree merged with a
|
||||
@ -102,14 +80,47 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
strbuf_addch(&buffer, '\n');
|
||||
|
||||
/* And add the comment */
|
||||
if (strbuf_read(&buffer, 0, 0) < 0)
|
||||
die("git-commit-tree: read returned %s", strerror(errno));
|
||||
strbuf_addstr(&buffer, msg);
|
||||
|
||||
/* And check the encoding */
|
||||
if (encoding_is_utf8 && !is_utf8(buffer.buf))
|
||||
fprintf(stderr, commit_utf8_warn);
|
||||
|
||||
if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) {
|
||||
return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
|
||||
}
|
||||
|
||||
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
struct commit_list *parents = NULL;
|
||||
unsigned char tree_sha1[20];
|
||||
unsigned char commit_sha1[20];
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
if (argc < 2)
|
||||
usage(commit_tree_usage);
|
||||
if (get_sha1(argv[1], tree_sha1))
|
||||
die("Not a valid object name %s", argv[1]);
|
||||
|
||||
for (i = 2; i < argc; i += 2) {
|
||||
unsigned char sha1[20];
|
||||
const char *a, *b;
|
||||
a = argv[i]; b = argv[i+1];
|
||||
if (!b || strcmp(a, "-p"))
|
||||
usage(commit_tree_usage);
|
||||
|
||||
if (get_sha1(b, sha1))
|
||||
die("Not a valid object name %s", b);
|
||||
check_valid(sha1, OBJ_COMMIT);
|
||||
new_parent(lookup_commit(sha1), &parents);
|
||||
}
|
||||
|
||||
if (strbuf_read(&buffer, 0, 0) < 0)
|
||||
die("git-commit-tree: read returned %s", strerror(errno));
|
||||
|
||||
if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1)) {
|
||||
printf("%s\n", sha1_to_hex(commit_sha1));
|
||||
return 0;
|
||||
}
|
||||
|
@ -159,23 +159,24 @@ static int handle_line(char *line)
|
||||
}
|
||||
|
||||
static void print_joined(const char *singular, const char *plural,
|
||||
struct list *list)
|
||||
struct list *list, struct strbuf *out)
|
||||
{
|
||||
if (list->nr == 0)
|
||||
return;
|
||||
if (list->nr == 1) {
|
||||
printf("%s%s", singular, list->list[0]);
|
||||
strbuf_addf(out, "%s%s", singular, list->list[0]);
|
||||
} else {
|
||||
int i;
|
||||
printf("%s", plural);
|
||||
strbuf_addstr(out, plural);
|
||||
for (i = 0; i < list->nr - 1; i++)
|
||||
printf("%s%s", i > 0 ? ", " : "", list->list[i]);
|
||||
printf(" and %s", list->list[list->nr - 1]);
|
||||
strbuf_addf(out, "%s%s", i > 0 ? ", " : "", list->list[i]);
|
||||
strbuf_addf(out, " and %s", list->list[list->nr - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void shortlog(const char *name, unsigned char *sha1,
|
||||
struct commit *head, struct rev_info *rev, int limit)
|
||||
struct commit *head, struct rev_info *rev, int limit,
|
||||
struct strbuf *out)
|
||||
{
|
||||
int i, count = 0;
|
||||
struct commit *commit;
|
||||
@ -232,15 +233,15 @@ static void shortlog(const char *name, unsigned char *sha1,
|
||||
}
|
||||
|
||||
if (count > limit)
|
||||
printf("\n* %s: (%d commits)\n", name, count);
|
||||
strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
|
||||
else
|
||||
printf("\n* %s:\n", name);
|
||||
strbuf_addf(out, "\n* %s:\n", name);
|
||||
|
||||
for (i = 0; i < subjects.nr; i++)
|
||||
if (i >= limit)
|
||||
printf(" ...\n");
|
||||
strbuf_addf(out, " ...\n");
|
||||
else
|
||||
printf(" %s\n", subjects.list[i]);
|
||||
strbuf_addf(out, " %s\n", subjects.list[i]);
|
||||
|
||||
clear_commit_marks((struct commit *)branch, flags);
|
||||
clear_commit_marks(head, flags);
|
||||
@ -251,15 +252,105 @@ static void shortlog(const char *name, unsigned char *sha1,
|
||||
free_list(&subjects);
|
||||
}
|
||||
|
||||
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int limit = 20, i = 0;
|
||||
int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
|
||||
int limit = 20, i = 0, pos = 0;
|
||||
char line[1024];
|
||||
FILE *in = stdin;
|
||||
const char *sep = "";
|
||||
char *p = line, *sep = "";
|
||||
unsigned char head_sha1[20];
|
||||
const char *current_branch;
|
||||
|
||||
/* get current branch */
|
||||
current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
|
||||
if (!current_branch)
|
||||
die("No current branch");
|
||||
if (!prefixcmp(current_branch, "refs/heads/"))
|
||||
current_branch += 11;
|
||||
|
||||
/* get a line */
|
||||
while (pos < in->len) {
|
||||
int len;
|
||||
char *newline;
|
||||
|
||||
p = in->buf + pos;
|
||||
newline = strchr(p, '\n');
|
||||
len = newline ? newline - p : strlen(p);
|
||||
pos += len + !!newline;
|
||||
i++;
|
||||
p[len] = 0;
|
||||
if (handle_line(p))
|
||||
die ("Error in line %d: %.*s", i, len, p);
|
||||
}
|
||||
|
||||
strbuf_addstr(out, "Merge ");
|
||||
for (i = 0; i < srcs.nr; i++) {
|
||||
struct src_data *src_data = srcs.payload[i];
|
||||
const char *subsep = "";
|
||||
|
||||
strbuf_addstr(out, sep);
|
||||
sep = "; ";
|
||||
|
||||
if (src_data->head_status == 1) {
|
||||
strbuf_addstr(out, srcs.list[i]);
|
||||
continue;
|
||||
}
|
||||
if (src_data->head_status == 3) {
|
||||
subsep = ", ";
|
||||
strbuf_addstr(out, "HEAD");
|
||||
}
|
||||
if (src_data->branch.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
subsep = ", ";
|
||||
print_joined("branch ", "branches ", &src_data->branch,
|
||||
out);
|
||||
}
|
||||
if (src_data->r_branch.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
subsep = ", ";
|
||||
print_joined("remote branch ", "remote branches ",
|
||||
&src_data->r_branch, out);
|
||||
}
|
||||
if (src_data->tag.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
subsep = ", ";
|
||||
print_joined("tag ", "tags ", &src_data->tag, out);
|
||||
}
|
||||
if (src_data->generic.nr) {
|
||||
strbuf_addstr(out, subsep);
|
||||
print_joined("commit ", "commits ", &src_data->generic,
|
||||
out);
|
||||
}
|
||||
if (strcmp(".", srcs.list[i]))
|
||||
strbuf_addf(out, " of %s", srcs.list[i]);
|
||||
}
|
||||
|
||||
if (!strcmp("master", current_branch))
|
||||
strbuf_addch(out, '\n');
|
||||
else
|
||||
strbuf_addf(out, " into %s\n", current_branch);
|
||||
|
||||
if (merge_summary) {
|
||||
struct commit *head;
|
||||
struct rev_info rev;
|
||||
|
||||
head = lookup_commit(head_sha1);
|
||||
init_revisions(&rev, NULL);
|
||||
rev.commit_format = CMIT_FMT_ONELINE;
|
||||
rev.ignore_merges = 1;
|
||||
rev.limited = 1;
|
||||
|
||||
for (i = 0; i < origins.nr; i++)
|
||||
shortlog(origins.list[i], origins.payload[i],
|
||||
head, &rev, limit, out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
FILE *in = stdin;
|
||||
struct strbuf input, output;
|
||||
int ret;
|
||||
|
||||
git_config(fmt_merge_msg_config, NULL);
|
||||
|
||||
while (argc > 1) {
|
||||
@ -288,82 +379,14 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
|
||||
if (argc > 1)
|
||||
usage(fmt_merge_msg_usage);
|
||||
|
||||
/* get current branch */
|
||||
current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
|
||||
if (!current_branch)
|
||||
die("No current branch");
|
||||
if (!prefixcmp(current_branch, "refs/heads/"))
|
||||
current_branch += 11;
|
||||
|
||||
while (fgets(line, sizeof(line), in)) {
|
||||
i++;
|
||||
if (line[0] == 0)
|
||||
continue;
|
||||
if (handle_line(line))
|
||||
die ("Error in line %d: %s", i, line);
|
||||
}
|
||||
|
||||
printf("Merge ");
|
||||
for (i = 0; i < srcs.nr; i++) {
|
||||
struct src_data *src_data = srcs.payload[i];
|
||||
const char *subsep = "";
|
||||
|
||||
printf(sep);
|
||||
sep = "; ";
|
||||
|
||||
if (src_data->head_status == 1) {
|
||||
printf(srcs.list[i]);
|
||||
continue;
|
||||
}
|
||||
if (src_data->head_status == 3) {
|
||||
subsep = ", ";
|
||||
printf("HEAD");
|
||||
}
|
||||
if (src_data->branch.nr) {
|
||||
printf(subsep);
|
||||
subsep = ", ";
|
||||
print_joined("branch ", "branches ", &src_data->branch);
|
||||
}
|
||||
if (src_data->r_branch.nr) {
|
||||
printf(subsep);
|
||||
subsep = ", ";
|
||||
print_joined("remote branch ", "remote branches ",
|
||||
&src_data->r_branch);
|
||||
}
|
||||
if (src_data->tag.nr) {
|
||||
printf(subsep);
|
||||
subsep = ", ";
|
||||
print_joined("tag ", "tags ", &src_data->tag);
|
||||
}
|
||||
if (src_data->generic.nr) {
|
||||
printf(subsep);
|
||||
print_joined("commit ", "commits ", &src_data->generic);
|
||||
}
|
||||
if (strcmp(".", srcs.list[i]))
|
||||
printf(" of %s", srcs.list[i]);
|
||||
}
|
||||
|
||||
if (!strcmp("master", current_branch))
|
||||
putchar('\n');
|
||||
else
|
||||
printf(" into %s\n", current_branch);
|
||||
|
||||
if (merge_summary) {
|
||||
struct commit *head;
|
||||
struct rev_info rev;
|
||||
|
||||
head = lookup_commit(head_sha1);
|
||||
init_revisions(&rev, prefix);
|
||||
rev.commit_format = CMIT_FMT_ONELINE;
|
||||
rev.ignore_merges = 1;
|
||||
rev.limited = 1;
|
||||
|
||||
for (i = 0; i < origins.nr; i++)
|
||||
shortlog(origins.list[i], origins.payload[i],
|
||||
head, &rev, limit);
|
||||
}
|
||||
|
||||
/* No cleanup yet; is standalone anyway */
|
||||
strbuf_init(&input, 0);
|
||||
if (strbuf_read(&input, fileno(in), 0) < 0)
|
||||
die("could not read input file %s", strerror(errno));
|
||||
strbuf_init(&output, 0);
|
||||
|
||||
ret = fmt_merge_msg(merge_summary, &input, &output);
|
||||
if (ret)
|
||||
return ret;
|
||||
printf("%s", output.buf);
|
||||
return 0;
|
||||
}
|
||||
|
@ -42,14 +42,6 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)
|
||||
* - *(int *)commit->object.sha1 set to the virtual id.
|
||||
*/
|
||||
|
||||
static unsigned commit_list_count(const struct commit_list *l)
|
||||
{
|
||||
unsigned c = 0;
|
||||
for (; l; l = l->next )
|
||||
c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
|
||||
{
|
||||
struct commit *commit = xcalloc(1, sizeof(struct commit));
|
||||
|
1156
builtin-merge.c
Normal file
1156
builtin-merge.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -29,30 +29,6 @@ static int list_tree(unsigned char *sha1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_cache_unmerged(void)
|
||||
{
|
||||
int i;
|
||||
struct cache_entry **dst;
|
||||
struct cache_entry *last = NULL;
|
||||
|
||||
read_cache();
|
||||
dst = active_cache;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce)) {
|
||||
remove_name_hash(ce);
|
||||
if (last && !strcmp(ce->name, last->name))
|
||||
continue;
|
||||
cache_tree_invalidate_path(active_cache_tree, ce->name);
|
||||
last = ce;
|
||||
continue;
|
||||
}
|
||||
*dst++ = ce;
|
||||
}
|
||||
active_nr = dst - active_cache;
|
||||
return !!last;
|
||||
}
|
||||
|
||||
static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
|
||||
{
|
||||
struct tree_desc desc;
|
||||
|
@ -29,12 +29,6 @@ static inline int postfixcmp(const char *string, const char *postfix)
|
||||
return strcmp(string + len1 - len2, postfix);
|
||||
}
|
||||
|
||||
static inline const char *skip_prefix(const char *name, const char *prefix)
|
||||
{
|
||||
return !name ? "" :
|
||||
prefixcmp(name, prefix) ? name : name + strlen(prefix);
|
||||
}
|
||||
|
||||
static int opt_parse_track(const struct option *opt, const char *arg, int not)
|
||||
{
|
||||
struct path_list *list = opt->value;
|
||||
@ -182,12 +176,18 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
info->remote = xstrdup(value);
|
||||
} else {
|
||||
char *space = strchr(value, ' ');
|
||||
value = skip_prefix(value, "refs/heads/");
|
||||
const char *ptr = skip_prefix(value, "refs/heads/");
|
||||
if (ptr)
|
||||
value = ptr;
|
||||
while (space) {
|
||||
char *merge;
|
||||
merge = xstrndup(value, space - value);
|
||||
path_list_append(merge, &info->merge);
|
||||
value = skip_prefix(space + 1, "refs/heads/");
|
||||
ptr = skip_prefix(space + 1, "refs/heads/");
|
||||
if (ptr)
|
||||
value = ptr;
|
||||
else
|
||||
value = space + 1;
|
||||
space = strchr(value, ' ');
|
||||
}
|
||||
path_list_append(xstrdup(value), &info->merge);
|
||||
@ -219,7 +219,12 @@ static int handle_one_branch(const char *refname,
|
||||
refspec.dst = (char *)refname;
|
||||
if (!remote_find_tracking(states->remote, &refspec)) {
|
||||
struct path_list_item *item;
|
||||
const char *name = skip_prefix(refspec.src, "refs/heads/");
|
||||
const char *name, *ptr;
|
||||
ptr = skip_prefix(refspec.src, "refs/heads/");
|
||||
if (ptr)
|
||||
name = ptr;
|
||||
else
|
||||
name = refspec.src;
|
||||
/* symbolic refs pointing nowhere were handled already */
|
||||
if ((flags & REF_ISSYMREF) ||
|
||||
unsorted_path_list_has_path(&states->tracked,
|
||||
@ -248,6 +253,7 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
struct path_list *target = &states->tracked;
|
||||
unsigned char sha1[20];
|
||||
void *util = NULL;
|
||||
const char *ptr;
|
||||
|
||||
if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))
|
||||
target = &states->new;
|
||||
@ -256,8 +262,10 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)
|
||||
if (hashcmp(sha1, ref->new_sha1))
|
||||
util = &states;
|
||||
}
|
||||
path_list_append(skip_prefix(ref->name, "refs/heads/"),
|
||||
target)->util = util;
|
||||
ptr = skip_prefix(ref->name, "refs/heads/");
|
||||
if (!ptr)
|
||||
ptr = ref->name;
|
||||
path_list_append(ptr, target)->util = util;
|
||||
}
|
||||
free_refs(fetch_map);
|
||||
|
||||
@ -522,10 +530,15 @@ static int show(int argc, const char **argv)
|
||||
"es" : "");
|
||||
for (i = 0; i < states.remote->push_refspec_nr; i++) {
|
||||
struct refspec *spec = states.remote->push + i;
|
||||
const char *p = "", *q = "";
|
||||
if (spec->src)
|
||||
p = skip_prefix(spec->src, "refs/heads/");
|
||||
if (spec->dst)
|
||||
q = skip_prefix(spec->dst, "refs/heads/");
|
||||
printf(" %s%s%s%s", spec->force ? "+" : "",
|
||||
skip_prefix(spec->src, "refs/heads/"),
|
||||
p ? p : spec->src,
|
||||
spec->dst ? ":" : "",
|
||||
skip_prefix(spec->dst, "refs/heads/"));
|
||||
q ? q : spec->dst);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "git-compat-util.h"
|
||||
#include "strbuf.h"
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
|
||||
extern const char git_version_string[];
|
||||
extern const char git_usage_string[];
|
||||
@ -11,6 +14,10 @@ extern void list_common_cmds_help(void);
|
||||
extern void help_unknown_cmd(const char *cmd);
|
||||
extern void prune_packed_objects(int);
|
||||
extern int read_line_with_nul(char *buf, int size, FILE *file);
|
||||
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
|
||||
struct strbuf *out);
|
||||
extern int commit_tree(const char *msg, unsigned char *tree,
|
||||
struct commit_list *parents, unsigned char *ret);
|
||||
|
||||
extern int cmd_add(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
|
||||
@ -57,6 +64,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
|
||||
|
3
cache.h
3
cache.h
@ -254,6 +254,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
|
||||
|
||||
#define read_cache() read_index(&the_index)
|
||||
#define read_cache_from(path) read_index_from(&the_index, (path))
|
||||
#define read_cache_unmerged() read_index_unmerged(&the_index)
|
||||
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
|
||||
#define discard_cache() discard_index(&the_index)
|
||||
#define unmerged_cache() unmerged_index(&the_index)
|
||||
@ -356,6 +357,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
|
||||
/* Initialize and use the cache information */
|
||||
extern int read_index(struct index_state *);
|
||||
extern int read_index_from(struct index_state *, const char *path);
|
||||
extern int read_index_unmerged(struct index_state *);
|
||||
extern int write_index(const struct index_state *, int newfd);
|
||||
extern int discard_index(struct index_state *);
|
||||
extern int unmerged_index(const struct index_state *);
|
||||
@ -837,5 +839,6 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_
|
||||
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
|
||||
|
||||
char *alias_lookup(const char *alias);
|
||||
int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
143
commit.c
143
commit.c
@ -325,6 +325,14 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *
|
||||
return new_list;
|
||||
}
|
||||
|
||||
unsigned commit_list_count(const struct commit_list *l)
|
||||
{
|
||||
unsigned c = 0;
|
||||
for (; l; l = l->next )
|
||||
c++;
|
||||
return c;
|
||||
}
|
||||
|
||||
void free_commit_list(struct commit_list *list)
|
||||
{
|
||||
while (list) {
|
||||
@ -525,26 +533,34 @@ static struct commit *interesting(struct commit_list *list)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct commit_list *merge_bases(struct commit *one, struct commit *two)
|
||||
static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
|
||||
{
|
||||
struct commit_list *list = NULL;
|
||||
struct commit_list *result = NULL;
|
||||
int i;
|
||||
|
||||
if (one == two)
|
||||
/* We do not mark this even with RESULT so we do not
|
||||
* have to clean it up.
|
||||
*/
|
||||
return commit_list_insert(one, &result);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (one == twos[i])
|
||||
/*
|
||||
* We do not mark this even with RESULT so we do not
|
||||
* have to clean it up.
|
||||
*/
|
||||
return commit_list_insert(one, &result);
|
||||
}
|
||||
|
||||
if (parse_commit(one))
|
||||
return NULL;
|
||||
if (parse_commit(two))
|
||||
return NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (parse_commit(twos[i]))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
one->object.flags |= PARENT1;
|
||||
two->object.flags |= PARENT2;
|
||||
insert_by_date(one, &list);
|
||||
insert_by_date(two, &list);
|
||||
for (i = 0; i < n; i++) {
|
||||
twos[i]->object.flags |= PARENT2;
|
||||
insert_by_date(twos[i], &list);
|
||||
}
|
||||
|
||||
while (interesting(list)) {
|
||||
struct commit *commit;
|
||||
@ -592,21 +608,53 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases(struct commit *one,
|
||||
struct commit *two, int cleanup)
|
||||
struct commit_list *get_octopus_merge_bases(struct commit_list *in)
|
||||
{
|
||||
struct commit_list *i, *j, *k, *ret = NULL;
|
||||
struct commit_list **pptr = &ret;
|
||||
|
||||
for (i = in; i; i = i->next) {
|
||||
if (!ret)
|
||||
pptr = &commit_list_insert(i->item, pptr)->next;
|
||||
else {
|
||||
struct commit_list *new = NULL, *end = NULL;
|
||||
|
||||
for (j = ret; j; j = j->next) {
|
||||
struct commit_list *bases;
|
||||
bases = get_merge_bases(i->item, j->item, 1);
|
||||
if (!new)
|
||||
new = bases;
|
||||
else
|
||||
end->next = bases;
|
||||
for (k = bases; k; k = k->next)
|
||||
end = k;
|
||||
}
|
||||
ret = new;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases_many(struct commit *one,
|
||||
int n,
|
||||
struct commit **twos,
|
||||
int cleanup)
|
||||
{
|
||||
struct commit_list *list;
|
||||
struct commit **rslt;
|
||||
struct commit_list *result;
|
||||
int cnt, i, j;
|
||||
|
||||
result = merge_bases(one, two);
|
||||
if (one == two)
|
||||
return result;
|
||||
result = merge_bases_many(one, n, twos);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (one == twos[i])
|
||||
return result;
|
||||
}
|
||||
if (!result || !result->next) {
|
||||
if (cleanup) {
|
||||
clear_commit_marks(one, all_flags);
|
||||
clear_commit_marks(two, all_flags);
|
||||
for (i = 0; i < n; i++)
|
||||
clear_commit_marks(twos[i], all_flags);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -624,12 +672,13 @@ struct commit_list *get_merge_bases(struct commit *one,
|
||||
free_commit_list(result);
|
||||
|
||||
clear_commit_marks(one, all_flags);
|
||||
clear_commit_marks(two, all_flags);
|
||||
for (i = 0; i < n; i++)
|
||||
clear_commit_marks(twos[i], all_flags);
|
||||
for (i = 0; i < cnt - 1; i++) {
|
||||
for (j = i+1; j < cnt; j++) {
|
||||
if (!rslt[i] || !rslt[j])
|
||||
continue;
|
||||
result = merge_bases(rslt[i], rslt[j]);
|
||||
result = merge_bases_many(rslt[i], 1, &rslt[j]);
|
||||
clear_commit_marks(rslt[i], all_flags);
|
||||
clear_commit_marks(rslt[j], all_flags);
|
||||
for (list = result; list; list = list->next) {
|
||||
@ -651,6 +700,12 @@ struct commit_list *get_merge_bases(struct commit *one,
|
||||
return result;
|
||||
}
|
||||
|
||||
struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
|
||||
int cleanup)
|
||||
{
|
||||
return get_merge_bases_many(one, 1, &two, cleanup);
|
||||
}
|
||||
|
||||
int in_merge_bases(struct commit *commit, struct commit **reference, int num)
|
||||
{
|
||||
struct commit_list *bases, *b;
|
||||
@ -670,3 +725,55 @@ int in_merge_bases(struct commit *commit, struct commit **reference, int num)
|
||||
free_commit_list(bases);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct commit_list *reduce_heads(struct commit_list *heads)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_list *result = NULL, **tail = &result;
|
||||
struct commit **other;
|
||||
size_t num_head, num_other;
|
||||
|
||||
if (!heads)
|
||||
return NULL;
|
||||
|
||||
/* Avoid unnecessary reallocations */
|
||||
for (p = heads, num_head = 0; p; p = p->next)
|
||||
num_head++;
|
||||
other = xcalloc(sizeof(*other), num_head);
|
||||
|
||||
/* For each commit, see if it can be reached by others */
|
||||
for (p = heads; p; p = p->next) {
|
||||
struct commit_list *q, *base;
|
||||
|
||||
/* Do we already have this in the result? */
|
||||
for (q = result; q; q = q->next)
|
||||
if (p->item == q->item)
|
||||
break;
|
||||
if (q)
|
||||
continue;
|
||||
|
||||
num_other = 0;
|
||||
for (q = heads; q; q = q->next) {
|
||||
if (p->item == q->item)
|
||||
continue;
|
||||
other[num_other++] = q->item;
|
||||
}
|
||||
if (num_other)
|
||||
base = get_merge_bases_many(p->item, num_other, other, 1);
|
||||
else
|
||||
base = NULL;
|
||||
/*
|
||||
* If p->item does not have anything common with other
|
||||
* commits, there won't be any merge base. If it is
|
||||
* reachable from some of the others, p->item will be
|
||||
* the merge base. If its history is connected with
|
||||
* others, but p->item is not reachable by others, we
|
||||
* will get something other than p->item back.
|
||||
*/
|
||||
if (!base || (base->item != p->item))
|
||||
tail = &(commit_list_insert(p->item, tail)->next);
|
||||
free_commit_list(base);
|
||||
}
|
||||
free(other);
|
||||
return result;
|
||||
}
|
||||
|
4
commit.h
4
commit.h
@ -41,6 +41,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
|
||||
int parse_commit(struct commit *item);
|
||||
|
||||
struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
|
||||
unsigned commit_list_count(const struct commit_list *l);
|
||||
struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
|
||||
|
||||
void free_commit_list(struct commit_list *list);
|
||||
@ -120,6 +121,7 @@ int read_graft_file(const char *graft_file);
|
||||
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
|
||||
|
||||
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
|
||||
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
|
||||
|
||||
extern int register_shallow(const unsigned char *sha1);
|
||||
extern int unregister_shallow(const unsigned char *sha1);
|
||||
@ -137,4 +139,6 @@ static inline int single_parent(struct commit *commit)
|
||||
return commit->parents && !commit->parents->next;
|
||||
}
|
||||
|
||||
struct commit_list *reduce_heads(struct commit_list *heads);
|
||||
|
||||
#endif /* COMMIT_H */
|
||||
|
@ -157,6 +157,12 @@ extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
|
||||
extern int prefixcmp(const char *str, const char *prefix);
|
||||
extern time_t tm_to_time_t(const struct tm *tm);
|
||||
|
||||
static inline const char *skip_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
#ifdef NO_MMAP
|
||||
|
||||
#ifndef PROT_READ
|
||||
|
54
git.c
54
git.c
@ -127,59 +127,6 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int split_cmdline(char *cmdline, const char ***argv)
|
||||
{
|
||||
int src, dst, count = 0, size = 16;
|
||||
char quoted = 0;
|
||||
|
||||
*argv = xmalloc(sizeof(char*) * size);
|
||||
|
||||
/* split alias_string */
|
||||
(*argv)[count++] = cmdline;
|
||||
for (src = dst = 0; cmdline[src];) {
|
||||
char c = cmdline[src];
|
||||
if (!quoted && isspace(c)) {
|
||||
cmdline[dst++] = 0;
|
||||
while (cmdline[++src]
|
||||
&& isspace(cmdline[src]))
|
||||
; /* skip */
|
||||
if (count >= size) {
|
||||
size += 16;
|
||||
*argv = xrealloc(*argv, sizeof(char*) * size);
|
||||
}
|
||||
(*argv)[count++] = cmdline + dst;
|
||||
} else if(!quoted && (c == '\'' || c == '"')) {
|
||||
quoted = c;
|
||||
src++;
|
||||
} else if (c == quoted) {
|
||||
quoted = 0;
|
||||
src++;
|
||||
} else {
|
||||
if (c == '\\' && quoted != '\'') {
|
||||
src++;
|
||||
c = cmdline[src];
|
||||
if (!c) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("cmdline ends with \\");
|
||||
}
|
||||
}
|
||||
cmdline[dst++] = c;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[dst] = 0;
|
||||
|
||||
if (quoted) {
|
||||
free(*argv);
|
||||
*argv = NULL;
|
||||
return error("unclosed quote");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int handle_alias(int *argcp, const char ***argv)
|
||||
{
|
||||
int envchanged = 0, ret = 0, saved_errno = errno;
|
||||
@ -366,6 +313,7 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "ls-remote", cmd_ls_remote },
|
||||
{ "mailinfo", cmd_mailinfo },
|
||||
{ "mailsplit", cmd_mailsplit },
|
||||
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
||||
{ "merge-file", cmd_merge_file },
|
||||
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
|
||||
|
@ -5,12 +5,6 @@
|
||||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
|
||||
static inline const char *skip_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
static int opterror(const struct option *opt, const char *reason, int flags)
|
||||
{
|
||||
if (flags & OPT_SHORT)
|
||||
|
31
read-cache.c
31
read-cache.c
@ -1410,3 +1410,34 @@ int write_index(const struct index_state *istate, int newfd)
|
||||
}
|
||||
return ce_flush(&c, newfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the index file that is potentially unmerged into given
|
||||
* index_state, dropping any unmerged entries. Returns true is
|
||||
* the index is unmerged. Callers who want to refuse to work
|
||||
* from an unmerged state can call this and check its return value,
|
||||
* instead of calling read_cache().
|
||||
*/
|
||||
int read_index_unmerged(struct index_state *istate)
|
||||
{
|
||||
int i;
|
||||
struct cache_entry **dst;
|
||||
struct cache_entry *last = NULL;
|
||||
|
||||
read_index(istate);
|
||||
dst = istate->cache;
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
struct cache_entry *ce = istate->cache[i];
|
||||
if (ce_stage(ce)) {
|
||||
remove_name_hash(ce);
|
||||
if (last && !strcmp(ce->name, last->name))
|
||||
continue;
|
||||
cache_tree_invalidate_path(istate->cache_tree, ce->name);
|
||||
last = ce;
|
||||
continue;
|
||||
}
|
||||
*dst++ = ce;
|
||||
}
|
||||
istate->cache_nr = dst - istate->cache;
|
||||
return !!last;
|
||||
}
|
||||
|
@ -89,4 +89,8 @@ EOF
|
||||
|
||||
test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
|
||||
|
||||
test_expect_success 'Criss-cross merge fails (-s resolve)' \
|
||||
'git reset --hard A^ &&
|
||||
test_must_fail git merge -s resolve -m "final merge" B'
|
||||
|
||||
test_done
|
||||
|
@ -465,11 +465,51 @@ test_expect_success 'merge log message' '
|
||||
git merge --no-log c2 &&
|
||||
git show -s --pretty=format:%b HEAD >msg.act &&
|
||||
verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
|
||||
|
||||
git merge --log c3 &&
|
||||
git show -s --pretty=format:%b HEAD >msg.act &&
|
||||
verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
|
||||
|
||||
git reset --hard HEAD^ &&
|
||||
git config merge.log yes &&
|
||||
git merge c3 &&
|
||||
git show -s --pretty=format:%b HEAD >msg.act &&
|
||||
verify_diff msg.log msg.act "[OOPS] bad merge log message"
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
|
||||
git reset --hard c1 &&
|
||||
git config branch.master.mergeoptions "" &&
|
||||
test_tick &&
|
||||
git merge c0 c2 c0 c1 &&
|
||||
verify_merge file result.1-5 &&
|
||||
verify_parents $c1 $c2
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_expect_success 'merge c1 with c0, c2, c0, and c1' '
|
||||
git reset --hard c1 &&
|
||||
git config branch.master.mergeoptions "" &&
|
||||
test_tick &&
|
||||
git merge c0 c2 c0 c1 &&
|
||||
verify_merge file result.1-5 &&
|
||||
verify_parents $c1 $c2
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_expect_success 'merge c1 with c1 and c2' '
|
||||
git reset --hard c1 &&
|
||||
git config branch.master.mergeoptions "" &&
|
||||
test_tick &&
|
||||
git merge c1 c2 &&
|
||||
verify_merge file result.1-5 &&
|
||||
verify_parents $c1 $c2
|
||||
'
|
||||
|
||||
test_debug 'gitk --all'
|
||||
|
||||
test_done
|
||||
|
129
t/t7601-merge-pull-config.sh
Executable file
129
t/t7601-merge-pull-config.sh
Executable file
@ -0,0 +1,129 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing pull.* configuration parsing.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 >c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 >c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 >c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2 &&
|
||||
git reset --hard c0 &&
|
||||
echo c3 >c3.c &&
|
||||
git add c3.c &&
|
||||
git commit -m c3 &&
|
||||
git tag c3
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2' '
|
||||
git reset --hard c1 &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test ! -f c2.c &&
|
||||
test ! -f c3.c &&
|
||||
git merge c2 &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
|
||||
git reset --hard c1 &&
|
||||
git config pull.twohead ours &&
|
||||
git merge c2 &&
|
||||
test -f c1.c &&
|
||||
! test -f c2.c
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '
|
||||
git reset --hard c1 &&
|
||||
git config pull.octopus "recursive" &&
|
||||
test_must_fail git merge c2 c3 &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD)"
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '
|
||||
git reset --hard c1 &&
|
||||
git config pull.octopus "recursive octopus" &&
|
||||
git merge c2 c3 &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
|
||||
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
|
||||
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
|
||||
git diff --exit-code &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c &&
|
||||
test -f c3.c
|
||||
'
|
||||
|
||||
conflict_count()
|
||||
{
|
||||
{
|
||||
git diff-files --name-only
|
||||
git ls-files --unmerged
|
||||
} | wc -l
|
||||
}
|
||||
|
||||
# c4 - c5
|
||||
# \ c6
|
||||
#
|
||||
# There are two conflicts here:
|
||||
#
|
||||
# 1) Because foo.c is renamed to bar.c, recursive will handle this,
|
||||
# resolve won't.
|
||||
#
|
||||
# 2) One in conflict.c and that will always fail.
|
||||
|
||||
test_expect_success 'setup conflicted merge' '
|
||||
git reset --hard c0 &&
|
||||
echo A >conflict.c &&
|
||||
git add conflict.c &&
|
||||
echo contents >foo.c &&
|
||||
git add foo.c &&
|
||||
git commit -m c4 &&
|
||||
git tag c4 &&
|
||||
echo B >conflict.c &&
|
||||
git add conflict.c &&
|
||||
git mv foo.c bar.c &&
|
||||
git commit -m c5 &&
|
||||
git tag c5 &&
|
||||
git reset --hard c4 &&
|
||||
echo C >conflict.c &&
|
||||
git add conflict.c &&
|
||||
echo secondline >> foo.c &&
|
||||
git add foo.c &&
|
||||
git commit -m c6 &&
|
||||
git tag c6
|
||||
'
|
||||
|
||||
# First do the merge with resolve and recursive then verify that
|
||||
# recusive is choosen.
|
||||
|
||||
test_expect_success 'merge picks up the best result' '
|
||||
git config pull.twohead "recursive resolve" &&
|
||||
git reset --hard c5 &&
|
||||
git merge -s resolve c6
|
||||
resolve_count=$(conflict_count) &&
|
||||
git reset --hard c5 &&
|
||||
git merge -s recursive c6
|
||||
recursive_count=$(conflict_count) &&
|
||||
git reset --hard c5 &&
|
||||
git merge c6
|
||||
auto_count=$(conflict_count) &&
|
||||
test $auto_count = $recursive_count &&
|
||||
test $auto_count != $resolve_count
|
||||
'
|
||||
|
||||
test_done
|
52
t/t7602-merge-octopus-many.sh
Executable file
52
t/t7602-merge-octopus-many.sh
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing octopus merge with more than 25 refs.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
i=1 &&
|
||||
while test $i -le 30
|
||||
do
|
||||
git reset --hard c0 &&
|
||||
echo c$i > c$i.c &&
|
||||
git add c$i.c &&
|
||||
git commit -m c$i &&
|
||||
git tag c$i &&
|
||||
i=`expr $i + 1` || return 1
|
||||
done
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
|
||||
git reset --hard c1 &&
|
||||
i=2 &&
|
||||
refs="" &&
|
||||
while test $i -le 30
|
||||
do
|
||||
refs="$refs c$i"
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
git merge $refs &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
i=1 &&
|
||||
while test $i -le 30
|
||||
do
|
||||
test "$(git rev-parse c$i)" = "$(git rev-parse HEAD^$i)" &&
|
||||
i=`expr $i + 1` || return 1
|
||||
done &&
|
||||
git diff --exit-code &&
|
||||
i=1 &&
|
||||
while test $i -le 30
|
||||
do
|
||||
test -f c$i.c &&
|
||||
i=`expr $i + 1` || return 1
|
||||
done
|
||||
'
|
||||
|
||||
test_done
|
63
t/t7603-merge-reduce-heads.sh
Executable file
63
t/t7603-merge-reduce-heads.sh
Executable file
@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing octopus merge when reducing parents to independent branches.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
# 0 - 1
|
||||
# \ 2
|
||||
# \ 3
|
||||
# \ 4 - 5
|
||||
#
|
||||
# So 1, 2, 3 and 5 should be kept, 4 should be avoided.
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 > c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2 &&
|
||||
git reset --hard c0 &&
|
||||
echo c3 > c3.c &&
|
||||
git add c3.c &&
|
||||
git commit -m c3 &&
|
||||
git tag c3 &&
|
||||
git reset --hard c0 &&
|
||||
echo c4 > c4.c &&
|
||||
git add c4.c &&
|
||||
git commit -m c4 &&
|
||||
git tag c4 &&
|
||||
echo c5 > c5.c &&
|
||||
git add c5.c &&
|
||||
git commit -m c5 &&
|
||||
git tag c5
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 with c2, c3, c4, c5' '
|
||||
git reset --hard c1 &&
|
||||
git merge c2 c3 c4 c5 &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
|
||||
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
|
||||
test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
|
||||
test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
|
||||
git diff --exit-code &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c &&
|
||||
test -f c3.c &&
|
||||
test -f c4.c &&
|
||||
test -f c5.c
|
||||
'
|
||||
|
||||
test_done
|
37
t/t7604-merge-custom-message.sh
Executable file
37
t/t7604-merge-custom-message.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing merge when using a custom message for the merge commit.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 > c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
custom message
|
||||
|
||||
Merge commit 'c2'
|
||||
EOF
|
||||
test_expect_success 'merge c2 with a custom message' '
|
||||
git reset --hard c1 &&
|
||||
git merge -m "custom message" c2 &&
|
||||
git cat-file commit HEAD | sed -e "1,/^$/d" > actual &&
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
test_done
|
46
t/t7605-merge-resolve.sh
Executable file
46
t/t7605-merge-resolve.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git-merge
|
||||
|
||||
Testing the resolve strategy.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
echo c0 > c0.c &&
|
||||
git add c0.c &&
|
||||
git commit -m c0 &&
|
||||
git tag c0 &&
|
||||
echo c1 > c1.c &&
|
||||
git add c1.c &&
|
||||
git commit -m c1 &&
|
||||
git tag c1 &&
|
||||
git reset --hard c0 &&
|
||||
echo c2 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c2 &&
|
||||
git tag c2 &&
|
||||
git reset --hard c0 &&
|
||||
echo c3 > c2.c &&
|
||||
git add c2.c &&
|
||||
git commit -m c3 &&
|
||||
git tag c3
|
||||
'
|
||||
|
||||
test_expect_success 'merge c1 to c2' '
|
||||
git reset --hard c1 &&
|
||||
git merge -s resolve c2 &&
|
||||
test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
|
||||
test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
|
||||
test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
|
||||
git diff --exit-code &&
|
||||
test -f c0.c &&
|
||||
test -f c1.c &&
|
||||
test -f c2.c
|
||||
'
|
||||
|
||||
test_expect_success 'merge c2 to c3 (fails)' '
|
||||
git reset --hard c2 &&
|
||||
test_must_fail git merge -s resolve c3
|
||||
'
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user