{"thread":{"id":"14186","subject":"[PATCH 08/15] Add new test to ensure git-merge handles more than 25 refs.","startedAt":"2008-06-27T16:21:53Z","lastAt":"2008-07-05T01:49:03Z","messageCount":50,"participants":["Miklos Vajna","Junio C Hamano","Johannes Schindelin","Jakub Narebski","Olivier Marin","Alex Riesen","Sverre Rabbelier","Mike Ralphson"],"isPatch":true,"patchVersion":1,"patchTotal":15},"messages":[{"id":"81422","messageId":"cover.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":null,"subject":"[PATCH 00/15] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:53Z","receivedAt":"2008-06-27T16:21:53Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Hello,\n\nDscho gave me a detailed review on builtin-merge, so I'm sending an\nupdated series.\n\nIt's based on 49646c5 in git.git, so hopefully it includes all the work\nJunio did recently in 'pu'. (PARSE_OPT_ARGV0_IS_AN_OPTION removal, etc.)\n\nChanges:\n\n* \"git-fmt-merge-msg: make it usable from other builtins\"\n  - Small cleanup.\n\n* \"Build in merge\"\n  - When a custom message was given for a merge, the result was not\n    identical to the one git-merge.sh had. I added a testcase for this\n    and fixed up builtin-merge to pass the test.\n  - Memleak fix in restore_state()\n  - Cleanup in finish_up_to_date() and squash_message()\n  - run_hook() now first checks if the executable bit is set and returns\n    immediately if not.\n  - Make merge_name() a bit more readable, like *(ptr+1) -> ptr[1]. Also\n    fixed a missing initialization in this function.\n  - split_merge_strategies(): while (1) -> for (;;)\n  - Simplified path_list_append_strategy() by removing unnecessary\n    parameters.\n  - cmd_merge(): Added more comments to make the code more readable,\n    fixed a memory leak, optimized the case when --no-commit is used.\n  - merge_one_remote(): Fixed a bug which for example caused a segfault\n    when building with -fno-inline.\n  - Fixed up color handling when showing the diff after a merge, as\n    noticed by Olivier Marin.\n  - Fixed up the \"Updating foo..bar\" message which was like \"Updating\n    bar..bar\", as pointed out by Olivier Marin.\n\n* Dscho's two patches: These introduces strbuf_initf() which can be used\n  instead of strbuf_init() and strbuf_addf(). Modified builtin-merge.c\n  to use strbuf_initf() where possible.\n\n* Extended t7601-merge-pull-config.sh to make sure git-merge picks up\n  the best strategy when no strategy can handle the merge without\n  conflicts.\n\nThe \"interdiff\" is available via git diff 49646c5..d1c62b2 in\ngit://repo.or.cz/git/vmiklos.git.\n\nI'm sending the whole series to avoid complexity, but in fact I only\nchanged the following patches:\n\n* \"git-fmt-merge-msg: make it usable from other builtins\"\n* \"Add new test case to ensure git-merge prepends the custom merge\n  message\"\n* \"Build in merge\"\n\nJohannes Schindelin (2):\n  Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()\n  strbuf_vaddf(): support %*s, too\n\nJunio C Hamano (2):\n  Introduce get_merge_bases_many()\n  Introduce reduce_heads()\n\nMiklos Vajna (11):\n  Move split_cmdline() to alias.c\n  Move commit_list_count() to commit.c\n  Move parse-options's skip_prefix() to git-compat-util.h\n  Add new test to ensure git-merge handles pull.twohead and\n    pull.octopus\n  Move read_cache_unmerged() to read-cache.c\n  git-fmt-merge-msg: make it usable from other builtins\n  Introduce get_octopus_merge_bases() in commit.c\n  Add new test to ensure git-merge handles more than 25 refs.\n  Add new test case to ensure git-merge reduces octopus parents when\n    possible\n  Add new test case to ensure git-merge prepends the custom merge\n    message\n  Build in merge\n\n .gitignore                                    |    1 +\n Makefile                                      |    6 +-\n alias.c                                       |   54 ++\n builtin-fmt-merge-msg.c                       |  155 ++--\n builtin-merge-recursive.c                     |    8 -\n builtin-merge.c                               | 1143 +++++++++++++++++++++++++\n builtin-read-tree.c                           |   24 -\n builtin-remote.c                              |   39 +-\n builtin.h                                     |    4 +\n cache.h                                       |    3 +\n commit.c                                      |  136 +++-\n commit.h                                      |    4 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git-compat-util.h                             |    6 +\n git.c                                         |   54 +--\n parse-options.c                               |    6 -\n read-cache.c                                  |   31 +\n strbuf.c                                      |  140 +++-\n strbuf.h                                      |    3 +\n t/t0000-basic.sh                              |    8 +\n t/t7601-merge-pull-config.sh                  |  129 +++\n t/t7602-merge-octopus-many.sh                 |   52 ++\n t/t7603-merge-reduce-heads.sh                 |   63 ++\n t/t7604-merge-custom-message.sh               |   37 +\n test-strbuf.c                                 |   17 +\n 25 files changed, 1917 insertions(+), 206 deletions(-)\n create mode 100644 builtin-merge.c\n rename git-merge.sh => contrib/examples/git-merge.sh (100%)\n create mode 100755 t/t7601-merge-pull-config.sh\n create mode 100755 t/t7602-merge-octopus-many.sh\n create mode 100755 t/t7603-merge-reduce-heads.sh\n create mode 100755 t/t7604-merge-custom-message.sh\n create mode 100644 test-strbuf.c\n"},{"id":"81424","messageId":"58b2c36de6a6f51a562da303695482bea567f4bf.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"cover.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 01/15] Move split_cmdline() to alias.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:54Z","receivedAt":"2008-06-27T16:21:54Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"split_cmdline() is currently used for aliases only, but later it can be\nuseful for other builtins as well. Move it to alias.c for now,\nindicating that originally it's for aliases, but we'll have it in libgit\nthis way.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n alias.c |   54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n cache.h |    1 +\n git.c   |   53 -----------------------------------------------------\n 3 files changed, 55 insertions(+), 53 deletions(-)\n\ndiff --git a/alias.c b/alias.c\nindex 995f3e6..ccb1108 100644\n--- a/alias.c\n+++ b/alias.c\n@@ -21,3 +21,57 @@ char *alias_lookup(const char *alias)\n \tgit_config(alias_lookup_cb, NULL);\n \treturn alias_val;\n }\n+\n+int split_cmdline(char *cmdline, const char ***argv)\n+{\n+\tint src, dst, count = 0, size = 16;\n+\tchar quoted = 0;\n+\n+\t*argv = xmalloc(sizeof(char*) * size);\n+\n+\t/* split alias_string */\n+\t(*argv)[count++] = cmdline;\n+\tfor (src = dst = 0; cmdline[src];) {\n+\t\tchar c = cmdline[src];\n+\t\tif (!quoted && isspace(c)) {\n+\t\t\tcmdline[dst++] = 0;\n+\t\t\twhile (cmdline[++src]\n+\t\t\t\t\t&& isspace(cmdline[src]))\n+\t\t\t\t; /* skip */\n+\t\t\tif (count >= size) {\n+\t\t\t\tsize += 16;\n+\t\t\t\t*argv = xrealloc(*argv, sizeof(char*) * size);\n+\t\t\t}\n+\t\t\t(*argv)[count++] = cmdline + dst;\n+\t\t} else if (!quoted && (c == '\\'' || c == '\"')) {\n+\t\t\tquoted = c;\n+\t\t\tsrc++;\n+\t\t} else if (c == quoted) {\n+\t\t\tquoted = 0;\n+\t\t\tsrc++;\n+\t\t} else {\n+\t\t\tif (c == '\\\\' && quoted != '\\'') {\n+\t\t\t\tsrc++;\n+\t\t\t\tc = cmdline[src];\n+\t\t\t\tif (!c) {\n+\t\t\t\t\tfree(*argv);\n+\t\t\t\t\t*argv = NULL;\n+\t\t\t\t\treturn error(\"cmdline ends with \\\\\");\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tcmdline[dst++] = c;\n+\t\t\tsrc++;\n+\t\t}\n+\t}\n+\n+\tcmdline[dst] = 0;\n+\n+\tif (quoted) {\n+\t\tfree(*argv);\n+\t\t*argv = NULL;\n+\t\treturn error(\"unclosed quote\");\n+\t}\n+\n+\treturn count;\n+}\n+\ndiff --git a/cache.h b/cache.h\nindex c8954ef..d5463d8 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -830,5 +830,6 @@ int report_path_error(const char *ps_matched, const char **pathspec, int prefix_\n void overlay_tree_on_cache(const char *tree_name, const char *prefix);\n \n char *alias_lookup(const char *alias);\n+int split_cmdline(char *cmdline, const char ***argv);\n \n #endif /* CACHE_H */\ndiff --git a/git.c b/git.c\nindex 59f0fcc..2fbe96b 100644\n--- a/git.c\n+++ b/git.c\n@@ -90,59 +90,6 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)\n \treturn handled;\n }\n \n-static int split_cmdline(char *cmdline, const char ***argv)\n-{\n-\tint src, dst, count = 0, size = 16;\n-\tchar quoted = 0;\n-\n-\t*argv = xmalloc(sizeof(char*) * size);\n-\n-\t/* split alias_string */\n-\t(*argv)[count++] = cmdline;\n-\tfor (src = dst = 0; cmdline[src];) {\n-\t\tchar c = cmdline[src];\n-\t\tif (!quoted && isspace(c)) {\n-\t\t\tcmdline[dst++] = 0;\n-\t\t\twhile (cmdline[++src]\n-\t\t\t\t\t&& isspace(cmdline[src]))\n-\t\t\t\t; /* skip */\n-\t\t\tif (count >= size) {\n-\t\t\t\tsize += 16;\n-\t\t\t\t*argv = xrealloc(*argv, sizeof(char*) * size);\n-\t\t\t}\n-\t\t\t(*argv)[count++] = cmdline + dst;\n-\t\t} else if(!quoted && (c == '\\'' || c == '\"')) {\n-\t\t\tquoted = c;\n-\t\t\tsrc++;\n-\t\t} else if (c == quoted) {\n-\t\t\tquoted = 0;\n-\t\t\tsrc++;\n-\t\t} else {\n-\t\t\tif (c == '\\\\' && quoted != '\\'') {\n-\t\t\t\tsrc++;\n-\t\t\t\tc = cmdline[src];\n-\t\t\t\tif (!c) {\n-\t\t\t\t\tfree(*argv);\n-\t\t\t\t\t*argv = NULL;\n-\t\t\t\t\treturn error(\"cmdline ends with \\\\\");\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tcmdline[dst++] = c;\n-\t\t\tsrc++;\n-\t\t}\n-\t}\n-\n-\tcmdline[dst] = 0;\n-\n-\tif (quoted) {\n-\t\tfree(*argv);\n-\t\t*argv = NULL;\n-\t\treturn error(\"unclosed quote\");\n-\t}\n-\n-\treturn count;\n-}\n-\n static int handle_alias(int *argcp, const char ***argv)\n {\n \tint envchanged = 0, ret = 0, saved_errno = errno;\n-- \n1.5.6\n"},{"id":"81426","messageId":"4a9a3a34d8c42b60f002acb20083cb9e187c262f.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"58b2c36de6a6f51a562da303695482bea567f4bf.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 02/15] Move commit_list_count() to commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:55Z","receivedAt":"2008-06-27T16:21:55Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This function is useful outside builtin-merge-recursive, for example in\nbuiltin-merge.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-merge-recursive.c |    8 --------\n commit.c                  |    8 ++++++++\n commit.h                  |    1 +\n 3 files changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c\nindex 43bf6aa..3731853 100644\n--- a/builtin-merge-recursive.c\n+++ b/builtin-merge-recursive.c\n@@ -42,14 +42,6 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two)\n  * - *(int *)commit->object.sha1 set to the virtual id.\n  */\n \n-static unsigned commit_list_count(const struct commit_list *l)\n-{\n-\tunsigned c = 0;\n-\tfor (; l; l = l->next )\n-\t\tc++;\n-\treturn c;\n-}\n-\n static struct commit *make_virtual_commit(struct tree *tree, const char *comment)\n {\n \tstruct commit *commit = xcalloc(1, sizeof(struct commit));\ndiff --git a/commit.c b/commit.c\nindex e2d8624..bbf9c75 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -325,6 +325,14 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *\n \treturn new_list;\n }\n \n+unsigned commit_list_count(const struct commit_list *l)\n+{\n+\tunsigned c = 0;\n+\tfor (; l; l = l->next )\n+\t\tc++;\n+\treturn c;\n+}\n+\n void free_commit_list(struct commit_list *list)\n {\n \twhile (list) {\ndiff --git a/commit.h b/commit.h\nindex 2d94d41..7f8c5ee 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -41,6 +41,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);\n int parse_commit(struct commit *item);\n \n struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);\n+unsigned commit_list_count(const struct commit_list *l);\n struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);\n \n void free_commit_list(struct commit_list *list);\n-- \n1.5.6\n"},{"id":"81420","messageId":"876e733753999f116bfd975d9a262a5c1b3855a1.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"4a9a3a34d8c42b60f002acb20083cb9e187c262f.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 03/15] Move parse-options's skip_prefix() to git-compat-util.h","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:56Z","receivedAt":"2008-06-27T16:21:56Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"builtin-remote.c and parse-options.c both have a skip_prefix() function,\nfor the same purpose. Move parse-options's one to git-compat-util.h and\nlet builtin-remote use it as well.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-remote.c  |   39 ++++++++++++++++++++++++++-------------\n git-compat-util.h |    6 ++++++\n parse-options.c   |    6 ------\n 3 files changed, 32 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 145dd85..1491354 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -29,12 +29,6 @@ static inline int postfixcmp(const char *string, const char *postfix)\n \treturn strcmp(string + len1 - len2, postfix);\n }\n \n-static inline const char *skip_prefix(const char *name, const char *prefix)\n-{\n-\treturn !name ? \"\" :\n-\t\tprefixcmp(name, prefix) ?  name : name + strlen(prefix);\n-}\n-\n static int opt_parse_track(const struct option *opt, const char *arg, int not)\n {\n \tstruct path_list *list = opt->value;\n@@ -182,12 +176,18 @@ static int config_read_branches(const char *key, const char *value, void *cb)\n \t\t\tinfo->remote = xstrdup(value);\n \t\t} else {\n \t\t\tchar *space = strchr(value, ' ');\n-\t\t\tvalue = skip_prefix(value, \"refs/heads/\");\n+\t\t\tconst char *ptr = skip_prefix(value, \"refs/heads/\");\n+\t\t\tif (ptr)\n+\t\t\t\tvalue = ptr;\n \t\t\twhile (space) {\n \t\t\t\tchar *merge;\n \t\t\t\tmerge = xstrndup(value, space - value);\n \t\t\t\tpath_list_append(merge, &info->merge);\n-\t\t\t\tvalue = skip_prefix(space + 1, \"refs/heads/\");\n+\t\t\t\tptr = skip_prefix(space + 1, \"refs/heads/\");\n+\t\t\t\tif (ptr)\n+\t\t\t\t\tvalue = ptr;\n+\t\t\t\telse\n+\t\t\t\t\tvalue = space + 1;\n \t\t\t\tspace = strchr(value, ' ');\n \t\t\t}\n \t\t\tpath_list_append(xstrdup(value), &info->merge);\n@@ -219,7 +219,12 @@ static int handle_one_branch(const char *refname,\n \trefspec.dst = (char *)refname;\n \tif (!remote_find_tracking(states->remote, &refspec)) {\n \t\tstruct path_list_item *item;\n-\t\tconst char *name = skip_prefix(refspec.src, \"refs/heads/\");\n+\t\tconst char *name, *ptr;\n+\t\tptr = skip_prefix(refspec.src, \"refs/heads/\");\n+\t\tif (ptr)\n+\t\t\tname = ptr;\n+\t\telse\n+\t\t\tname = refspec.src;\n \t\t/* symbolic refs pointing nowhere were handled already */\n \t\tif ((flags & REF_ISSYMREF) ||\n \t\t\t\tunsorted_path_list_has_path(&states->tracked,\n@@ -248,6 +253,7 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)\n \t\tstruct path_list *target = &states->tracked;\n \t\tunsigned char sha1[20];\n \t\tvoid *util = NULL;\n+\t\tconst char *ptr;\n \n \t\tif (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))\n \t\t\ttarget = &states->new;\n@@ -256,8 +262,10 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)\n \t\t\tif (hashcmp(sha1, ref->new_sha1))\n \t\t\t\tutil = &states;\n \t\t}\n-\t\tpath_list_append(skip_prefix(ref->name, \"refs/heads/\"),\n-\t\t\t\ttarget)->util = util;\n+\t\tptr = skip_prefix(ref->name, \"refs/heads/\");\n+\t\tif (!ptr)\n+\t\t\tptr = ref->name;\n+\t\tpath_list_append(ptr, target)->util = util;\n \t}\n \tfree_refs(fetch_map);\n \n@@ -522,10 +530,15 @@ static int show(int argc, const char **argv)\n \t\t\t\t\t\"es\" : \"\");\n \t\t\tfor (i = 0; i < states.remote->push_refspec_nr; i++) {\n \t\t\t\tstruct refspec *spec = states.remote->push + i;\n+\t\t\t\tconst char *p = \"\", *q = \"\";\n+\t\t\t\tif (spec->src)\n+\t\t\t\t\tp = skip_prefix(spec->src, \"refs/heads/\");\n+\t\t\t\tif (spec->dst)\n+\t\t\t\t\tq = skip_prefix(spec->dst, \"refs/heads/\");\n \t\t\t\tprintf(\" %s%s%s%s\", spec->force ? \"+\" : \"\",\n-\t\t\t\t\tskip_prefix(spec->src, \"refs/heads/\"),\n+\t\t\t\t\tp ? p : spec->src,\n \t\t\t\t\tspec->dst ? \":\" : \"\",\n-\t\t\t\t\tskip_prefix(spec->dst, \"refs/heads/\"));\n+\t\t\t\t\tq ? q : spec->dst);\n \t\t\t}\n \t\t\tprintf(\"\\n\");\n \t\t}\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 6f94a81..31edc98 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -127,6 +127,12 @@ extern void set_warn_routine(void (*routine)(const char *warn, va_list params));\n \n extern int prefixcmp(const char *str, const char *prefix);\n \n+static inline const char *skip_prefix(const char *str, const char *prefix)\n+{\n+\tsize_t len = strlen(prefix);\n+\treturn strncmp(str, prefix, len) ? NULL : str + len;\n+}\n+\n #ifdef NO_MMAP\n \n #ifndef PROT_READ\ndiff --git a/parse-options.c b/parse-options.c\nindex b8bde2b..bbc3ca4 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -22,12 +22,6 @@ static inline const char *get_arg(struct optparse_t *p)\n \treturn *++p->argv;\n }\n \n-static inline const char *skip_prefix(const char *str, const char *prefix)\n-{\n-\tsize_t len = strlen(prefix);\n-\treturn strncmp(str, prefix, len) ? NULL : str + len;\n-}\n-\n static int opterror(const struct option *opt, const char *reason, int flags)\n {\n \tif (flags & OPT_SHORT)\n-- \n1.5.6\n"},{"id":"81428","messageId":"99b931de28ab3017269d15c334c83e242d025489.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"876e733753999f116bfd975d9a262a5c1b3855a1.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 04/15] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:57Z","receivedAt":"2008-06-27T16:21:57Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Test if the given strategies are used and test the case when multiple\nstrategies are configured using a space separated list.\n\nAlso test if the best strategy is picked if none is specified.  This is\ndone by adding a simple test case where recursive detects a rename, but\nresolve does not, and verify that finally merge will pick up the\nprevious.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n t/t7601-merge-pull-config.sh |  129 ++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 129 insertions(+), 0 deletions(-)\n create mode 100755 t/t7601-merge-pull-config.sh\n\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nnew file mode 100755\nindex 0000000..be622f3\n--- /dev/null\n+++ b/t/t7601-merge-pull-config.sh\n@@ -0,0 +1,129 @@\n+#!/bin/sh\n+\n+test_description='git-merge\n+\n+Testing pull.* configuration parsing.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo c0 >c0.c &&\n+\tgit add c0.c &&\n+\tgit commit -m c0 &&\n+\tgit tag c0 &&\n+\techo c1 >c1.c &&\n+\tgit add c1.c &&\n+\tgit commit -m c1 &&\n+\tgit tag c1 &&\n+\tgit reset --hard c0 &&\n+\techo c2 >c2.c &&\n+\tgit add c2.c &&\n+\tgit commit -m c2 &&\n+\tgit tag c2\n+\tgit reset --hard c0 &&\n+\techo c3 >c3.c &&\n+\tgit add c3.c &&\n+\tgit commit -m c3 &&\n+\tgit tag c3\n+'\n+\n+test_expect_success 'merge c1 with c2' '\n+\tgit reset --hard c1 &&\n+\ttest -f c0.c &&\n+\ttest -f c1.c &&\n+\ttest ! -f c2.c &&\n+\ttest ! -f c3.c &&\n+\tgit merge c2 &&\n+\ttest -f c1.c &&\n+\ttest -f c2.c\n+'\n+\n+test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '\n+\tgit reset --hard c1 &&\n+\tgit config pull.twohead ours &&\n+\tgit merge c2 &&\n+\ttest -f c1.c &&\n+\t! test -f c2.c\n+'\n+\n+test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '\n+\tgit reset --hard c1 &&\n+\tgit config pull.octopus \"recursive\" &&\n+\ttest_must_fail git merge c2 c3 &&\n+\ttest \"$(git rev-parse c1)\" = \"$(git rev-parse HEAD)\"\n+'\n+\n+test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '\n+\tgit reset --hard c1 &&\n+\tgit config pull.octopus \"recursive octopus\" &&\n+\tgit merge c2 c3 &&\n+\ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\n+\ttest \"$(git rev-parse c1)\" = \"$(git rev-parse HEAD^1)\" &&\n+\ttest \"$(git rev-parse c2)\" = \"$(git rev-parse HEAD^2)\" &&\n+\ttest \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\"\n+\ttest \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\" &&\n+\tgit diff --exit-code &&\n+\ttest -f c0.c &&\n+\ttest -f c1.c &&\n+\ttest -f c2.c &&\n+\ttest -f c3.c\n+'\n+\n+conflict_count()\n+{\n+\teval $1=`{\n+\t\tgit diff-files --name-only\n+\t\tgit ls-files --unmerged\n+\t} | wc -l`\n+}\n+\n+# c4 - c5\n+#    \\ c6\n+#\n+# There are two conflicts here:\n+#\n+# 1) Because foo.c is renamed to bar.c, recursive will handle this,\n+# resolve won't.\n+#\n+# 2) One in conflict.c and that will always fail.\n+\n+test_expect_success 'setup conflicted merge' '\n+\tgit reset --hard c0 &&\n+\techo A >conflict.c &&\n+\tgit add conflict.c &&\n+\techo contents >foo.c &&\n+\tgit add foo.c &&\n+\tgit commit -m c4 &&\n+\tgit tag c4 &&\n+\techo B >conflict.c &&\n+\tgit add conflict.c &&\n+\tgit mv foo.c bar.c &&\n+\tgit commit -m c5 &&\n+\tgit tag c5 &&\n+\tgit reset --hard c4 &&\n+\techo C >conflict.c &&\n+\tgit add conflict.c &&\n+\techo secondline >> foo.c &&\n+\tgit add foo.c &&\n+\tgit commit -m c6 &&\n+\tgit tag c6\n+'\n+\n+# First do the merge with resolve and recursive then verify that\n+# recusive is choosen.\n+\n+test_expect_success 'merge picks up the best result' '\n+\tgit config pull.twohead \"recursive resolve\" &&\n+\tgit reset --hard c5 &&\n+\tgit merge -s resolve c6\n+\tconflict_count resolve_count &&\n+\tgit reset --hard c5 &&\n+\tgit merge -s recursive c6\n+\tconflict_count recursive_count &&\n+\tgit reset --hard c5 &&\n+\tgit merge c6\n+\tconflict_count auto_count &&\n+\ttest \"$auto_count\" = \"$recursive_count\"\n+'\n+\n+test_done\n-- \n1.5.6\n"},{"id":"81421","messageId":"5c33ca94d6edb203bd2fbbb949f834b3771470ed.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"99b931de28ab3017269d15c334c83e242d025489.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 05/15] Move read_cache_unmerged() to read-cache.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:58Z","receivedAt":"2008-06-27T16:21:58Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"builtin-read-tree has a read_cache_unmerged() which is useful for other\nbuiltins, for example builtin-merge uses it as well. Move it to\nread-cache.c to avoid code duplication.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-read-tree.c |   24 ------------------------\n cache.h             |    2 ++\n read-cache.c        |   31 +++++++++++++++++++++++++++++++\n 3 files changed, 33 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin-read-tree.c b/builtin-read-tree.c\nindex 5a09e17..72a6de3 100644\n--- a/builtin-read-tree.c\n+++ b/builtin-read-tree.c\n@@ -29,30 +29,6 @@ static int list_tree(unsigned char *sha1)\n \treturn 0;\n }\n \n-static int read_cache_unmerged(void)\n-{\n-\tint i;\n-\tstruct cache_entry **dst;\n-\tstruct cache_entry *last = NULL;\n-\n-\tread_cache();\n-\tdst = active_cache;\n-\tfor (i = 0; i < active_nr; i++) {\n-\t\tstruct cache_entry *ce = active_cache[i];\n-\t\tif (ce_stage(ce)) {\n-\t\t\tremove_name_hash(ce);\n-\t\t\tif (last && !strcmp(ce->name, last->name))\n-\t\t\t\tcontinue;\n-\t\t\tcache_tree_invalidate_path(active_cache_tree, ce->name);\n-\t\t\tlast = ce;\n-\t\t\tcontinue;\n-\t\t}\n-\t\t*dst++ = ce;\n-\t}\n-\tactive_nr = dst - active_cache;\n-\treturn !!last;\n-}\n-\n static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)\n {\n \tstruct tree_desc desc;\ndiff --git a/cache.h b/cache.h\nindex d5463d8..7fb6c70 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -254,6 +254,7 @@ static inline void remove_name_hash(struct cache_entry *ce)\n \n #define read_cache() read_index(&the_index)\n #define read_cache_from(path) read_index_from(&the_index, (path))\n+#define read_cache_unmerged() read_index_unmerged(&the_index)\n #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))\n #define discard_cache() discard_index(&the_index)\n #define unmerged_cache() unmerged_index(&the_index)\n@@ -356,6 +357,7 @@ extern int init_db(const char *template_dir, unsigned int flags);\n /* Initialize and use the cache information */\n extern int read_index(struct index_state *);\n extern int read_index_from(struct index_state *, const char *path);\n+extern int read_index_unmerged(struct index_state *);\n extern int write_index(const struct index_state *, int newfd);\n extern int discard_index(struct index_state *);\n extern int unmerged_index(const struct index_state *);\ndiff --git a/read-cache.c b/read-cache.c\nindex f83de8c..d801f9d 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1410,3 +1410,34 @@ int write_index(const struct index_state *istate, int newfd)\n \t}\n \treturn ce_flush(&c, newfd);\n }\n+\n+/*\n+ * Read the index file that is potentially unmerged into given\n+ * index_state, dropping any unmerged entries.  Returns true is\n+ * the index is unmerged.  Callers who want to refuse to work\n+ * from an unmerged state can call this and check its return value,\n+ * instead of calling read_cache().\n+ */\n+int read_index_unmerged(struct index_state *istate)\n+{\n+\tint i;\n+\tstruct cache_entry **dst;\n+\tstruct cache_entry *last = NULL;\n+\n+\tread_index(istate);\n+\tdst = istate->cache;\n+\tfor (i = 0; i < istate->cache_nr; i++) {\n+\t\tstruct cache_entry *ce = istate->cache[i];\n+\t\tif (ce_stage(ce)) {\n+\t\t\tremove_name_hash(ce);\n+\t\t\tif (last && !strcmp(ce->name, last->name))\n+\t\t\t\tcontinue;\n+\t\t\tcache_tree_invalidate_path(istate->cache_tree, ce->name);\n+\t\t\tlast = ce;\n+\t\t\tcontinue;\n+\t\t}\n+\t\t*dst++ = ce;\n+\t}\n+\tistate->cache_nr = dst - istate->cache;\n+\treturn !!last;\n+}\n-- \n1.5.6\n"},{"id":"81418","messageId":"25a8ffde9783638080d2b998d8f2579e6ed6c8e7.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"5c33ca94d6edb203bd2fbbb949f834b3771470ed.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 06/15] git-fmt-merge-msg: make it usable from other builtins","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:21:59Z","receivedAt":"2008-06-27T16:21:59Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Move all functionality (except config and option parsing) from\ncmd_fmt_merge_msg() to fmt_merge_msg(), so that other builtins can use\nit without a child process.\n\nAll functions have been changed to use strbufs, and now only\ncmd_fmt_merge_msg() reads directly from a file / writes anything to\nstdout.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n builtin-fmt-merge-msg.c |  155 +++++++++++++++++++++++++++--------------------\n builtin.h               |    3 +\n 2 files changed, 92 insertions(+), 66 deletions(-)\n\ndiff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c\nindex b892621..dbd7d2d 100644\n--- a/builtin-fmt-merge-msg.c\n+++ b/builtin-fmt-merge-msg.c\n@@ -159,23 +159,24 @@ static int handle_line(char *line)\n }\n \n static void print_joined(const char *singular, const char *plural,\n-\t\tstruct list *list)\n+\t\tstruct list *list, struct strbuf *out)\n {\n \tif (list->nr == 0)\n \t\treturn;\n \tif (list->nr == 1) {\n-\t\tprintf(\"%s%s\", singular, list->list[0]);\n+\t\tstrbuf_addf(out, \"%s%s\", singular, list->list[0]);\n \t} else {\n \t\tint i;\n-\t\tprintf(\"%s\", plural);\n+\t\tstrbuf_addstr(out, plural);\n \t\tfor (i = 0; i < list->nr - 1; i++)\n-\t\t\tprintf(\"%s%s\", i > 0 ? \", \" : \"\", list->list[i]);\n-\t\tprintf(\" and %s\", list->list[list->nr - 1]);\n+\t\t\tstrbuf_addf(out, \"%s%s\", i > 0 ? \", \" : \"\", list->list[i]);\n+\t\tstrbuf_addf(out, \" and %s\", list->list[list->nr - 1]);\n \t}\n }\n \n static void shortlog(const char *name, unsigned char *sha1,\n-\t\tstruct commit *head, struct rev_info *rev, int limit)\n+\t\tstruct commit *head, struct rev_info *rev, int limit,\n+\t\tstruct strbuf *out)\n {\n \tint i, count = 0;\n \tstruct commit *commit;\n@@ -232,15 +233,15 @@ static void shortlog(const char *name, unsigned char *sha1,\n \t}\n \n \tif (count > limit)\n-\t\tprintf(\"\\n* %s: (%d commits)\\n\", name, count);\n+\t\tstrbuf_addf(out, \"\\n* %s: (%d commits)\\n\", name, count);\n \telse\n-\t\tprintf(\"\\n* %s:\\n\", name);\n+\t\tstrbuf_addf(out, \"\\n* %s:\\n\", name);\n \n \tfor (i = 0; i < subjects.nr; i++)\n \t\tif (i >= limit)\n-\t\t\tprintf(\"  ...\\n\");\n+\t\t\tstrbuf_addf(out, \"  ...\\n\");\n \t\telse\n-\t\t\tprintf(\"  %s\\n\", subjects.list[i]);\n+\t\t\tstrbuf_addf(out, \"  %s\\n\", subjects.list[i]);\n \n \tclear_commit_marks((struct commit *)branch, flags);\n \tclear_commit_marks(head, flags);\n@@ -251,43 +252,13 @@ static void shortlog(const char *name, unsigned char *sha1,\n \tfree_list(&subjects);\n }\n \n-int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)\n-{\n-\tint limit = 20, i = 0;\n+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {\n+\tint limit = 20, i = 0, pos = 0;\n \tchar line[1024];\n-\tFILE *in = stdin;\n-\tconst char *sep = \"\";\n+\tchar *p = line, *sep = \"\";\n \tunsigned char head_sha1[20];\n \tconst char *current_branch;\n \n-\tgit_config(fmt_merge_msg_config, NULL);\n-\n-\twhile (argc > 1) {\n-\t\tif (!strcmp(argv[1], \"--log\") || !strcmp(argv[1], \"--summary\"))\n-\t\t\tmerge_summary = 1;\n-\t\telse if (!strcmp(argv[1], \"--no-log\")\n-\t\t\t\t|| !strcmp(argv[1], \"--no-summary\"))\n-\t\t\tmerge_summary = 0;\n-\t\telse if (!strcmp(argv[1], \"-F\") || !strcmp(argv[1], \"--file\")) {\n-\t\t\tif (argc < 3)\n-\t\t\t\tdie (\"Which file?\");\n-\t\t\tif (!strcmp(argv[2], \"-\"))\n-\t\t\t\tin = stdin;\n-\t\t\telse {\n-\t\t\t\tfclose(in);\n-\t\t\t\tin = fopen(argv[2], \"r\");\n-\t\t\t\tif (!in)\n-\t\t\t\t\tdie(\"cannot open %s\", argv[2]);\n-\t\t\t}\n-\t\t\targc--; argv++;\n-\t\t} else\n-\t\t\tbreak;\n-\t\targc--; argv++;\n-\t}\n-\n-\tif (argc > 1)\n-\t\tusage(fmt_merge_msg_usage);\n-\n \t/* get current branch */\n \tcurrent_branch = resolve_ref(\"HEAD\", head_sha1, 1, NULL);\n \tif (!current_branch)\n@@ -295,75 +266,127 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)\n \tif (!prefixcmp(current_branch, \"refs/heads/\"))\n \t\tcurrent_branch += 11;\n \n-\twhile (fgets(line, sizeof(line), in)) {\n+\t/* get a line */\n+\twhile (pos < in->len) {\n+\t\tint len;\n+\t\tchar *newline;\n+\n+\t\tp = in->buf + pos;\n+\t\tnewline = strchr(p, '\\n');\n+\t\tlen = newline ? newline - p : strlen(p);\n+\t\tpos += len + !!newline;\n \t\ti++;\n-\t\tif (line[0] == 0)\n-\t\t\tcontinue;\n-\t\tif (handle_line(line))\n-\t\t\tdie (\"Error in line %d: %s\", i, line);\n+\t\tp[len] = 0;\n+\t\tif (handle_line(p))\n+\t\t\tdie (\"Error in line %d: %.*s\", i, len, p);\n \t}\n \n-\tprintf(\"Merge \");\n+\tstrbuf_addstr(out, \"Merge \");\n \tfor (i = 0; i < srcs.nr; i++) {\n \t\tstruct src_data *src_data = srcs.payload[i];\n \t\tconst char *subsep = \"\";\n \n-\t\tprintf(sep);\n+\t\tstrbuf_addstr(out, sep);\n \t\tsep = \"; \";\n \n \t\tif (src_data->head_status == 1) {\n-\t\t\tprintf(srcs.list[i]);\n+\t\t\tstrbuf_addstr(out, srcs.list[i]);\n \t\t\tcontinue;\n \t\t}\n \t\tif (src_data->head_status == 3) {\n \t\t\tsubsep = \", \";\n-\t\t\tprintf(\"HEAD\");\n+\t\t\tstrbuf_addstr(out, \"HEAD\");\n \t\t}\n \t\tif (src_data->branch.nr) {\n-\t\t\tprintf(subsep);\n+\t\t\tstrbuf_addstr(out, subsep);\n \t\t\tsubsep = \", \";\n-\t\t\tprint_joined(\"branch \", \"branches \", &src_data->branch);\n+\t\t\tprint_joined(\"branch \", \"branches \", &src_data->branch,\n+\t\t\t\t\tout);\n \t\t}\n \t\tif (src_data->r_branch.nr) {\n-\t\t\tprintf(subsep);\n+\t\t\tstrbuf_addstr(out, subsep);\n \t\t\tsubsep = \", \";\n \t\t\tprint_joined(\"remote branch \", \"remote branches \",\n-\t\t\t\t\t&src_data->r_branch);\n+\t\t\t\t\t&src_data->r_branch, out);\n \t\t}\n \t\tif (src_data->tag.nr) {\n-\t\t\tprintf(subsep);\n+\t\t\tstrbuf_addstr(out, subsep);\n \t\t\tsubsep = \", \";\n-\t\t\tprint_joined(\"tag \", \"tags \", &src_data->tag);\n+\t\t\tprint_joined(\"tag \", \"tags \", &src_data->tag, out);\n \t\t}\n \t\tif (src_data->generic.nr) {\n-\t\t\tprintf(subsep);\n-\t\t\tprint_joined(\"commit \", \"commits \", &src_data->generic);\n+\t\t\tstrbuf_addstr(out, subsep);\n+\t\t\tprint_joined(\"commit \", \"commits \", &src_data->generic,\n+\t\t\t\t\tout);\n \t\t}\n \t\tif (strcmp(\".\", srcs.list[i]))\n-\t\t\tprintf(\" of %s\", srcs.list[i]);\n+\t\t\tstrbuf_addf(out, \" of %s\", srcs.list[i]);\n \t}\n \n \tif (!strcmp(\"master\", current_branch))\n-\t\tputchar('\\n');\n+\t\tstrbuf_addch(out, '\\n');\n \telse\n-\t\tprintf(\" into %s\\n\", current_branch);\n+\t\tstrbuf_addf(out, \" into %s\\n\", current_branch);\n \n \tif (merge_summary) {\n \t\tstruct commit *head;\n \t\tstruct rev_info rev;\n \n \t\thead = lookup_commit(head_sha1);\n-\t\tinit_revisions(&rev, prefix);\n+\t\tinit_revisions(&rev, NULL);\n \t\trev.commit_format = CMIT_FMT_ONELINE;\n \t\trev.ignore_merges = 1;\n \t\trev.limited = 1;\n \n \t\tfor (i = 0; i < origins.nr; i++)\n \t\t\tshortlog(origins.list[i], origins.payload[i],\n-\t\t\t\t\thead, &rev, limit);\n+\t\t\t\t\thead, &rev, limit, out);\n \t}\n+\treturn 0;\n+}\n+\n+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)\n+{\n+\tFILE *in = stdin;\n+\tstruct strbuf input, output;\n+\tint ret;\n+\n+\tgit_config(fmt_merge_msg_config, NULL);\n+\n+\twhile (argc > 1) {\n+\t\tif (!strcmp(argv[1], \"--log\") || !strcmp(argv[1], \"--summary\"))\n+\t\t\tmerge_summary = 1;\n+\t\telse if (!strcmp(argv[1], \"--no-log\")\n+\t\t\t\t|| !strcmp(argv[1], \"--no-summary\"))\n+\t\t\tmerge_summary = 0;\n+\t\telse if (!strcmp(argv[1], \"-F\") || !strcmp(argv[1], \"--file\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie (\"Which file?\");\n+\t\t\tif (!strcmp(argv[2], \"-\"))\n+\t\t\t\tin = stdin;\n+\t\t\telse {\n+\t\t\t\tfclose(in);\n+\t\t\t\tin = fopen(argv[2], \"r\");\n+\t\t\t\tif (!in)\n+\t\t\t\t\tdie(\"cannot open %s\", argv[2]);\n+\t\t\t}\n+\t\t\targc--; argv++;\n+\t\t} else\n+\t\t\tbreak;\n+\t\targc--; argv++;\n+\t}\n+\n+\tif (argc > 1)\n+\t\tusage(fmt_merge_msg_usage);\n \n-\t/* No cleanup yet; is standalone anyway */\n+\tstrbuf_init(&input, 0);\n+\tif (strbuf_read(&input, fileno(in), 0) < 0)\n+\t\tdie(\"could not read input file %s\", strerror(errno));\n+\tstrbuf_init(&output, 0);\n \n+\tret = fmt_merge_msg(merge_summary, &input, &output);\n+\tif (ret)\n+\t\treturn ret;\n+\tprintf(\"%s\", output.buf);\n \treturn 0;\n }\ndiff --git a/builtin.h b/builtin.h\nindex b460b2d..2b01fea 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -2,6 +2,7 @@\n #define BUILTIN_H\n \n #include \"git-compat-util.h\"\n+#include \"strbuf.h\"\n \n extern const char git_version_string[];\n extern const char git_usage_string[];\n@@ -11,6 +12,8 @@ extern void list_common_cmds_help(void);\n extern void help_unknown_cmd(const char *cmd);\n extern void prune_packed_objects(int);\n extern int read_line_with_nul(char *buf, int size, FILE *file);\n+extern int fmt_merge_msg(int merge_summary, struct strbuf *in,\n+\tstruct strbuf *out);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n-- \n1.5.6\n"},{"id":"81423","messageId":"715117f89992568500bd26b1a4e1357c0a570c4a.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"25a8ffde9783638080d2b998d8f2579e6ed6c8e7.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 07/15] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:00Z","receivedAt":"2008-06-27T16:22:00Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This is like get_merge_bases() but it works for multiple heads, like\nshow-branch --merge-base.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n commit.c |   27 +++++++++++++++++++++++++++\n commit.h |    1 +\n 2 files changed, 28 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex bbf9c75..6052ca3 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -600,6 +600,33 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n \treturn result;\n }\n \n+struct commit_list *get_octopus_merge_bases(struct commit_list *in)\n+{\n+\tstruct commit_list *i, *j, *k, *ret = NULL;\n+\tstruct commit_list **pptr = &ret;\n+\n+\tfor (i = in; i; i = i->next) {\n+\t\tif (!ret)\n+\t\t\tpptr = &commit_list_insert(i->item, pptr)->next;\n+\t\telse {\n+\t\t\tstruct commit_list *new = NULL, *end = NULL;\n+\n+\t\t\tfor (j = ret; j; j = j->next) {\n+\t\t\t\tstruct commit_list *bases;\n+\t\t\t\tbases = get_merge_bases(i->item, j->item, 1);\n+\t\t\t\tif (!new)\n+\t\t\t\t\tnew = bases;\n+\t\t\t\telse\n+\t\t\t\t\tend->next = bases;\n+\t\t\t\tfor (k = bases; k; k = k->next)\n+\t\t\t\t\tend = k;\n+\t\t\t}\n+\t\t\tret = new;\n+\t\t}\n+\t}\n+\treturn ret;\n+}\n+\n struct commit_list *get_merge_bases(struct commit *one,\n \t\t\t\t\tstruct commit *two, int cleanup)\n {\ndiff --git a/commit.h b/commit.h\nindex 7f8c5ee..dcec7fb 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -121,6 +121,7 @@ int read_graft_file(const char *graft_file);\n struct commit_graft *lookup_commit_graft(const unsigned char *sha1);\n \n extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);\n+extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);\n \n extern int register_shallow(const unsigned char *sha1);\n extern int unregister_shallow(const unsigned char *sha1);\n-- \n1.5.6\n"},{"id":"81417","messageId":"d600b25cb1d8f3b6bce12b2a479d4b68b1935dfd.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"715117f89992568500bd26b1a4e1357c0a570c4a.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 08/15] Add new test to ensure git-merge handles more than 25 refs.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:01Z","receivedAt":"2008-06-27T16:22:01Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The old shell version handled only 25 refs but we no longer have this\nlimitation. Add a test to make sure this limitation will not be\nintroduced again in the future.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t7602-merge-octopus-many.sh |   52 +++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 52 insertions(+), 0 deletions(-)\n create mode 100755 t/t7602-merge-octopus-many.sh\n\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nnew file mode 100755\nindex 0000000..f3a4bb2\n--- /dev/null\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+\n+test_description='git-merge\n+\n+Testing octopus merge with more than 25 refs.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo c0 > c0.c &&\n+\tgit add c0.c &&\n+\tgit commit -m c0 &&\n+\tgit tag c0 &&\n+\ti=1 &&\n+\twhile test $i -le 30\n+\tdo\n+\t\tgit reset --hard c0 &&\n+\t\techo c$i > c$i.c &&\n+\t\tgit add c$i.c &&\n+\t\tgit commit -m c$i &&\n+\t\tgit tag c$i &&\n+\t\ti=`expr $i + 1` || return 1\n+\tdone\n+'\n+\n+test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '\n+\tgit reset --hard c1 &&\n+\ti=2 &&\n+\trefs=\"\" &&\n+\twhile test $i -le 30\n+\tdo\n+\t\trefs=\"$refs c$i\"\n+\t\ti=`expr $i + 1`\n+\tdone\n+\tgit merge $refs &&\n+\ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\n+\ti=1 &&\n+\twhile test $i -le 30\n+\tdo\n+\t\ttest \"$(git rev-parse c$i)\" = \"$(git rev-parse HEAD^$i)\" &&\n+\t\ti=`expr $i + 1` || return 1\n+\tdone &&\n+\tgit diff --exit-code &&\n+\ti=1 &&\n+\twhile test $i -le 30\n+\tdo\n+\t\ttest -f c$i.c &&\n+\t\ti=`expr $i + 1` || return 1\n+\tdone\n+'\n+\n+test_done\n-- \n1.5.6\n"},{"id":"81425","messageId":"a01223ac1d530522b383fc3e9590ac1a2a5d66ed.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"d600b25cb1d8f3b6bce12b2a479d4b68b1935dfd.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 09/15] Introduce get_merge_bases_many()","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:02Z","receivedAt":"2008-06-27T16:22:02Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"From: Junio C Hamano <gitster@pobox.com>\n\nThis introduces a new function get_merge_bases_many() which is a natural\nextension of two commit merge base computation.  It is given one commit\n(one) and a set of other commits (twos), and computes the merge base of\none and a merge across other commits.\n\nThis is mostly useful to figure out the common ancestor when iterating\nover heads during an octopus merge.  When making an octopus between\ncommits A, B, C and D, we first merge tree of A and B, and then try to\nmerge C with it.  If we were making pairwise merge, we would be recording\nthe tree resulting from the merge between A and B as a commit, say M, and\nthen the next round we will be computing the merge base between M and C.\n\n         o---C...*\n        /       .\n       o---B...M\n      /       .\n     o---o---A\n\nBut during an octopus merge, we actually do not create a commit M.  In\norder to figure out that the common ancestor to use for this merge,\ninstead of computing the merge base between C and M, we can call\nmerge_bases_many() with one set to C and twos containing A and B.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n commit.c |   56 ++++++++++++++++++++++++++++++++++++++------------------\n 1 files changed, 38 insertions(+), 18 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 6052ca3..cafed26 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -533,26 +533,34 @@ static struct commit *interesting(struct commit_list *list)\n \treturn NULL;\n }\n \n-static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n+static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)\n {\n \tstruct commit_list *list = NULL;\n \tstruct commit_list *result = NULL;\n+\tint i;\n \n-\tif (one == two)\n-\t\t/* We do not mark this even with RESULT so we do not\n-\t\t * have to clean it up.\n-\t\t */\n-\t\treturn commit_list_insert(one, &result);\n+\tfor (i = 0; i < n; i++) {\n+\t\tif (one == twos[i])\n+\t\t\t/*\n+\t\t\t * We do not mark this even with RESULT so we do not\n+\t\t\t * have to clean it up.\n+\t\t\t */\n+\t\t\treturn commit_list_insert(one, &result);\n+\t}\n \n \tif (parse_commit(one))\n \t\treturn NULL;\n-\tif (parse_commit(two))\n-\t\treturn NULL;\n+\tfor (i = 0; i < n; i++) {\n+\t\tif (parse_commit(twos[i]))\n+\t\t\treturn NULL;\n+\t}\n \n \tone->object.flags |= PARENT1;\n-\ttwo->object.flags |= PARENT2;\n \tinsert_by_date(one, &list);\n-\tinsert_by_date(two, &list);\n+\tfor (i = 0; i < n; i++) {\n+\t\ttwos[i]->object.flags |= PARENT2;\n+\t\tinsert_by_date(twos[i], &list);\n+\t}\n \n \twhile (interesting(list)) {\n \t\tstruct commit *commit;\n@@ -627,21 +635,26 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)\n \treturn ret;\n }\n \n-struct commit_list *get_merge_bases(struct commit *one,\n-\t\t\t\t\tstruct commit *two, int cleanup)\n+struct commit_list *get_merge_bases_many(struct commit *one,\n+\t\t\t\t\t int n,\n+\t\t\t\t\t struct commit **twos,\n+\t\t\t\t\t int cleanup)\n {\n \tstruct commit_list *list;\n \tstruct commit **rslt;\n \tstruct commit_list *result;\n \tint cnt, i, j;\n \n-\tresult = merge_bases(one, two);\n-\tif (one == two)\n-\t\treturn result;\n+\tresult = merge_bases_many(one, n, twos);\n+\tfor (i = 0; i < n; i++) {\n+\t\tif (one == twos[i])\n+\t\t\treturn result;\n+\t}\n \tif (!result || !result->next) {\n \t\tif (cleanup) {\n \t\t\tclear_commit_marks(one, all_flags);\n-\t\t\tclear_commit_marks(two, all_flags);\n+\t\t\tfor (i = 0; i < n; i++)\n+\t\t\t\tclear_commit_marks(twos[i], all_flags);\n \t\t}\n \t\treturn result;\n \t}\n@@ -659,12 +672,13 @@ struct commit_list *get_merge_bases(struct commit *one,\n \tfree_commit_list(result);\n \n \tclear_commit_marks(one, all_flags);\n-\tclear_commit_marks(two, all_flags);\n+\tfor (i = 0; i < n; i++)\n+\t\tclear_commit_marks(twos[i], all_flags);\n \tfor (i = 0; i < cnt - 1; i++) {\n \t\tfor (j = i+1; j < cnt; j++) {\n \t\t\tif (!rslt[i] || !rslt[j])\n \t\t\t\tcontinue;\n-\t\t\tresult = merge_bases(rslt[i], rslt[j]);\n+\t\t\tresult = merge_bases_many(rslt[i], 1, &rslt[j]);\n \t\t\tclear_commit_marks(rslt[i], all_flags);\n \t\t\tclear_commit_marks(rslt[j], all_flags);\n \t\t\tfor (list = result; list; list = list->next) {\n@@ -686,6 +700,12 @@ struct commit_list *get_merge_bases(struct commit *one,\n \treturn result;\n }\n \n+struct commit_list *get_merge_bases(struct commit *one, struct commit *two,\n+\t\t\t\t    int cleanup)\n+{\n+\treturn get_merge_bases_many(one, 1, &two, cleanup);\n+}\n+\n int in_merge_bases(struct commit *commit, struct commit **reference, int num)\n {\n \tstruct commit_list *bases, *b;\n-- \n1.5.6\n"},{"id":"81419","messageId":"67035c91a933887c7cc97fa6d3dda9462594d611.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"a01223ac1d530522b383fc3e9590ac1a2a5d66ed.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 10/15] Introduce reduce_heads()","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:03Z","receivedAt":"2008-06-27T16:22:03Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"From: Junio C Hamano <gitster@pobox.com>\n\nThe new function reduce_heads() is given a list of commits, and removes\nones that can be reached from other commits in the list.  It is useful for\nreducing the commits randomly thrown at the git-merge command and remove\nredundant commits that the user shouldn't have given to it.\n\nThe implementation uses the get_merge_bases_many() introduced in the\nprevious commit.  If the merge base between one commit taken from the list\nand the remaining commits is the commit itself, that means the commit is\nreachable from some of the other commits.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n commit.c |   45 +++++++++++++++++++++++++++++++++++++++++++++\n commit.h |    2 ++\n 2 files changed, 47 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex cafed26..d20b14e 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -725,3 +725,48 @@ int in_merge_bases(struct commit *commit, struct commit **reference, int num)\n \tfree_commit_list(bases);\n \treturn ret;\n }\n+\n+struct commit_list *reduce_heads(struct commit_list *heads)\n+{\n+\tstruct commit_list *p;\n+\tstruct commit_list *result = NULL, **tail = &result;\n+\tstruct commit **other;\n+\tsize_t num_head, num_other;\n+\n+\tif (!heads)\n+\t\treturn NULL;\n+\n+\t/* Avoid unnecessary reallocations */\n+\tfor (p = heads, num_head = 0; p; p = p->next)\n+\t\tnum_head++;\n+\tother = xcalloc(sizeof(*other), num_head);\n+\n+\t/* For each commit, see if it can be reached by others */\n+\tfor (p = heads; p; p = p->next) {\n+\t\tstruct commit_list *q, *base;\n+\n+\t\tnum_other = 0;\n+\t\tfor (q = heads; q; q = q->next) {\n+\t\t\tif (p == q)\n+\t\t\t\tcontinue;\n+\t\t\tother[num_other++] = q->item;\n+\t\t}\n+\t\tif (num_other) {\n+\t\t\tbase = get_merge_bases_many(p->item, num_other, other, 1);\n+\t\t} else\n+\t\t\tbase = NULL;\n+\t\t/*\n+\t\t * If p->item does not have anything common with other\n+\t\t * commits, there won't be any merge base.  If it is\n+\t\t * reachable from some of the others, p->item will be\n+\t\t * the merge base.  If its history is connected with\n+\t\t * others, but p->item is not reachable by others, we\n+\t\t * will get something other than p->item back.\n+\t\t */\n+\t\tif (!base || (base->item != p->item))\n+\t\t\ttail = &(commit_list_insert(p->item, tail)->next);\n+\t\tfree_commit_list(base);\n+\t}\n+\tfree(other);\n+\treturn result;\n+}\ndiff --git a/commit.h b/commit.h\nindex dcec7fb..2acfc79 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -140,4 +140,6 @@ static inline int single_parent(struct commit *commit)\n \treturn commit->parents && !commit->parents->next;\n }\n \n+struct commit_list *reduce_heads(struct commit_list *heads);\n+\n #endif /* COMMIT_H */\n-- \n1.5.6\n"},{"id":"81432","messageId":"7ea320cd49601de0e331777f82528876101fd946.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"67035c91a933887c7cc97fa6d3dda9462594d611.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:04Z","receivedAt":"2008-06-27T16:22:04Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe most common use of addf() was to init a strbuf and addf() right away.\nSince it is so common, it makes sense to have a function strbuf_initf() to\nwrap both calls into one.\n\nTo do that, we implement a (really minimal) vaddf() lookalike to\nvsprintf().\n\nAt the moment, it only handles %u, %i, %d, %l, %o, %x and %X with size\nindicators '<number>', ' <number>' and '0<number>', as well as %c and %s,\nthe latter with size indicators '.*' in addition to the same size\nindicators as for numbers.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n .gitignore       |    1 +\n Makefile         |    4 +-\n strbuf.c         |  134 +++++++++++++++++++++++++++++++++++++++++++++++------\n strbuf.h         |    3 +\n t/t0000-basic.sh |    8 +++\n test-strbuf.c    |   17 +++++++\n 6 files changed, 150 insertions(+), 17 deletions(-)\n create mode 100644 test-strbuf.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 4ff2fec..9281395 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -151,6 +151,7 @@ test-genrandom\n test-match-trees\n test-parse-options\n test-sha1\n+test-strbuf\n common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 314339d..1b43171 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1230,7 +1230,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X test-strbuf$X\n \n all:: $(TEST_PROGRAMS)\n \n@@ -1249,6 +1249,8 @@ test-delta$X: diff-delta.o patch-delta.o\n \n test-parse-options$X: parse-options.o\n \n+test-strbuf$X: strbuf.o\n+\n .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))\n \n test-%$X: test-%.o $(GITLIBS)\ndiff --git a/strbuf.c b/strbuf.c\nindex 4aed752..72b3585 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -122,28 +122,130 @@ void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)\n \tstrbuf_setlen(sb, sb->len + len);\n }\n \n+static int number_length(unsigned long number, long base)\n+{\n+\tint length = 1;\n+\twhile (number >= base) {\n+\t\tnumber /= base;\n+\t\tlength++;\n+\t}\n+\treturn length;\n+}\n+\n+/*\n+ * Only supports %u, %i, %d, %l, %o, %x and %X with size indicators\n+ *   '<number>', '0<number>', and ' <number>',\n+ * as well as %c,\n+ * and %s with size indicators '<number>', ' <number>' and '.*'.\n+ */\n+void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap)\n+{\n+\twhile (*fmt) {\n+\t\tchar fill = '\\0';\n+\t\tint size = -1, max_size = -1;\n+\t\tchar *p = (char *)fmt;\n+\n+\t\tif (*p != '%' || *(++p) == '%') {\n+\t\t\tstrbuf_addch(sb, *p++);\n+\t\t\tfmt = p;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (*p == ' ' || *p == '0')\n+\t\t\tfill = *p++;\n+\t\tif (isdigit(*p))\n+\t\t\tsize = (int)strtol(p, &p, 10);\n+\t\telse if (!prefixcmp(p, \".*\")) {\n+\t\t\tmax_size = va_arg(ap, int);\n+\t\t\tp += 2;\n+\t\t}\n+\t\tswitch (*p) {\n+\t\tcase 's': {\n+\t\t\tconst char *s = va_arg(ap, const char *);\n+\t\t\tif (fill) {\n+\t\t\t\tint len = size - strlen(s);\n+\t\t\t\twhile (len-- > 0)\n+\t\t\t\t\tstrbuf_addch(sb, fill);\n+\t\t\t}\n+\t\t\twhile (*s && max_size--)\n+\t\t\t\tstrbuf_addch(sb, *s++);\n+\t\t\tbreak;\n+\t\t}\n+\t\tcase 'c':\n+\t\t\tstrbuf_addch(sb, va_arg(ap, int));\n+\t\t\tbreak;\n+\t\tcase 'u':\n+\t\tcase 'i':\n+\t\tcase 'l':\n+\t\tcase 'd':\n+\t\tcase 'o':\n+\t\tcase 'x':\n+\t\tcase 'X': {\n+\t\t\tint base = *p == 'x' || *p == 'X' ? 16 :\n+\t\t\t\t*p == 'o' ? 8 : 10;\n+\t\t\tint negative = 0, len;\n+\t\t\tunsigned long number, power;\n+\n+\t\t\tif (*p == 'u')\n+\t\t\t\tnumber = va_arg(ap, unsigned int);\n+\t\t\telse {\n+\t\t\t\tlong signed_number;\n+\t\t\t\tif (*p == 'l')\n+\t\t\t\t\tsigned_number = va_arg(ap, long);\n+\t\t\t\telse\n+\t\t\t\t\tsigned_number = va_arg(ap, int);\n+\t\t\t\tif (signed_number < 0) {\n+\t\t\t\t\tnegative = 1;\n+\t\t\t\t\tnumber = -signed_number;\n+\t\t\t\t} else\n+\t\t\t\t\tnumber = signed_number;\n+\t\t\t}\n+\n+\t\t\t/* pad */\n+\t\t\tlen = number_length(number, base);\n+\t\t\twhile (size-- > len + negative)\n+\t\t\t\tstrbuf_addch(sb, fill ? fill : ' ');\n+\t\t\tif (negative)\n+\t\t\t\tstrbuf_addch(sb, '-');\n+\n+\t\t\t/* output number */\n+\t\t\tpower = 1;\n+\t\t\twhile (len-- > 1)\n+\t\t\t\tpower *= base;\n+\t\t\twhile (power) {\n+\t\t\t\tint digit = number / power;\n+\t\t\t\tstrbuf_addch(sb, digit < 10 ? '0' + digit\n+\t\t\t\t\t: *p + 'A' - 'X' + digit - 10);\n+\t\t\t\tnumber -= digit * power;\n+\t\t\t\tpower /= base;\n+\t\t\t}\n+\n+\t\t\tbreak;\n+\t\t}\n+\t\tdefault:\n+\t\t\t/* unknown / invalid format: copy verbatim */\n+\t\t\tstrbuf_insert(sb, sb->len, fmt, p - fmt + 1);\n+\t\t}\n+\t\tfmt = p + (*p != '\\0');\n+\t}\n+}\n+\n void strbuf_addf(struct strbuf *sb, const char *fmt, ...)\n {\n-\tint len;\n \tva_list ap;\n \n-\tif (!strbuf_avail(sb))\n-\t\tstrbuf_grow(sb, 64);\n \tva_start(ap, fmt);\n-\tlen = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);\n+\tstrbuf_vaddf(sb, fmt, ap);\n+\tva_end(ap);\n+}\n+\n+void strbuf_initf(struct strbuf *sb, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\n+\tstrbuf_init(sb, strlen(fmt) + 64);\n+\tva_start(ap, fmt);\n+\tstrbuf_vaddf(sb, fmt, ap);\n \tva_end(ap);\n-\tif (len < 0)\n-\t\tdie(\"your vsnprintf is broken\");\n-\tif (len > strbuf_avail(sb)) {\n-\t\tstrbuf_grow(sb, len);\n-\t\tva_start(ap, fmt);\n-\t\tlen = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);\n-\t\tva_end(ap);\n-\t\tif (len > strbuf_avail(sb)) {\n-\t\t\tdie(\"this should not happen, your snprintf is broken\");\n-\t\t}\n-\t}\n-\tstrbuf_setlen(sb, sb->len + len);\n }\n \n void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,\ndiff --git a/strbuf.h b/strbuf.h\nindex faec229..d7c7aaf 100644\n--- a/strbuf.h\n+++ b/strbuf.h\n@@ -106,8 +106,11 @@ extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);\n typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);\n extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);\n \n+extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list args);\n __attribute__((format(printf,2,3)))\n extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);\n+__attribute__((format(printf,2,3)))\n+extern void strbuf_initf(struct strbuf *sb, const char *fmt, ...);\n \n extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);\n /* XXX: if read fails, any partial read is undone */\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 690f80a..7c7ca4c 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -311,6 +311,14 @@ test_expect_success 'absolute path works as expected' '\n \ttest \"$sym\" = \"$(test-absolute-path \"$dir2/syml\")\"\n '\n \n+test_expect_success 'strbuf_initf() works as expected' '\n+\n+\teval $(test-strbuf) &&\n+\ttest ! -z \"$result\" &&\n+\ttest \"$result\" = \"$expect\"\n+\n+'\n+\n test_expect_success 'very long name in the index handled sanely' '\n \n \ta=a && # 1\ndiff --git a/test-strbuf.c b/test-strbuf.c\nnew file mode 100644\nindex 0000000..479fa08\n--- /dev/null\n+++ b/test-strbuf.c\n@@ -0,0 +1,17 @@\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\tstruct strbuf buf;\n+#define TEST_FORMAT \\\n+\t\"'%%%.*s,%x,%05X,%u,%i,% 4d,%3d,%c,%3d'\", \\\n+\t5, \"Hello, World!\", 27, 27, -1, -1, 1, 5, ':', 1234\n+\n+\tstrbuf_initf(&buf, TEST_FORMAT);\n+\tprintf(\"result=%s\\n\", buf.buf);\n+\tprintf(\"expect=\" TEST_FORMAT);\n+\tstrbuf_release(&buf);\n+\n+\treturn 0;\n+}\n-- \n1.5.6\n"},{"id":"81430","messageId":"3a8521edeb769e0fb85bec434daf064ce29edaa5.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"7ea320cd49601de0e331777f82528876101fd946.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 12/15] strbuf_vaddf(): support %*s, too","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:05Z","receivedAt":"2008-06-27T16:22:05Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe recent addition of graph.c broke things with our custom implementation\nof strbuf_vaddf(), because \"%*s\" was not yet supported (because it did not\nhave any users before).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n strbuf.c |    6 ++++++\n 1 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/strbuf.c b/strbuf.c\nindex 72b3585..bd5dc80 100644\n--- a/strbuf.c\n+++ b/strbuf.c\n@@ -154,6 +154,12 @@ void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap)\n \t\t\tfill = *p++;\n \t\tif (isdigit(*p))\n \t\t\tsize = (int)strtol(p, &p, 10);\n+\t\telse if (*p == '*') {\n+\t\t\tsize = va_arg(ap, int);\n+\t\t\tif (!fill)\n+\t\t\t\tfill = ' ';\n+\t\t\tp++;\n+\t\t}\n \t\telse if (!prefixcmp(p, \".*\")) {\n \t\t\tmax_size = va_arg(ap, int);\n \t\t\tp += 2;\n-- \n1.5.6\n"},{"id":"81431","messageId":"2f3a41b78511ab22c31c2c2827b9b8a456462c83.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"3a8521edeb769e0fb85bec434daf064ce29edaa5.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 13/15] Add new test case to ensure git-merge reduces octopus parents when possible","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:06Z","receivedAt":"2008-06-27T16:22:06Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The old shell version used show-branch --independent to filter for the\nones that cannot be reached from any other reference.\n\nThe new C version uses reduce_heads() from commit.c for this, so\nadd test to ensure it works as expected.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n t/t7603-merge-reduce-heads.sh |   63 +++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 63 insertions(+), 0 deletions(-)\n create mode 100755 t/t7603-merge-reduce-heads.sh\n\ndiff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh\nnew file mode 100755\nindex 0000000..17b19dc\n--- /dev/null\n+++ b/t/t7603-merge-reduce-heads.sh\n@@ -0,0 +1,63 @@\n+#!/bin/sh\n+\n+test_description='git-merge\n+\n+Testing octopus merge when reducing parents to independent branches.'\n+\n+. ./test-lib.sh\n+\n+# 0 - 1\n+#   \\ 2\n+#   \\ 3\n+#   \\ 4 - 5\n+#\n+# So 1, 2, 3 and 5 should be kept, 4 should be avoided.\n+\n+test_expect_success 'setup' '\n+\techo c0 > c0.c &&\n+\tgit add c0.c &&\n+\tgit commit -m c0 &&\n+\tgit tag c0 &&\n+\techo c1 > c1.c &&\n+\tgit add c1.c &&\n+\tgit commit -m c1 &&\n+\tgit tag c1 &&\n+\tgit reset --hard c0 &&\n+\techo c2 > c2.c &&\n+\tgit add c2.c &&\n+\tgit commit -m c2 &&\n+\tgit tag c2 &&\n+\tgit reset --hard c0 &&\n+\techo c3 > c3.c &&\n+\tgit add c3.c &&\n+\tgit commit -m c3 &&\n+\tgit tag c3 &&\n+\tgit reset --hard c0 &&\n+\techo c4 > c4.c &&\n+\tgit add c4.c &&\n+\tgit commit -m c4 &&\n+\tgit tag c4 &&\n+\techo c5 > c5.c &&\n+\tgit add c5.c &&\n+\tgit commit -m c5 &&\n+\tgit tag c5\n+'\n+\n+test_expect_success 'merge c1 with c2, c3, c4, c5' '\n+\tgit reset --hard c1 &&\n+\tgit merge c2 c3 c4 c5 &&\n+\ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\n+\ttest \"$(git rev-parse c1)\" = \"$(git rev-parse HEAD^1)\" &&\n+\ttest \"$(git rev-parse c2)\" = \"$(git rev-parse HEAD^2)\" &&\n+\ttest \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\" &&\n+\ttest \"$(git rev-parse c5)\" = \"$(git rev-parse HEAD^4)\" &&\n+\tgit diff --exit-code &&\n+\ttest -f c0.c &&\n+\ttest -f c1.c &&\n+\ttest -f c2.c &&\n+\ttest -f c3.c &&\n+\ttest -f c4.c &&\n+\ttest -f c5.c\n+'\n+\n+test_done\n-- \n1.5.6\n"},{"id":"81427","messageId":"37b95171b8cb3b9db721bf5e854cc73d0b263536.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"2f3a41b78511ab22c31c2c2827b9b8a456462c83.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 14/15] Add new test case to ensure git-merge prepends the custom merge message","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:07Z","receivedAt":"2008-06-27T16:22:07Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"There was no test for this before, so the testsuite passed, even in case\nthe merge summary was missing from the merge commit message.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n t/t7604-merge-custom-message.sh |   37 +++++++++++++++++++++++++++++++++++++\n 1 files changed, 37 insertions(+), 0 deletions(-)\n create mode 100755 t/t7604-merge-custom-message.sh\n\ndiff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh\nnew file mode 100755\nindex 0000000..6081677\n--- /dev/null\n+++ b/t/t7604-merge-custom-message.sh\n@@ -0,0 +1,37 @@\n+#!/bin/sh\n+\n+test_description='git-merge\n+\n+Testing merge when using a custom message for the merge commit.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo c0 > c0.c &&\n+\tgit add c0.c &&\n+\tgit commit -m c0 &&\n+\tgit tag c0 &&\n+\techo c1 > c1.c &&\n+\tgit add c1.c &&\n+\tgit commit -m c1 &&\n+\tgit tag c1 &&\n+\tgit reset --hard c0 &&\n+\techo c2 > c2.c &&\n+\tgit add c2.c &&\n+\tgit commit -m c2 &&\n+\tgit tag c2\n+'\n+\n+cat >expected <<\\EOF\n+custom message\n+\n+Merge commit 'c2'\n+EOF\n+test_expect_success 'merge c2 with a custom message' '\n+\tgit reset --hard c1 &&\n+\tgit merge -m \"custom message\" c2 &&\n+\tgit cat-file commit HEAD | sed -e \"1,/^$/d\" > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\n-- \n1.5.6\n"},{"id":"81429","messageId":"6bfb52304e0bdad4cd9a15b7a2ec98ffdd3de152.1214581610.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"37b95171b8cb3b9db721bf5e854cc73d0b263536.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 15/15] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T16:22:08Z","receivedAt":"2008-06-27T16:22:08Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n Makefile                                      |    2 +-\n builtin-merge.c                               | 1143 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git.c                                         |    1 +\n t/t7602-merge-octopus-many.sh                 |    2 +-\n 6 files changed, 1147 insertions(+), 2 deletions(-)\n create mode 100644 builtin-merge.c\n rename git-merge.sh => contrib/examples/git-merge.sh (100%)\n\ndiff --git a/Makefile b/Makefile\nindex 1b43171..38135cd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n-SCRIPT_SH += git-merge.sh\n SCRIPT_SH += git-merge-stupid.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-parse-remote.sh\n@@ -512,6 +511,7 @@ BUILTIN_OBJS += builtin-ls-remote.o\n BUILTIN_OBJS += builtin-ls-tree.o\n BUILTIN_OBJS += builtin-mailinfo.o\n BUILTIN_OBJS += builtin-mailsplit.o\n+BUILTIN_OBJS += builtin-merge.o\n BUILTIN_OBJS += builtin-merge-base.o\n BUILTIN_OBJS += builtin-merge-file.o\n BUILTIN_OBJS += builtin-merge-ours.o\ndiff --git a/builtin-merge.c b/builtin-merge.c\nnew file mode 100644\nindex 0000000..0d56dca\n--- /dev/null\n+++ b/builtin-merge.c\n@@ -0,0 +1,1143 @@\n+/*\n+ * Builtin \"git merge\"\n+ *\n+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>\n+ *\n+ * Based on git-merge.sh by Junio C Hamano.\n+ */\n+\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"builtin.h\"\n+#include \"run-command.h\"\n+#include \"path-list.h\"\n+#include \"diff.h\"\n+#include \"refs.h\"\n+#include \"commit.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+#include \"unpack-trees.h\"\n+#include \"cache-tree.h\"\n+#include \"dir.h\"\n+#include \"utf8.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+\n+enum strategy {\n+\tDEFAULT_TWOHEAD = 1,\n+\tDEFAULT_OCTOPUS = 2,\n+\tNO_FAST_FORWARD = 4,\n+\tNO_TRIVIAL = 8\n+};\n+\n+static const char * const builtin_merge_usage[] = {\n+\t\"git-merge [options] <remote>...\",\n+\t\"git-merge [options] <msg> HEAD <remote>\",\n+\tNULL\n+};\n+\n+static int show_diffstat = 1, option_log, squash;\n+static int option_commit = 1, allow_fast_forward = 1;\n+static int allow_trivial = 1, have_message;\n+static struct strbuf merge_msg;\n+static struct commit_list *remoteheads;\n+static unsigned char head[20];\n+static struct path_list use_strategies;\n+static const char *branch;\n+\n+static struct path_list_item strategy_items[] = {\n+\t{ \"recur\",      (void *)NO_TRIVIAL },\n+\t{ \"recursive\",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },\n+\t{ \"octopus\",    (void *)DEFAULT_OCTOPUS },\n+\t{ \"resolve\",    (void *)0 },\n+\t{ \"stupid\",     (void *)0 },\n+\t{ \"ours\",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+\t{ \"subtree\",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+};\n+static struct path_list strategies = { strategy_items,\n+\tARRAY_SIZE(strategy_items), 0, 0 };\n+\n+static const char *pull_twohead, *pull_octopus;\n+\n+static int option_parse_message(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tstruct strbuf *buf = opt->value;\n+\n+\tif (unset)\n+\t\tstrbuf_setlen(buf, 0);\n+\telse {\n+\t\tstrbuf_addf(buf, \"%s\\n\\n\", arg);\n+\t\thave_message = 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static struct path_list_item *unsorted_path_list_lookup(const char *path,\n+\tstruct path_list *list)\n+{\n+\tint i;\n+\n+\tif (!path)\n+\t\treturn NULL;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tif (!strcmp(path, list->items[i].path))\n+\t\t\treturn &list->items[i];\n+\treturn NULL;\n+}\n+\n+static inline void path_list_append_strategy(\n+\t\tstruct path_list_item *item)\n+{\n+\tpath_list_append(item->path, &use_strategies)->util = item->util;\n+}\n+\n+static int option_parse_strategy(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tint i;\n+\tstruct path_list_item *item =\n+\t\tunsorted_path_list_lookup(arg, &strategies);\n+\n+\tif (unset)\n+\t\treturn 0;\n+\n+\tif (item)\n+\t\tpath_list_append_strategy(item);\n+\telse {\n+\t\tstruct strbuf err;\n+\t\tstrbuf_init(&err, 0);\n+\t\tfor (i = 0; i < strategies.nr; i++)\n+\t\t\tstrbuf_addf(&err, \" %s\", strategies.items[i].path);\n+\t\tfprintf(stderr, \"Could not find merge strategy '%s'.\\n\", arg);\n+\t\tfprintf(stderr, \"Available strategies are:%s.\\n\", err.buf);\n+\t\texit(1);\n+\t}\n+\treturn 0;\n+}\n+\n+static int option_parse_n(const struct option *opt,\n+\t\tconst char *arg, int unset)\n+{\n+\tshow_diffstat = unset;\n+\treturn 0;\n+}\n+\n+static struct option builtin_merge_options[] = {\n+\t{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,\n+\t\t\"do not show a diffstat at the end of the merge\",\n+\t\tPARSE_OPT_NOARG, option_parse_n },\n+\tOPT_BOOLEAN(0, \"stat\", &show_diffstat,\n+\t\t\"show a diffstat at the end of the merge\"),\n+\tOPT_BOOLEAN(0, \"summary\", &show_diffstat, \"(synonym to --stat)\"),\n+\tOPT_BOOLEAN(0, \"log\", &option_log,\n+\t\t\"add list of one-line log to merge commit message\"),\n+\tOPT_BOOLEAN(0, \"squash\", &squash,\n+\t\t\"create a single commit instead of doing a merge\"),\n+\tOPT_BOOLEAN(0, \"commit\", &option_commit,\n+\t\t\"perform a commit if the merge succeeds (default)\"),\n+\tOPT_BOOLEAN(0, \"ff\", &allow_fast_forward,\n+\t\t\"allow fast forward (default)\"),\n+\tOPT_CALLBACK('s', \"strategy\", &use_strategies, \"strategy\",\n+\t\t\"merge strategy to use\", option_parse_strategy),\n+\tOPT_CALLBACK('m', \"message\", &merge_msg, \"message\",\n+\t\t\"message to be used for the merge commit (if any)\",\n+\t\toption_parse_message),\n+\tOPT_END()\n+};\n+\n+/* Cleans up metadata that is uninteresting after a succeeded merge. */\n+static void dropsave(void)\n+{\n+\tunlink(git_path(\"MERGE_HEAD\"));\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tunlink(git_path(\"MERGE_STASH\"));\n+}\n+\n+static void save_state(void)\n+{\n+\tint fd;\n+\tstruct child_process stash;\n+\tconst char *argv[] = {\"stash\", \"create\", NULL};\n+\n+\tfd = open(git_path(\"MERGE_STASH\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"MERGE_STASH\"));\n+\tmemset(&stash, 0, sizeof(stash));\n+\tstash.argv = argv;\n+\tstash.out = fd;\n+\tstash.git_cmd = 1;\n+\trun_command(&stash);\n+}\n+\n+static void reset_hard(unsigned const char *sha1, int verbose)\n+{\n+\tstruct tree *tree;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.reset = 1;\n+\tif (verbose)\n+\t\topts.verbose_update = 1;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\tdie(\"failed to unpack %s tree object\", sha1_to_hex(sha1));\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tif (unpack_trees(1, &t, &opts))\n+\t\texit(128); /* We've already reported the error, finish dying */\n+}\n+\n+static void restore_state(void)\n+{\n+\tstruct strbuf sb;\n+\tconst char *args[] = { \"stash\", \"apply\", NULL, NULL };\n+\n+\tif (access(git_path(\"MERGE_STASH\"), R_OK) < 0)\n+\t\treturn;\n+\n+\treset_hard(head, 1);\n+\n+\tstrbuf_init(&sb, 0);\n+\tif (strbuf_read_file(&sb, git_path(\"MERGE_STASH\"), 0) < 0)\n+\t\tdie(\"could not read MERGE_STASH: %s\", strerror(errno));\n+\targs[2] = sb.buf;\n+\n+\t/*\n+\t * It is OK to ignore error here, for example when there was\n+\t * nothing to restore.\n+\t */\n+\trun_command_v_opt(args, RUN_GIT_CMD);\n+\n+\tstrbuf_release(&sb);\n+\trefresh_cache(REFRESH_QUIET);\n+}\n+\n+/* This is called when no merge was necessary. */\n+static void finish_up_to_date(const char *msg)\n+{\n+\tprintf(\"%s%s\\n\", squash ? \" (nothing to squash)\" : \"\", msg);\n+\tdropsave();\n+}\n+\n+static void squash_message(void)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf out;\n+\tstruct commit_list *j;\n+\tint fd;\n+\n+\tprintf(\"Squash commit -- not updating HEAD\\n\");\n+\tfd = open(git_path(\"SQUASH_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"SQUASH_MSG\"));\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.ignore_merges = 1;\n+\trev.commit_format = CMIT_FMT_MEDIUM;\n+\n+\tcommit = lookup_commit(head);\n+\tcommit->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&rev, &commit->object, NULL);\n+\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tadd_pending_object(&rev, &j->item->object, NULL);\n+\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\tif (prepare_revision_walk(&rev))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\tstrbuf_initf(&out, \"Squashed commit of the following:\\n\");\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstrbuf_addch(&out, '\\n');\n+\t\tstrbuf_addf(&out, \"commit %s\\n\",\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\t\tpretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,\n+\t\t\tNULL, NULL, rev.date_mode, 0);\n+\t}\n+\twrite(fd, out.buf, out.len);\n+\tclose(fd);\n+\tstrbuf_release(&out);\n+}\n+\n+static int run_hook(const char *name)\n+{\n+\tstruct child_process hook;\n+\tconst char *argv[3], *env[2];\n+\tchar index[PATH_MAX];\n+\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\n+\n+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", get_index_file());\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\tif (squash)\n+\t\targv[1] = \"1\";\n+\telse\n+\t\targv[1] = \"0\";\n+\targv[2] = NULL;\n+\n+\tmemset(&hook, 0, sizeof(hook));\n+\thook.argv = argv;\n+\thook.no_stdin = 1;\n+\thook.stdout_to_stderr = 1;\n+\thook.env = env;\n+\n+\treturn run_command(&hook);\n+}\n+\n+static void finish(const unsigned char *new_head, const char *msg)\n+{\n+\tstruct strbuf reflog_message;\n+\n+\tif (!msg)\n+\t\tstrbuf_initf(&reflog_message, \"%s\",\n+\t\t\tgetenv(\"GIT_REFLOG_ACTION\"));\n+\telse {\n+\t\tprintf(\"%s\\n\", msg);\n+\t\tstrbuf_initf(&reflog_message, \"%s: %s\",\n+\t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n+\t}\n+\tif (squash) {\n+\t\tsquash_message();\n+\t} else {\n+\t\tif (!merge_msg.len)\n+\t\t\tprintf(\"No merge message -- not updating HEAD\\n\");\n+\t\telse {\n+\t\t\tconst char *argv_gc_auto[] = { \"gc\", \"--auto\", NULL };\n+\t\t\tupdate_ref(reflog_message.buf, \"HEAD\",\n+\t\t\t\tnew_head, head, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\t\t/*\n+\t\t\t * We ignore errors in 'gc --auto', since the\n+\t\t\t * user should see them.\n+\t\t\t */\n+\t\t\trun_command_v_opt(argv_gc_auto, RUN_GIT_CMD);\n+\t\t}\n+\t}\n+\tif (new_head && show_diffstat) {\n+\t\tstruct diff_options opts;\n+\t\tdiff_setup(&opts);\n+\t\topts.output_format |=\n+\t\t\tDIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;\n+\t\topts.detect_rename = DIFF_DETECT_RENAME;\n+\t\tif (diff_use_color_default > 0)\n+\t\t\tDIFF_OPT_SET(&opts, COLOR_DIFF);\n+\t\tdiff_tree_sha1(head, new_head, \"\", &opts);\n+\t\tdiffcore_std(&opts);\n+\t\tdiff_flush(&opts);\n+\t}\n+\n+\t/* Run a post-merge hook */\n+\trun_hook(\"post-merge\");\n+\n+\tstrbuf_release(&reflog_message);\n+}\n+\n+/* Get the name for the merge commit's message. */\n+static void merge_name(const char *remote, struct strbuf *msg)\n+{\n+\tstruct object *remote_head;\n+\tunsigned char branch_head[20], buf_sha[20];\n+\tstruct strbuf buf;\n+\tchar *ptr;\n+\tint len = 0;\n+\n+\tmemset(branch_head, 0, sizeof(branch_head));\n+\tremote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);\n+\tif (!remote_head)\n+\t\treturn;\n+\n+\tstrbuf_initf(&buf, \"refs/heads/%s\", remote);\n+\tget_sha1(buf.buf, branch_head);\n+\n+\tif (!hashcmp(remote_head->sha1, branch_head)) {\n+\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n+\t\t\tsha1_to_hex(branch_head), remote);\n+\t\treturn;\n+\t}\n+\t/* See if remote matches <name>~<number>, or <name>^ */\n+\tptr = strrchr(remote, '^');\n+\tif (ptr && ptr[1] == '\\0')\n+\t\tlen = ptr-remote;\n+\telse {\n+\t\tptr = strrchr(remote, '~');\n+\t\tif (ptr && ptr[1] != '0' && isdigit(ptr[1])) {\n+\t\t\tlen = ptr-remote;\n+\t\t\tptr++;\n+\t\t\tfor (ptr++; *ptr; ptr++)\n+\t\t\t\tif (!isdigit(*ptr)) {\n+\t\t\t\t\tlen = 0;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t}\n+\t}\n+\tif (len) {\n+\t\tstruct strbuf truname = STRBUF_INIT;\n+\t\tstrbuf_addstr(&truname, remote);\n+\t\tstrbuf_setlen(&truname, len);\n+\t\tif (!get_sha1(truname.buf, buf_sha)) {\n+\t\t\tstrbuf_addf(msg,\n+\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n+\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(remote, \"FETCH_HEAD\") &&\n+\t\t\t!access(git_path(\"FETCH_HEAD\"), R_OK)) {\n+\t\tFILE *fp;\n+\t\tstruct strbuf line;\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_init(&line, 0);\n+\t\tfp = fopen(git_path(\"FETCH_HEAD\"), \"r\");\n+\t\tif (!fp)\n+\t\t\tdie(\"could not open %s for reading: %s\",\n+\t\t\t\tgit_path(\"FETCH_HEAD\"), strerror(errno));\n+\t\tstrbuf_getline(&line, fp, '\\n');\n+\t\tfclose(fp);\n+\t\tptr = strstr(line.buf, \"\\tnot-for-merge\\t\");\n+\t\tif (ptr)\n+\t\t\tstrbuf_remove(&line, ptr-line.buf+1, 13);\n+\t\tstrbuf_addbuf(msg, &line);\n+\t\tstrbuf_release(&line);\n+\t\treturn;\n+\t}\n+\tstrbuf_addf(msg, \"%s\\t\\tcommit '%s'\\n\",\n+\t\tsha1_to_hex(remote_head->sha1), remote);\n+}\n+\n+int git_merge_config(const char *k, const char *v, void *cb)\n+{\n+\tif (branch && !prefixcmp(k, \"branch.\") &&\n+\t\t!prefixcmp(k + 7, branch) &&\n+\t\t!strcmp(k + 7 + strlen(branch), \".mergeoptions\")) {\n+\t\tconst char **argv;\n+\t\tint argc;\n+\t\tchar *buf;\n+\n+\t\tbuf = xstrdup(v);\n+\t\targc = split_cmdline(buf, &argv);\n+\t\targv = xrealloc(argv, sizeof(*argv) * (argc + 2));\n+\t\tmemmove(argv + 1, argv, sizeof(*argv) * (argc + 1));\n+\t\targc++;\n+\t\tparse_options(argc, argv, builtin_merge_options,\n+\t\t\t      builtin_merge_usage, 0);\n+\t\tfree(buf);\n+\t}\n+\n+\tif (!strcmp(k, \"merge.diffstat\") || !strcmp(k, \"merge.stat\"))\n+\t\tshow_diffstat = git_config_bool(k, v);\n+\telse if (!strcmp(k, \"pull.twohead\"))\n+\t\treturn git_config_string(&pull_twohead, k, v);\n+\telse if (!strcmp(k, \"pull.octopus\"))\n+\t\treturn git_config_string(&pull_octopus, k, v);\n+\treturn 0;\n+}\n+\n+static int read_tree_trivial(unsigned char *common, unsigned char *head,\n+\tunsigned char *one)\n+{\n+\tint i, nr_trees = 0;\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.trivial_merges_only = 1;\n+\topts.merge = 1;\n+\ttrees[nr_trees] = parse_tree_indirect(common);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(one);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\topts.fn = threeway_merge;\n+\tcache_tree_free(&active_cache_tree);\n+\topts.head_idx = 2;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static int commit_tree_trivial(const char *msg, unsigned const char *tree,\n+\t\tstruct commit_list *parents, unsigned char *ret)\n+{\n+\tstruct commit_list *i;\n+\tstruct strbuf buf;\n+\tint encoding_is_utf8;\n+\n+\t/* Not having i18n.commitencoding is the same as having utf-8 */\n+\tencoding_is_utf8 = is_encoding_utf8(git_commit_encoding);\n+\n+\tstrbuf_init(&buf, 8192); /* should avoid reallocs for the headers */\n+\tstrbuf_addf(&buf, \"tree %s\\n\", sha1_to_hex(tree));\n+\n+\tfor (i = parents; i; i = i->next)\n+\t\tstrbuf_addf(&buf, \"parent %s\\n\",\n+\t\t\tsha1_to_hex(i->item->object.sha1));\n+\n+\t/* Person/date information */\n+\tstrbuf_addf(&buf, \"author %s\\n\",\n+\t\tgit_author_info(IDENT_ERROR_ON_NO_NAME));\n+\tstrbuf_addf(&buf, \"committer %s\\n\",\n+\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME));\n+\tif (!encoding_is_utf8)\n+\t\tstrbuf_addf(&buf, \"encoding %s\\n\", git_commit_encoding);\n+\tstrbuf_addch(&buf, '\\n');\n+\n+\t/* And add the comment */\n+\tstrbuf_addstr(&buf, msg);\n+\n+\twrite_sha1_file(buf.buf, buf.len, commit_type, ret);\n+\tstrbuf_release(&buf);\n+\treturn *ret;\n+}\n+\n+static void write_tree_trivial(unsigned char *sha1)\n+{\n+\tif (write_cache_as_tree(sha1, 0, NULL))\n+\t\tdie(\"git write-tree failed to write a tree\");\n+}\n+\n+static int try_merge_strategy(char *strategy, struct commit_list *common,\n+\tstruct strbuf *head_arg)\n+{\n+\tconst char **args;\n+\tint i = 0, ret;\n+\tstruct commit_list *j;\n+\tstruct strbuf buf;\n+\n+\targs = xmalloc((4 + commit_list_count(common) +\n+\t\t\tcommit_list_count(remoteheads)) * sizeof(char *));\n+\tstrbuf_initf(&buf, \"merge-%s\", strategy);\n+\targs[i++] = buf.buf;\n+\tfor (j = common; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i++] = \"--\";\n+\targs[i++] = head_arg->buf;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i] = NULL;\n+\tret = run_command_v_opt(args, RUN_GIT_CMD);\n+\tstrbuf_release(&buf);\n+\ti = 1;\n+\tfor (j = common; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\ti += 2;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\tfree(args);\n+\treturn -ret;\n+}\n+\n+static void count_diff_files(struct diff_queue_struct *q,\n+\t\tstruct diff_options *opt, void *data)\n+{\n+\tint *count = data;\n+\n+\t(*count) += q->nr;\n+}\n+\n+static int count_unmerged_entries(void)\n+{\n+\tconst struct index_state *state = &the_index;\n+\tint i, ret = 0;\n+\n+\tfor (i = 0; i < state->cache_nr; i++)\n+\t\tif (ce_stage(state->cache[i]))\n+\t\t\tret++;\n+\n+\treturn ret;\n+}\n+\n+static int merge_one_remote(unsigned char *head, unsigned char *remote)\n+{\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tint i, fd, nr_trees = 0;\n+\tstruct dir_struct dir;\n+\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\n+\tif (read_cache_unmerged())\n+\t\tdie(\"you need to resolve your current index first\");\n+\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&trees, 0, sizeof(trees));\n+\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(&t, 0, sizeof(t));\n+\tdir.show_ignored = 1;\n+\tdir.exclude_per_dir = \".gitignore\";\n+\topts.dir = &dir;\n+\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.merge = 1;\n+\topts.fn = twoway_merge;\n+\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(remote);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\tcommit_locked_index(lock_file))\n+\t\tdie(\"unable to write new index file\");\n+\treturn 0;\n+}\n+\n+static void split_merge_strategies(const char *string, struct path_list *list)\n+{\n+\tchar *p, *q, *buf;\n+\n+\tif (!string)\n+\t\treturn;\n+\n+\tlist->strdup_paths = 1;\n+\tbuf = xstrdup(string);\n+\tq = buf;\n+\tfor (;;) {\n+\t\tp = strchr(q, ' ');\n+\t\tif (!p) {\n+\t\t\tpath_list_append(q, list);\n+\t\t\tfree(buf);\n+\t\t\treturn;\n+\t\t} else {\n+\t\t\t*p = '\\0';\n+\t\t\tpath_list_append(q, list);\n+\t\t\tq = ++p;\n+\t\t}\n+\t}\n+}\n+\n+static void add_strategies(const char *string, enum strategy strategy)\n+{\n+\tstruct path_list list;\n+\tint i;\n+\n+\tmemset(&list, 0, sizeof(list));\n+\tsplit_merge_strategies(string, &list);\n+\tif (list.nr) {\n+\t\tfor (i = 0; i < list.nr; i++) {\n+\t\t\tstruct path_list_item *item;\n+\n+\t\t\titem = unsorted_path_list_lookup(list.items[i].path,\n+\t\t\t\t&strategies);\n+\t\t\tif (item)\n+\t\t\t\tpath_list_append_strategy(item);\n+\t\t}\n+\t\treturn;\n+\t}\n+\tfor (i = 0; i < strategies.nr; i++)\n+\t\tif ((enum strategy)strategies.items[i].util & strategy)\n+\t\t\tpath_list_append_strategy(&strategies.items[i]);\n+}\n+\n+int cmd_merge(int argc, const char **argv, const char *prefix)\n+{\n+\tunsigned char sha1[20], result_tree[20];\n+\tstruct object *second_token = NULL;\n+\tstruct strbuf buf, head_arg;\n+\tint flag, head_invalid, i, single_strategy;\n+\tint best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;\n+\tstruct commit_list *common = NULL;\n+\tstruct path_list_item *best_strategy = NULL, *wt_strategy = NULL;\n+\tstruct commit_list **remotes = &remoteheads;\n+\n+\tsetup_work_tree();\n+\tif (unmerged_cache())\n+\t\tdie(\"You are in the middle of a conflicted merge.\");\n+\n+\t/*\n+\t * Check if we are _not_ on a detached HEAD, i.e. if there is a\n+\t * current branch.\n+\t */\n+\tbranch = resolve_ref(\"HEAD\", sha1, 0, &flag);\n+\tif (branch && flag & REF_ISSYMREF) {\n+\t\tconst char *ptr = skip_prefix(branch, \"refs/heads/\");\n+\t\tif (ptr)\n+\t\t\tbranch = ptr;\n+\t}\n+\n+\tgit_config(git_merge_config, NULL);\n+\n+\t/* for color.diff and diff.color */\n+\tgit_config(git_diff_ui_config, NULL);\n+\n+\t/* for color.ui */\n+\tif (diff_use_color_default == -1)\n+\t\tdiff_use_color_default = git_use_color_default;\n+\n+\targc = parse_options(argc, argv, builtin_merge_options,\n+\t\t\tbuiltin_merge_usage, 0);\n+\n+\tif (squash) {\n+\t\tif (!allow_fast_forward)\n+\t\t\tdie(\"You cannot combine --squash with --no-ff.\");\n+\t\toption_commit = 0;\n+\t}\n+\n+\tif (!argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\t/*\n+\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n+\t * the way we can tell it is to see if the second token is HEAD,\n+\t * but some people might have misused the interface and used a\n+\t * committish that is the same as HEAD there instead.\n+\t * Traditional format never would have \"-m\" so it is an\n+\t * additional safety measure to check for it.\n+\t */\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_init(&head_arg, 0);\n+\tif (argc > 1)\n+\t\tsecond_token = peel_to_type(argv[1], 0, NULL, OBJ_COMMIT);\n+\thead_invalid = get_sha1(\"HEAD\", head);\n+\n+\tif (!have_message && second_token &&\n+\t\t!hashcmp(second_token->sha1, head)) {\n+\t\tstrbuf_addstr(&merge_msg, argv[0]);\n+\t\tstrbuf_addstr(&head_arg, argv[1]);\n+\t\targv += 2;\n+\t\targc -= 2;\n+\t} else if (head_invalid) {\n+\t\tstruct object *remote_head;\n+\t\t/*\n+\t\t * If the merged head is a valid one there is no reason\n+\t\t * to forbid \"git merge\" into a branch yet to be born.\n+\t\t * We do the same for \"git pull\".\n+\t\t */\n+\t\tif (argc != 1)\n+\t\t\tdie(\"Can merge only exactly one commit into \"\n+\t\t\t\t\"empty head\");\n+\t\tremote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);\n+\t\tif (!remote_head)\n+\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n+\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\treset_hard(remote_head->sha1, 0);\n+\t\treturn 0;\n+\t} else {\n+\t\t/* We are invoked directly as the first-class UI. */\n+\t\tstrbuf_addstr(&head_arg, \"HEAD\");\n+\t\t/*\n+\t\t * All the rest are the commits being merged;\n+\t\t * prepare the standard merge summary message to\n+\t\t * be appended to the given message.  If remote\n+\t\t * is invalid we will die later in the common\n+\t\t * codepath so we discard the error in this\n+\t\t * loop.\n+\t\t */\n+\t\tstruct strbuf msg;\n+\n+\t\tstrbuf_init(&msg, 0);\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tmerge_name(argv[i], &msg);\n+\t\tfmt_merge_msg(option_log, &msg, &merge_msg);\n+\t\tif (merge_msg.len)\n+\t\t\tstrbuf_setlen(&merge_msg, merge_msg.len-1);\n+\t}\n+\n+\tif (head_invalid || !argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\tstrbuf_addstr(&buf, \"merge\");\n+\tfor (i = 0; i < argc; i++)\n+\t\tstrbuf_addf(&buf, \" %s\", argv[i]);\n+\tsetenv(\"GIT_REFLOG_ACTION\", buf.buf, 0);\n+\tstrbuf_reset(&buf);\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tstruct object *o;\n+\n+\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n+\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n+\t\t\tremotes)->next;\n+\n+\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n+\t\tsetenv(buf.buf, argv[i], 1);\n+\t\tstrbuf_reset(&buf);\n+\t}\n+\n+\tif (!use_strategies.nr) {\n+\t\tif (!remoteheads->next)\n+\t\t\tadd_strategies(pull_twohead, DEFAULT_TWOHEAD);\n+\t\telse\n+\t\t\tadd_strategies(pull_octopus, DEFAULT_OCTOPUS);\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tif ((unsigned int)use_strategies.items[i].util &\n+\t\t\tNO_FAST_FORWARD)\n+\t\t\tallow_fast_forward = 0;\n+\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n+\t\t\tallow_trivial = 0;\n+\t}\n+\n+\tif (!remoteheads->next)\n+\t\tcommon = get_merge_bases(lookup_commit(head),\n+\t\t\t\tremoteheads->item, 1);\n+\telse {\n+\t\tstruct commit_list *list = remoteheads;\n+\t\tcommit_list_insert(lookup_commit(head), &list);\n+\t\tcommon = get_octopus_merge_bases(list);\n+\t\tfree(list);\n+\t}\n+\n+\tupdate_ref(\"updating ORIG_HEAD\", \"ORIG_HEAD\", head, NULL, 0,\n+\t\tDIE_ON_ERR);\n+\n+\tif (!common)\n+\t\t; /* No common ancestors found. We need a real merge. */\n+\telse if (!remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1,\n+\t\tremoteheads->item->object.sha1)) {\n+\t\t/*\n+\t\t * If head can reach all the remote heads then we are up\n+\t\t * to date.\n+\t\t */\n+\t\tfinish_up_to_date(\"Already up-to-date.\");\n+\t\treturn 0;\n+\t} else if (allow_fast_forward && !remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1, head)) {\n+\t\t/* Again the most common case of merging one remote. */\n+\t\tstruct strbuf msg;\n+\t\tstruct object *o;\n+\t\tchar hex[41];\n+\n+\t\tstrcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));\n+\n+\t\tprintf(\"Updating %s..%s\\n\",\n+\t\t\thex,\n+\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n+\t\t\tDEFAULT_ABBREV));\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tstrbuf_initf(&msg, \"%s\", \"Fast forward\");\n+\t\tif (have_message)\n+\t\t\tstrbuf_addstr(&msg,\n+\t\t\t\t\" (no commit created; -m option ignored)\");\n+\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n+\t\t\t0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\treturn 0;\n+\n+\t\tif (merge_one_remote(head, remoteheads->item->object.sha1))\n+\t\t\treturn 0;\n+\n+\t\tfinish(o->sha1, msg.buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t} else if (!remoteheads->next && common->next)\n+\t\t;\n+\t\t/*\n+\t\t * We are not doing octopus and not fast forward.  Need\n+\t\t * a real merge.\n+\t\t */\n+\telse if (!remoteheads->next && option_commit) {\n+\t\t/*\n+\t\t * We are not doing octopus, not fast forward, and have\n+\t\t * only one common.\n+\t\t */\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tif (allow_trivial) {\n+\t\t\t/* See if it is really trivial. */\n+\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n+\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n+\t\t\t\t\thead, remoteheads->item->object.sha1)) {\n+\t\t\t\tunsigned char result_tree[20],\n+\t\t\t\t\tresult_commit[20];\n+\t\t\t\tstruct commit_list parent;\n+\n+\t\t\t\twrite_tree_trivial(result_tree);\n+\t\t\t\tprintf(\"Wonderful.\\n\");\n+\t\t\t\tparent.item = remoteheads->item;\n+\t\t\t\tparent.next = NULL;\n+\t\t\t\tcommit_tree_trivial(merge_msg.buf,\n+\t\t\t\t\tresult_tree, &parent,\n+\t\t\t\t\tresult_commit);\n+\t\t\t\tfinish(result_commit, \"In-index merge\");\n+\t\t\t\tdropsave();\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tprintf(\"Nope.\\n\");\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * An octopus.  If we can reach all the remote we are up\n+\t\t * to date.\n+\t\t */\n+\t\tint up_to_date = 1;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next) {\n+\t\t\tstruct commit_list *common_one;\n+\n+\t\t\t/*\n+\t\t\t * Here we *have* to calculate the individual\n+\t\t\t * merge_bases again, otherwise \"git merge HEAD^\n+\t\t\t * HEAD^^\" would be missed.\n+\t\t\t */\n+\t\t\tcommon_one = get_merge_bases(lookup_commit(head),\n+\t\t\t\tj->item, 1);\n+\t\t\tif (hashcmp(common_one->item->object.sha1,\n+\t\t\t\tj->item->object.sha1)) {\n+\t\t\t\tup_to_date = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (up_to_date) {\n+\t\t\tfinish_up_to_date(\"Already up-to-date. Yeeah!\");\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/* We are going to make a new commit. */\n+\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\n+\t/*\n+\t * At this point, we need a real merge.  No matter what strategy\n+\t * we use, it would operate on the index, possibly affecting the\n+\t * working tree, and when resolved cleanly, have the desired\n+\t * tree in the index -- this means that the index must be in\n+\t * sync with the head commit.  The strategies are responsible\n+\t * to ensure this.\n+\t */\n+\tif (use_strategies.nr != 1) {\n+\t\t/*\n+\t\t * Stash away the local changes so that we can try more\n+\t\t * than one.\n+\t\t */\n+\t\tsave_state();\n+\t\tsingle_strategy = 0;\n+\t} else {\n+\t\tunlink(git_path(\"MERGE_STASH\"));\n+\t\tsingle_strategy = 1;\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tint ret;\n+\t\tif (i) {\n+\t\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\t\trestore_state();\n+\t\t}\n+\t\tif (!single_strategy)\n+\t\t\tprintf(\"Trying merge strategy %s...\\n\",\n+\t\t\t\tuse_strategies.items[i].path);\n+\t\t/*\n+\t\t * Remember which strategy left the state in the working\n+\t\t * tree.\n+\t\t */\n+\t\twt_strategy = &use_strategies.items[i];\n+\n+\t\tret = try_merge_strategy(use_strategies.items[i].path,\n+\t\t\tcommon, &head_arg);\n+\t\tif (!option_commit && !ret) {\n+\t\t\tmerge_was_ok = 1;\n+\t\t\t/*\n+\t\t\t * This is necessary here just to avoid writing\n+\t\t\t * the tree, but later we will *not* exit with\n+\t\t\t * status code 1 because merge_was_ok is set.\n+\t\t\t */\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * The backend exits with 1 when conflicts are\n+\t\t\t * left to be resolved, with 2 when it does not\n+\t\t\t * handle the given merge at all.\n+\t\t\t */\n+\t\t\tif (ret == 1) {\n+\t\t\t\tint cnt = 0;\n+\t\t\t\tstruct rev_info rev;\n+\n+\t\t\t\tif (read_cache() < 0)\n+\t\t\t\t\tdie(\"failed to read the cache\");\n+\n+\t\t\t\t/* Check how many files differ. */\n+\t\t\t\tinit_revisions(&rev, \"\");\n+\t\t\t\tsetup_revisions(0, NULL, &rev, NULL);\n+\t\t\t\trev.diffopt.output_format |=\n+\t\t\t\t\tDIFF_FORMAT_CALLBACK;\n+\t\t\t\trev.diffopt.format_callback = count_diff_files;\n+\t\t\t\trev.diffopt.format_callback_data = &cnt;\n+\t\t\t\trun_diff_files(&rev, 0);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Check how many unmerged entries are\n+\t\t\t\t * there.\n+\t\t\t\t */\n+\t\t\t\tcnt += count_unmerged_entries();\n+\n+\t\t\t\tif (best_cnt <= 0 || cnt <= best_cnt) {\n+\t\t\t\t\tbest_strategy =\n+\t\t\t\t\t\t&use_strategies.items[i];\n+\t\t\t\t\tbest_cnt = cnt;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (merge_was_ok)\n+\t\t\t\tbreak;\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Automerge succeeded. */\n+\t\twrite_tree_trivial(result_tree);\n+\t\tautomerge_was_ok = 1;\n+\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * If we have a resulting tree, that means the strategy module\n+\t * auto resolved the merge cleanly.\n+\t */\n+\tif (automerge_was_ok) {\n+\t\tstruct commit_list *parents = NULL, *j;\n+\t\tunsigned char result_commit[20];\n+\n+\t\tfree_commit_list(common);\n+\t\tif (allow_fast_forward) {\n+\t\t\tparents = remoteheads;\n+\t\t\tcommit_list_insert(lookup_commit(head), &parents);\n+\t\t\tparents = reduce_heads(parents);\n+\t\t} else {\n+\t\t\tstruct commit_list **pptr = &parents;\n+\n+\t\t\tpptr = &commit_list_insert(lookup_commit(head),\n+\t\t\t\tpptr)->next;\n+\t\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\t\tpptr = &commit_list_insert(j->item, pptr)->next;\n+\t\t}\n+\t\tfree_commit_list(remoteheads);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tcommit_tree_trivial(merge_msg.buf, result_tree, parents,\n+\t\t\tresult_commit);\n+\t\tfree_commit_list(parents);\n+\t\tstrbuf_addf(&buf, \"Merge made by %s.\", wt_strategy->path);\n+\t\tfinish(result_commit, buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Pick the result from the best strategy and have the user fix\n+\t * it up.\n+\t */\n+\tif (!best_strategy) {\n+\t\trestore_state();\n+\t\tif (use_strategies.nr > 1)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"No merge strategy handled the merge.\\n\");\n+\t\telse\n+\t\t\tfprintf(stderr, \"Merge with strategy %s failed.\\n\",\n+\t\t\t\tuse_strategies.items[0].path);\n+\t\treturn 2;\n+\t} else if (best_strategy == wt_strategy)\n+\t\t; /* We already have its result in the working tree. */\n+\telse {\n+\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\trestore_state();\n+\t\tprintf(\"Using the %s to prepare resolving by hand.\\n\",\n+\t\t\tbest_strategy->path);\n+\t\ttry_merge_strategy(best_strategy->path, common, &head_arg);\n+\t}\n+\n+\tif (squash)\n+\t\tfinish(NULL, NULL);\n+\telse {\n+\t\tint fd;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\tstrbuf_addf(&buf, \"%s\\n\",\n+\t\t\t\tsha1_to_hex(j->item->object.sha1));\n+\t\tfd = open(git_path(\"MERGE_HEAD\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\",\n+\t\t\t\tgit_path(\"MERGE_HEAD\"));\n+\t\tif (write_in_full(fd, buf.buf, buf.len) != buf.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_HEAD\"));\n+\t\tclose(fd);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tfd = open(git_path(\"MERGE_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tif (write_in_full(fd, merge_msg.buf, merge_msg.len) !=\n+\t\t\tmerge_msg.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_MSG\"));\n+\t\tclose(fd);\n+\t}\n+\n+\tif (merge_was_ok) {\n+\t\tfprintf(stderr, \"Automatic merge went well; \"\n+\t\t\t\"stopped before committing as requested\\n\");\n+\t\treturn 0;\n+\t} else {\n+\t\tFILE *fp;\n+\t\tint pos;\n+\t\tconst char *argv_rerere[] = { \"rerere\", NULL };\n+\n+\t\tfp = fopen(git_path(\"MERGE_MSG\"), \"a\");\n+\t\tif (!fp)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tfprintf(fp, \"\\nConflicts:\\n\");\n+\t\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\t\tstruct cache_entry *ce = active_cache[pos];\n+\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tfprintf(fp, \"\\t%s\\n\", ce->name);\n+\t\t\t\twhile (pos + 1 < active_nr &&\n+\t\t\t\t\t!strcmp(ce->name,\n+\t\t\t\t\tactive_cache[pos + 1]->name))\n+\t\t\t\t\tpos++;\n+\t\t\t}\n+\t\t}\n+\t\tfclose(fp);\n+\t\trun_command_v_opt(argv_rerere, RUN_GIT_CMD);\n+\t\tprintf(\"Automatic merge failed; \"\n+\t\t\t\"fix conflicts and then commit the result.\\n\");\n+\t\treturn 1;\n+\t}\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 2b01fea..8bf5280 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -60,6 +60,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);\n extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);\n+extern int cmd_merge(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_base(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_file(int argc, const char **argv, const char *prefix);\ndiff --git a/git-merge.sh b/contrib/examples/git-merge.sh\nsimilarity index 100%\nrename from git-merge.sh\nrename to contrib/examples/git-merge.sh\ndiff --git a/git.c b/git.c\nindex 2fbe96b..770aadd 100644\n--- a/git.c\n+++ b/git.c\n@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"ls-remote\", cmd_ls_remote },\n \t\t{ \"mailinfo\", cmd_mailinfo },\n \t\t{ \"mailsplit\", cmd_mailsplit },\n+\t\t{ \"merge\", cmd_merge, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n \t\t{ \"merge-file\", cmd_merge_file },\n \t\t{ \"merge-ours\", cmd_merge_ours, RUN_SETUP },\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nindex f3a4bb2..fcb8285 100755\n--- a/t/t7602-merge-octopus-many.sh\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -23,7 +23,7 @@ test_expect_success 'setup' '\n \tdone\n '\n \n-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '\n+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n \tgit reset --hard c1 &&\n \ti=2 &&\n \trefs=\"\" &&\n-- \n1.5.6\n"},{"id":"81438","messageId":"1214586386-26912-1-git-send-email-vmiklos@frugalware.org","threadId":"14186","inReplyTo":"d600b25cb1d8f3b6bce12b2a479d4b68b1935dfd.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 08/15] Add new test to ensure git-merge handles more than 25 refs.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T17:06:26Z","receivedAt":"2008-06-27T17:06:26Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The old shell version handled only 25 refs but we no longer have this\nlimitation. Add a test to make sure this limitation will not be\nintroduced again in the future.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Fri, Jun 27, 2008 at 06:22:01PM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:\n> +test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '\n\nI just noticed this was wrong here. The tests passed because a later\ncommit had a hunk to fix this but this fix is unrelated there. So here\nis the right version.\n\n t/t7602-merge-octopus-many.sh |   52 +++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 52 insertions(+), 0 deletions(-)\n create mode 100755 t/t7602-merge-octopus-many.sh\n\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nnew file mode 100755\nindex 0000000..fcb8285\n--- /dev/null\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -0,0 +1,52 @@\n+#!/bin/sh\n+\n+test_description='git-merge\n+\n+Testing octopus merge with more than 25 refs.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo c0 > c0.c &&\n+\tgit add c0.c &&\n+\tgit commit -m c0 &&\n+\tgit tag c0 &&\n+\ti=1 &&\n+\twhile test $i -le 30\n+\tdo\n+\t\tgit reset --hard c0 &&\n+\t\techo c$i > c$i.c &&\n+\t\tgit add c$i.c &&\n+\t\tgit commit -m c$i &&\n+\t\tgit tag c$i &&\n+\t\ti=`expr $i + 1` || return 1\n+\tdone\n+'\n+\n+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n+\tgit reset --hard c1 &&\n+\ti=2 &&\n+\trefs=\"\" &&\n+\twhile test $i -le 30\n+\tdo\n+\t\trefs=\"$refs c$i\"\n+\t\ti=`expr $i + 1`\n+\tdone\n+\tgit merge $refs &&\n+\ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\n+\ti=1 &&\n+\twhile test $i -le 30\n+\tdo\n+\t\ttest \"$(git rev-parse c$i)\" = \"$(git rev-parse HEAD^$i)\" &&\n+\t\ti=`expr $i + 1` || return 1\n+\tdone &&\n+\tgit diff --exit-code &&\n+\ti=1 &&\n+\twhile test $i -le 30\n+\tdo\n+\t\ttest -f c$i.c &&\n+\t\ti=`expr $i + 1` || return 1\n+\tdone\n+'\n+\n+test_done\n-- \n1.5.6\n"},{"id":"81439","messageId":"40e8a4cdddf757273b39f45fbfaa91fedd4c85a3.1214586273.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"6bfb52304e0bdad4cd9a15b7a2ec98ffdd3de152.1214581610.git.vmiklos@frugalware.org","subject":"[PATCH 15/15] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-27T17:09:09Z","receivedAt":"2008-06-27T17:09:09Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Fri, Jun 27, 2008 at 06:22:08PM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:\n> diff --git a/t/t7602-merge-octopus-many.sh\n> b/t/t7602-merge-octopus-many.sh\n> index f3a4bb2..fcb8285 100755\n> --- a/t/t7602-merge-octopus-many.sh\n> +++ b/t/t7602-merge-octopus-many.sh\n> @@ -23,7 +23,7 @@ test_expect_success 'setup' '\n>       done\n>  '\n>\n> -test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '\n> +test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n>       git reset --hard c1 &&\n>       i=2 &&\n>       refs=\"\" &&\n\nThis is an unrelated hunk, patch below which does not contains this.\n\n Makefile                                      |    2 +-\n builtin-merge.c                               | 1143 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git.c                                         |    1 +\n 5 files changed, 1146 insertions(+), 1 deletions(-)\n create mode 100644 builtin-merge.c\n rename git-merge.sh => contrib/examples/git-merge.sh (100%)\n\ndiff --git a/Makefile b/Makefile\nindex 425d1ca..6c642cd 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n-SCRIPT_SH += git-merge.sh\n SCRIPT_SH += git-merge-stupid.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-parse-remote.sh\n@@ -514,6 +513,7 @@ BUILTIN_OBJS += builtin-ls-remote.o\n BUILTIN_OBJS += builtin-ls-tree.o\n BUILTIN_OBJS += builtin-mailinfo.o\n BUILTIN_OBJS += builtin-mailsplit.o\n+BUILTIN_OBJS += builtin-merge.o\n BUILTIN_OBJS += builtin-merge-base.o\n BUILTIN_OBJS += builtin-merge-file.o\n BUILTIN_OBJS += builtin-merge-ours.o\ndiff --git a/builtin-merge.c b/builtin-merge.c\nnew file mode 100644\nindex 0000000..0d56dca\n--- /dev/null\n+++ b/builtin-merge.c\n@@ -0,0 +1,1143 @@\n+/*\n+ * Builtin \"git merge\"\n+ *\n+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>\n+ *\n+ * Based on git-merge.sh by Junio C Hamano.\n+ */\n+\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"builtin.h\"\n+#include \"run-command.h\"\n+#include \"path-list.h\"\n+#include \"diff.h\"\n+#include \"refs.h\"\n+#include \"commit.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+#include \"unpack-trees.h\"\n+#include \"cache-tree.h\"\n+#include \"dir.h\"\n+#include \"utf8.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+\n+enum strategy {\n+\tDEFAULT_TWOHEAD = 1,\n+\tDEFAULT_OCTOPUS = 2,\n+\tNO_FAST_FORWARD = 4,\n+\tNO_TRIVIAL = 8\n+};\n+\n+static const char * const builtin_merge_usage[] = {\n+\t\"git-merge [options] <remote>...\",\n+\t\"git-merge [options] <msg> HEAD <remote>\",\n+\tNULL\n+};\n+\n+static int show_diffstat = 1, option_log, squash;\n+static int option_commit = 1, allow_fast_forward = 1;\n+static int allow_trivial = 1, have_message;\n+static struct strbuf merge_msg;\n+static struct commit_list *remoteheads;\n+static unsigned char head[20];\n+static struct path_list use_strategies;\n+static const char *branch;\n+\n+static struct path_list_item strategy_items[] = {\n+\t{ \"recur\",      (void *)NO_TRIVIAL },\n+\t{ \"recursive\",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },\n+\t{ \"octopus\",    (void *)DEFAULT_OCTOPUS },\n+\t{ \"resolve\",    (void *)0 },\n+\t{ \"stupid\",     (void *)0 },\n+\t{ \"ours\",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+\t{ \"subtree\",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+};\n+static struct path_list strategies = { strategy_items,\n+\tARRAY_SIZE(strategy_items), 0, 0 };\n+\n+static const char *pull_twohead, *pull_octopus;\n+\n+static int option_parse_message(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tstruct strbuf *buf = opt->value;\n+\n+\tif (unset)\n+\t\tstrbuf_setlen(buf, 0);\n+\telse {\n+\t\tstrbuf_addf(buf, \"%s\\n\\n\", arg);\n+\t\thave_message = 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static struct path_list_item *unsorted_path_list_lookup(const char *path,\n+\tstruct path_list *list)\n+{\n+\tint i;\n+\n+\tif (!path)\n+\t\treturn NULL;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tif (!strcmp(path, list->items[i].path))\n+\t\t\treturn &list->items[i];\n+\treturn NULL;\n+}\n+\n+static inline void path_list_append_strategy(\n+\t\tstruct path_list_item *item)\n+{\n+\tpath_list_append(item->path, &use_strategies)->util = item->util;\n+}\n+\n+static int option_parse_strategy(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tint i;\n+\tstruct path_list_item *item =\n+\t\tunsorted_path_list_lookup(arg, &strategies);\n+\n+\tif (unset)\n+\t\treturn 0;\n+\n+\tif (item)\n+\t\tpath_list_append_strategy(item);\n+\telse {\n+\t\tstruct strbuf err;\n+\t\tstrbuf_init(&err, 0);\n+\t\tfor (i = 0; i < strategies.nr; i++)\n+\t\t\tstrbuf_addf(&err, \" %s\", strategies.items[i].path);\n+\t\tfprintf(stderr, \"Could not find merge strategy '%s'.\\n\", arg);\n+\t\tfprintf(stderr, \"Available strategies are:%s.\\n\", err.buf);\n+\t\texit(1);\n+\t}\n+\treturn 0;\n+}\n+\n+static int option_parse_n(const struct option *opt,\n+\t\tconst char *arg, int unset)\n+{\n+\tshow_diffstat = unset;\n+\treturn 0;\n+}\n+\n+static struct option builtin_merge_options[] = {\n+\t{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,\n+\t\t\"do not show a diffstat at the end of the merge\",\n+\t\tPARSE_OPT_NOARG, option_parse_n },\n+\tOPT_BOOLEAN(0, \"stat\", &show_diffstat,\n+\t\t\"show a diffstat at the end of the merge\"),\n+\tOPT_BOOLEAN(0, \"summary\", &show_diffstat, \"(synonym to --stat)\"),\n+\tOPT_BOOLEAN(0, \"log\", &option_log,\n+\t\t\"add list of one-line log to merge commit message\"),\n+\tOPT_BOOLEAN(0, \"squash\", &squash,\n+\t\t\"create a single commit instead of doing a merge\"),\n+\tOPT_BOOLEAN(0, \"commit\", &option_commit,\n+\t\t\"perform a commit if the merge succeeds (default)\"),\n+\tOPT_BOOLEAN(0, \"ff\", &allow_fast_forward,\n+\t\t\"allow fast forward (default)\"),\n+\tOPT_CALLBACK('s', \"strategy\", &use_strategies, \"strategy\",\n+\t\t\"merge strategy to use\", option_parse_strategy),\n+\tOPT_CALLBACK('m', \"message\", &merge_msg, \"message\",\n+\t\t\"message to be used for the merge commit (if any)\",\n+\t\toption_parse_message),\n+\tOPT_END()\n+};\n+\n+/* Cleans up metadata that is uninteresting after a succeeded merge. */\n+static void dropsave(void)\n+{\n+\tunlink(git_path(\"MERGE_HEAD\"));\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tunlink(git_path(\"MERGE_STASH\"));\n+}\n+\n+static void save_state(void)\n+{\n+\tint fd;\n+\tstruct child_process stash;\n+\tconst char *argv[] = {\"stash\", \"create\", NULL};\n+\n+\tfd = open(git_path(\"MERGE_STASH\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"MERGE_STASH\"));\n+\tmemset(&stash, 0, sizeof(stash));\n+\tstash.argv = argv;\n+\tstash.out = fd;\n+\tstash.git_cmd = 1;\n+\trun_command(&stash);\n+}\n+\n+static void reset_hard(unsigned const char *sha1, int verbose)\n+{\n+\tstruct tree *tree;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.reset = 1;\n+\tif (verbose)\n+\t\topts.verbose_update = 1;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\tdie(\"failed to unpack %s tree object\", sha1_to_hex(sha1));\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tif (unpack_trees(1, &t, &opts))\n+\t\texit(128); /* We've already reported the error, finish dying */\n+}\n+\n+static void restore_state(void)\n+{\n+\tstruct strbuf sb;\n+\tconst char *args[] = { \"stash\", \"apply\", NULL, NULL };\n+\n+\tif (access(git_path(\"MERGE_STASH\"), R_OK) < 0)\n+\t\treturn;\n+\n+\treset_hard(head, 1);\n+\n+\tstrbuf_init(&sb, 0);\n+\tif (strbuf_read_file(&sb, git_path(\"MERGE_STASH\"), 0) < 0)\n+\t\tdie(\"could not read MERGE_STASH: %s\", strerror(errno));\n+\targs[2] = sb.buf;\n+\n+\t/*\n+\t * It is OK to ignore error here, for example when there was\n+\t * nothing to restore.\n+\t */\n+\trun_command_v_opt(args, RUN_GIT_CMD);\n+\n+\tstrbuf_release(&sb);\n+\trefresh_cache(REFRESH_QUIET);\n+}\n+\n+/* This is called when no merge was necessary. */\n+static void finish_up_to_date(const char *msg)\n+{\n+\tprintf(\"%s%s\\n\", squash ? \" (nothing to squash)\" : \"\", msg);\n+\tdropsave();\n+}\n+\n+static void squash_message(void)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf out;\n+\tstruct commit_list *j;\n+\tint fd;\n+\n+\tprintf(\"Squash commit -- not updating HEAD\\n\");\n+\tfd = open(git_path(\"SQUASH_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"SQUASH_MSG\"));\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.ignore_merges = 1;\n+\trev.commit_format = CMIT_FMT_MEDIUM;\n+\n+\tcommit = lookup_commit(head);\n+\tcommit->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&rev, &commit->object, NULL);\n+\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tadd_pending_object(&rev, &j->item->object, NULL);\n+\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\tif (prepare_revision_walk(&rev))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\tstrbuf_initf(&out, \"Squashed commit of the following:\\n\");\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstrbuf_addch(&out, '\\n');\n+\t\tstrbuf_addf(&out, \"commit %s\\n\",\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\t\tpretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,\n+\t\t\tNULL, NULL, rev.date_mode, 0);\n+\t}\n+\twrite(fd, out.buf, out.len);\n+\tclose(fd);\n+\tstrbuf_release(&out);\n+}\n+\n+static int run_hook(const char *name)\n+{\n+\tstruct child_process hook;\n+\tconst char *argv[3], *env[2];\n+\tchar index[PATH_MAX];\n+\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\n+\n+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", get_index_file());\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\tif (squash)\n+\t\targv[1] = \"1\";\n+\telse\n+\t\targv[1] = \"0\";\n+\targv[2] = NULL;\n+\n+\tmemset(&hook, 0, sizeof(hook));\n+\thook.argv = argv;\n+\thook.no_stdin = 1;\n+\thook.stdout_to_stderr = 1;\n+\thook.env = env;\n+\n+\treturn run_command(&hook);\n+}\n+\n+static void finish(const unsigned char *new_head, const char *msg)\n+{\n+\tstruct strbuf reflog_message;\n+\n+\tif (!msg)\n+\t\tstrbuf_initf(&reflog_message, \"%s\",\n+\t\t\tgetenv(\"GIT_REFLOG_ACTION\"));\n+\telse {\n+\t\tprintf(\"%s\\n\", msg);\n+\t\tstrbuf_initf(&reflog_message, \"%s: %s\",\n+\t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n+\t}\n+\tif (squash) {\n+\t\tsquash_message();\n+\t} else {\n+\t\tif (!merge_msg.len)\n+\t\t\tprintf(\"No merge message -- not updating HEAD\\n\");\n+\t\telse {\n+\t\t\tconst char *argv_gc_auto[] = { \"gc\", \"--auto\", NULL };\n+\t\t\tupdate_ref(reflog_message.buf, \"HEAD\",\n+\t\t\t\tnew_head, head, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\t\t/*\n+\t\t\t * We ignore errors in 'gc --auto', since the\n+\t\t\t * user should see them.\n+\t\t\t */\n+\t\t\trun_command_v_opt(argv_gc_auto, RUN_GIT_CMD);\n+\t\t}\n+\t}\n+\tif (new_head && show_diffstat) {\n+\t\tstruct diff_options opts;\n+\t\tdiff_setup(&opts);\n+\t\topts.output_format |=\n+\t\t\tDIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;\n+\t\topts.detect_rename = DIFF_DETECT_RENAME;\n+\t\tif (diff_use_color_default > 0)\n+\t\t\tDIFF_OPT_SET(&opts, COLOR_DIFF);\n+\t\tdiff_tree_sha1(head, new_head, \"\", &opts);\n+\t\tdiffcore_std(&opts);\n+\t\tdiff_flush(&opts);\n+\t}\n+\n+\t/* Run a post-merge hook */\n+\trun_hook(\"post-merge\");\n+\n+\tstrbuf_release(&reflog_message);\n+}\n+\n+/* Get the name for the merge commit's message. */\n+static void merge_name(const char *remote, struct strbuf *msg)\n+{\n+\tstruct object *remote_head;\n+\tunsigned char branch_head[20], buf_sha[20];\n+\tstruct strbuf buf;\n+\tchar *ptr;\n+\tint len = 0;\n+\n+\tmemset(branch_head, 0, sizeof(branch_head));\n+\tremote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);\n+\tif (!remote_head)\n+\t\treturn;\n+\n+\tstrbuf_initf(&buf, \"refs/heads/%s\", remote);\n+\tget_sha1(buf.buf, branch_head);\n+\n+\tif (!hashcmp(remote_head->sha1, branch_head)) {\n+\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n+\t\t\tsha1_to_hex(branch_head), remote);\n+\t\treturn;\n+\t}\n+\t/* See if remote matches <name>~<number>, or <name>^ */\n+\tptr = strrchr(remote, '^');\n+\tif (ptr && ptr[1] == '\\0')\n+\t\tlen = ptr-remote;\n+\telse {\n+\t\tptr = strrchr(remote, '~');\n+\t\tif (ptr && ptr[1] != '0' && isdigit(ptr[1])) {\n+\t\t\tlen = ptr-remote;\n+\t\t\tptr++;\n+\t\t\tfor (ptr++; *ptr; ptr++)\n+\t\t\t\tif (!isdigit(*ptr)) {\n+\t\t\t\t\tlen = 0;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t}\n+\t}\n+\tif (len) {\n+\t\tstruct strbuf truname = STRBUF_INIT;\n+\t\tstrbuf_addstr(&truname, remote);\n+\t\tstrbuf_setlen(&truname, len);\n+\t\tif (!get_sha1(truname.buf, buf_sha)) {\n+\t\t\tstrbuf_addf(msg,\n+\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n+\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(remote, \"FETCH_HEAD\") &&\n+\t\t\t!access(git_path(\"FETCH_HEAD\"), R_OK)) {\n+\t\tFILE *fp;\n+\t\tstruct strbuf line;\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_init(&line, 0);\n+\t\tfp = fopen(git_path(\"FETCH_HEAD\"), \"r\");\n+\t\tif (!fp)\n+\t\t\tdie(\"could not open %s for reading: %s\",\n+\t\t\t\tgit_path(\"FETCH_HEAD\"), strerror(errno));\n+\t\tstrbuf_getline(&line, fp, '\\n');\n+\t\tfclose(fp);\n+\t\tptr = strstr(line.buf, \"\\tnot-for-merge\\t\");\n+\t\tif (ptr)\n+\t\t\tstrbuf_remove(&line, ptr-line.buf+1, 13);\n+\t\tstrbuf_addbuf(msg, &line);\n+\t\tstrbuf_release(&line);\n+\t\treturn;\n+\t}\n+\tstrbuf_addf(msg, \"%s\\t\\tcommit '%s'\\n\",\n+\t\tsha1_to_hex(remote_head->sha1), remote);\n+}\n+\n+int git_merge_config(const char *k, const char *v, void *cb)\n+{\n+\tif (branch && !prefixcmp(k, \"branch.\") &&\n+\t\t!prefixcmp(k + 7, branch) &&\n+\t\t!strcmp(k + 7 + strlen(branch), \".mergeoptions\")) {\n+\t\tconst char **argv;\n+\t\tint argc;\n+\t\tchar *buf;\n+\n+\t\tbuf = xstrdup(v);\n+\t\targc = split_cmdline(buf, &argv);\n+\t\targv = xrealloc(argv, sizeof(*argv) * (argc + 2));\n+\t\tmemmove(argv + 1, argv, sizeof(*argv) * (argc + 1));\n+\t\targc++;\n+\t\tparse_options(argc, argv, builtin_merge_options,\n+\t\t\t      builtin_merge_usage, 0);\n+\t\tfree(buf);\n+\t}\n+\n+\tif (!strcmp(k, \"merge.diffstat\") || !strcmp(k, \"merge.stat\"))\n+\t\tshow_diffstat = git_config_bool(k, v);\n+\telse if (!strcmp(k, \"pull.twohead\"))\n+\t\treturn git_config_string(&pull_twohead, k, v);\n+\telse if (!strcmp(k, \"pull.octopus\"))\n+\t\treturn git_config_string(&pull_octopus, k, v);\n+\treturn 0;\n+}\n+\n+static int read_tree_trivial(unsigned char *common, unsigned char *head,\n+\tunsigned char *one)\n+{\n+\tint i, nr_trees = 0;\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.trivial_merges_only = 1;\n+\topts.merge = 1;\n+\ttrees[nr_trees] = parse_tree_indirect(common);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(one);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\topts.fn = threeway_merge;\n+\tcache_tree_free(&active_cache_tree);\n+\topts.head_idx = 2;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static int commit_tree_trivial(const char *msg, unsigned const char *tree,\n+\t\tstruct commit_list *parents, unsigned char *ret)\n+{\n+\tstruct commit_list *i;\n+\tstruct strbuf buf;\n+\tint encoding_is_utf8;\n+\n+\t/* Not having i18n.commitencoding is the same as having utf-8 */\n+\tencoding_is_utf8 = is_encoding_utf8(git_commit_encoding);\n+\n+\tstrbuf_init(&buf, 8192); /* should avoid reallocs for the headers */\n+\tstrbuf_addf(&buf, \"tree %s\\n\", sha1_to_hex(tree));\n+\n+\tfor (i = parents; i; i = i->next)\n+\t\tstrbuf_addf(&buf, \"parent %s\\n\",\n+\t\t\tsha1_to_hex(i->item->object.sha1));\n+\n+\t/* Person/date information */\n+\tstrbuf_addf(&buf, \"author %s\\n\",\n+\t\tgit_author_info(IDENT_ERROR_ON_NO_NAME));\n+\tstrbuf_addf(&buf, \"committer %s\\n\",\n+\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME));\n+\tif (!encoding_is_utf8)\n+\t\tstrbuf_addf(&buf, \"encoding %s\\n\", git_commit_encoding);\n+\tstrbuf_addch(&buf, '\\n');\n+\n+\t/* And add the comment */\n+\tstrbuf_addstr(&buf, msg);\n+\n+\twrite_sha1_file(buf.buf, buf.len, commit_type, ret);\n+\tstrbuf_release(&buf);\n+\treturn *ret;\n+}\n+\n+static void write_tree_trivial(unsigned char *sha1)\n+{\n+\tif (write_cache_as_tree(sha1, 0, NULL))\n+\t\tdie(\"git write-tree failed to write a tree\");\n+}\n+\n+static int try_merge_strategy(char *strategy, struct commit_list *common,\n+\tstruct strbuf *head_arg)\n+{\n+\tconst char **args;\n+\tint i = 0, ret;\n+\tstruct commit_list *j;\n+\tstruct strbuf buf;\n+\n+\targs = xmalloc((4 + commit_list_count(common) +\n+\t\t\tcommit_list_count(remoteheads)) * sizeof(char *));\n+\tstrbuf_initf(&buf, \"merge-%s\", strategy);\n+\targs[i++] = buf.buf;\n+\tfor (j = common; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i++] = \"--\";\n+\targs[i++] = head_arg->buf;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i] = NULL;\n+\tret = run_command_v_opt(args, RUN_GIT_CMD);\n+\tstrbuf_release(&buf);\n+\ti = 1;\n+\tfor (j = common; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\ti += 2;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\tfree(args);\n+\treturn -ret;\n+}\n+\n+static void count_diff_files(struct diff_queue_struct *q,\n+\t\tstruct diff_options *opt, void *data)\n+{\n+\tint *count = data;\n+\n+\t(*count) += q->nr;\n+}\n+\n+static int count_unmerged_entries(void)\n+{\n+\tconst struct index_state *state = &the_index;\n+\tint i, ret = 0;\n+\n+\tfor (i = 0; i < state->cache_nr; i++)\n+\t\tif (ce_stage(state->cache[i]))\n+\t\t\tret++;\n+\n+\treturn ret;\n+}\n+\n+static int merge_one_remote(unsigned char *head, unsigned char *remote)\n+{\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tint i, fd, nr_trees = 0;\n+\tstruct dir_struct dir;\n+\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\n+\tif (read_cache_unmerged())\n+\t\tdie(\"you need to resolve your current index first\");\n+\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&trees, 0, sizeof(trees));\n+\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(&t, 0, sizeof(t));\n+\tdir.show_ignored = 1;\n+\tdir.exclude_per_dir = \".gitignore\";\n+\topts.dir = &dir;\n+\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.merge = 1;\n+\topts.fn = twoway_merge;\n+\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(remote);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\tcommit_locked_index(lock_file))\n+\t\tdie(\"unable to write new index file\");\n+\treturn 0;\n+}\n+\n+static void split_merge_strategies(const char *string, struct path_list *list)\n+{\n+\tchar *p, *q, *buf;\n+\n+\tif (!string)\n+\t\treturn;\n+\n+\tlist->strdup_paths = 1;\n+\tbuf = xstrdup(string);\n+\tq = buf;\n+\tfor (;;) {\n+\t\tp = strchr(q, ' ');\n+\t\tif (!p) {\n+\t\t\tpath_list_append(q, list);\n+\t\t\tfree(buf);\n+\t\t\treturn;\n+\t\t} else {\n+\t\t\t*p = '\\0';\n+\t\t\tpath_list_append(q, list);\n+\t\t\tq = ++p;\n+\t\t}\n+\t}\n+}\n+\n+static void add_strategies(const char *string, enum strategy strategy)\n+{\n+\tstruct path_list list;\n+\tint i;\n+\n+\tmemset(&list, 0, sizeof(list));\n+\tsplit_merge_strategies(string, &list);\n+\tif (list.nr) {\n+\t\tfor (i = 0; i < list.nr; i++) {\n+\t\t\tstruct path_list_item *item;\n+\n+\t\t\titem = unsorted_path_list_lookup(list.items[i].path,\n+\t\t\t\t&strategies);\n+\t\t\tif (item)\n+\t\t\t\tpath_list_append_strategy(item);\n+\t\t}\n+\t\treturn;\n+\t}\n+\tfor (i = 0; i < strategies.nr; i++)\n+\t\tif ((enum strategy)strategies.items[i].util & strategy)\n+\t\t\tpath_list_append_strategy(&strategies.items[i]);\n+}\n+\n+int cmd_merge(int argc, const char **argv, const char *prefix)\n+{\n+\tunsigned char sha1[20], result_tree[20];\n+\tstruct object *second_token = NULL;\n+\tstruct strbuf buf, head_arg;\n+\tint flag, head_invalid, i, single_strategy;\n+\tint best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;\n+\tstruct commit_list *common = NULL;\n+\tstruct path_list_item *best_strategy = NULL, *wt_strategy = NULL;\n+\tstruct commit_list **remotes = &remoteheads;\n+\n+\tsetup_work_tree();\n+\tif (unmerged_cache())\n+\t\tdie(\"You are in the middle of a conflicted merge.\");\n+\n+\t/*\n+\t * Check if we are _not_ on a detached HEAD, i.e. if there is a\n+\t * current branch.\n+\t */\n+\tbranch = resolve_ref(\"HEAD\", sha1, 0, &flag);\n+\tif (branch && flag & REF_ISSYMREF) {\n+\t\tconst char *ptr = skip_prefix(branch, \"refs/heads/\");\n+\t\tif (ptr)\n+\t\t\tbranch = ptr;\n+\t}\n+\n+\tgit_config(git_merge_config, NULL);\n+\n+\t/* for color.diff and diff.color */\n+\tgit_config(git_diff_ui_config, NULL);\n+\n+\t/* for color.ui */\n+\tif (diff_use_color_default == -1)\n+\t\tdiff_use_color_default = git_use_color_default;\n+\n+\targc = parse_options(argc, argv, builtin_merge_options,\n+\t\t\tbuiltin_merge_usage, 0);\n+\n+\tif (squash) {\n+\t\tif (!allow_fast_forward)\n+\t\t\tdie(\"You cannot combine --squash with --no-ff.\");\n+\t\toption_commit = 0;\n+\t}\n+\n+\tif (!argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\t/*\n+\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n+\t * the way we can tell it is to see if the second token is HEAD,\n+\t * but some people might have misused the interface and used a\n+\t * committish that is the same as HEAD there instead.\n+\t * Traditional format never would have \"-m\" so it is an\n+\t * additional safety measure to check for it.\n+\t */\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_init(&head_arg, 0);\n+\tif (argc > 1)\n+\t\tsecond_token = peel_to_type(argv[1], 0, NULL, OBJ_COMMIT);\n+\thead_invalid = get_sha1(\"HEAD\", head);\n+\n+\tif (!have_message && second_token &&\n+\t\t!hashcmp(second_token->sha1, head)) {\n+\t\tstrbuf_addstr(&merge_msg, argv[0]);\n+\t\tstrbuf_addstr(&head_arg, argv[1]);\n+\t\targv += 2;\n+\t\targc -= 2;\n+\t} else if (head_invalid) {\n+\t\tstruct object *remote_head;\n+\t\t/*\n+\t\t * If the merged head is a valid one there is no reason\n+\t\t * to forbid \"git merge\" into a branch yet to be born.\n+\t\t * We do the same for \"git pull\".\n+\t\t */\n+\t\tif (argc != 1)\n+\t\t\tdie(\"Can merge only exactly one commit into \"\n+\t\t\t\t\"empty head\");\n+\t\tremote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);\n+\t\tif (!remote_head)\n+\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n+\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\treset_hard(remote_head->sha1, 0);\n+\t\treturn 0;\n+\t} else {\n+\t\t/* We are invoked directly as the first-class UI. */\n+\t\tstrbuf_addstr(&head_arg, \"HEAD\");\n+\t\t/*\n+\t\t * All the rest are the commits being merged;\n+\t\t * prepare the standard merge summary message to\n+\t\t * be appended to the given message.  If remote\n+\t\t * is invalid we will die later in the common\n+\t\t * codepath so we discard the error in this\n+\t\t * loop.\n+\t\t */\n+\t\tstruct strbuf msg;\n+\n+\t\tstrbuf_init(&msg, 0);\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tmerge_name(argv[i], &msg);\n+\t\tfmt_merge_msg(option_log, &msg, &merge_msg);\n+\t\tif (merge_msg.len)\n+\t\t\tstrbuf_setlen(&merge_msg, merge_msg.len-1);\n+\t}\n+\n+\tif (head_invalid || !argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\tstrbuf_addstr(&buf, \"merge\");\n+\tfor (i = 0; i < argc; i++)\n+\t\tstrbuf_addf(&buf, \" %s\", argv[i]);\n+\tsetenv(\"GIT_REFLOG_ACTION\", buf.buf, 0);\n+\tstrbuf_reset(&buf);\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tstruct object *o;\n+\n+\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n+\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n+\t\t\tremotes)->next;\n+\n+\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n+\t\tsetenv(buf.buf, argv[i], 1);\n+\t\tstrbuf_reset(&buf);\n+\t}\n+\n+\tif (!use_strategies.nr) {\n+\t\tif (!remoteheads->next)\n+\t\t\tadd_strategies(pull_twohead, DEFAULT_TWOHEAD);\n+\t\telse\n+\t\t\tadd_strategies(pull_octopus, DEFAULT_OCTOPUS);\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tif ((unsigned int)use_strategies.items[i].util &\n+\t\t\tNO_FAST_FORWARD)\n+\t\t\tallow_fast_forward = 0;\n+\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n+\t\t\tallow_trivial = 0;\n+\t}\n+\n+\tif (!remoteheads->next)\n+\t\tcommon = get_merge_bases(lookup_commit(head),\n+\t\t\t\tremoteheads->item, 1);\n+\telse {\n+\t\tstruct commit_list *list = remoteheads;\n+\t\tcommit_list_insert(lookup_commit(head), &list);\n+\t\tcommon = get_octopus_merge_bases(list);\n+\t\tfree(list);\n+\t}\n+\n+\tupdate_ref(\"updating ORIG_HEAD\", \"ORIG_HEAD\", head, NULL, 0,\n+\t\tDIE_ON_ERR);\n+\n+\tif (!common)\n+\t\t; /* No common ancestors found. We need a real merge. */\n+\telse if (!remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1,\n+\t\tremoteheads->item->object.sha1)) {\n+\t\t/*\n+\t\t * If head can reach all the remote heads then we are up\n+\t\t * to date.\n+\t\t */\n+\t\tfinish_up_to_date(\"Already up-to-date.\");\n+\t\treturn 0;\n+\t} else if (allow_fast_forward && !remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1, head)) {\n+\t\t/* Again the most common case of merging one remote. */\n+\t\tstruct strbuf msg;\n+\t\tstruct object *o;\n+\t\tchar hex[41];\n+\n+\t\tstrcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));\n+\n+\t\tprintf(\"Updating %s..%s\\n\",\n+\t\t\thex,\n+\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n+\t\t\tDEFAULT_ABBREV));\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tstrbuf_initf(&msg, \"%s\", \"Fast forward\");\n+\t\tif (have_message)\n+\t\t\tstrbuf_addstr(&msg,\n+\t\t\t\t\" (no commit created; -m option ignored)\");\n+\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n+\t\t\t0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\treturn 0;\n+\n+\t\tif (merge_one_remote(head, remoteheads->item->object.sha1))\n+\t\t\treturn 0;\n+\n+\t\tfinish(o->sha1, msg.buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t} else if (!remoteheads->next && common->next)\n+\t\t;\n+\t\t/*\n+\t\t * We are not doing octopus and not fast forward.  Need\n+\t\t * a real merge.\n+\t\t */\n+\telse if (!remoteheads->next && option_commit) {\n+\t\t/*\n+\t\t * We are not doing octopus, not fast forward, and have\n+\t\t * only one common.\n+\t\t */\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tif (allow_trivial) {\n+\t\t\t/* See if it is really trivial. */\n+\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n+\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n+\t\t\t\t\thead, remoteheads->item->object.sha1)) {\n+\t\t\t\tunsigned char result_tree[20],\n+\t\t\t\t\tresult_commit[20];\n+\t\t\t\tstruct commit_list parent;\n+\n+\t\t\t\twrite_tree_trivial(result_tree);\n+\t\t\t\tprintf(\"Wonderful.\\n\");\n+\t\t\t\tparent.item = remoteheads->item;\n+\t\t\t\tparent.next = NULL;\n+\t\t\t\tcommit_tree_trivial(merge_msg.buf,\n+\t\t\t\t\tresult_tree, &parent,\n+\t\t\t\t\tresult_commit);\n+\t\t\t\tfinish(result_commit, \"In-index merge\");\n+\t\t\t\tdropsave();\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tprintf(\"Nope.\\n\");\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * An octopus.  If we can reach all the remote we are up\n+\t\t * to date.\n+\t\t */\n+\t\tint up_to_date = 1;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next) {\n+\t\t\tstruct commit_list *common_one;\n+\n+\t\t\t/*\n+\t\t\t * Here we *have* to calculate the individual\n+\t\t\t * merge_bases again, otherwise \"git merge HEAD^\n+\t\t\t * HEAD^^\" would be missed.\n+\t\t\t */\n+\t\t\tcommon_one = get_merge_bases(lookup_commit(head),\n+\t\t\t\tj->item, 1);\n+\t\t\tif (hashcmp(common_one->item->object.sha1,\n+\t\t\t\tj->item->object.sha1)) {\n+\t\t\t\tup_to_date = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (up_to_date) {\n+\t\t\tfinish_up_to_date(\"Already up-to-date. Yeeah!\");\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/* We are going to make a new commit. */\n+\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\n+\t/*\n+\t * At this point, we need a real merge.  No matter what strategy\n+\t * we use, it would operate on the index, possibly affecting the\n+\t * working tree, and when resolved cleanly, have the desired\n+\t * tree in the index -- this means that the index must be in\n+\t * sync with the head commit.  The strategies are responsible\n+\t * to ensure this.\n+\t */\n+\tif (use_strategies.nr != 1) {\n+\t\t/*\n+\t\t * Stash away the local changes so that we can try more\n+\t\t * than one.\n+\t\t */\n+\t\tsave_state();\n+\t\tsingle_strategy = 0;\n+\t} else {\n+\t\tunlink(git_path(\"MERGE_STASH\"));\n+\t\tsingle_strategy = 1;\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tint ret;\n+\t\tif (i) {\n+\t\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\t\trestore_state();\n+\t\t}\n+\t\tif (!single_strategy)\n+\t\t\tprintf(\"Trying merge strategy %s...\\n\",\n+\t\t\t\tuse_strategies.items[i].path);\n+\t\t/*\n+\t\t * Remember which strategy left the state in the working\n+\t\t * tree.\n+\t\t */\n+\t\twt_strategy = &use_strategies.items[i];\n+\n+\t\tret = try_merge_strategy(use_strategies.items[i].path,\n+\t\t\tcommon, &head_arg);\n+\t\tif (!option_commit && !ret) {\n+\t\t\tmerge_was_ok = 1;\n+\t\t\t/*\n+\t\t\t * This is necessary here just to avoid writing\n+\t\t\t * the tree, but later we will *not* exit with\n+\t\t\t * status code 1 because merge_was_ok is set.\n+\t\t\t */\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * The backend exits with 1 when conflicts are\n+\t\t\t * left to be resolved, with 2 when it does not\n+\t\t\t * handle the given merge at all.\n+\t\t\t */\n+\t\t\tif (ret == 1) {\n+\t\t\t\tint cnt = 0;\n+\t\t\t\tstruct rev_info rev;\n+\n+\t\t\t\tif (read_cache() < 0)\n+\t\t\t\t\tdie(\"failed to read the cache\");\n+\n+\t\t\t\t/* Check how many files differ. */\n+\t\t\t\tinit_revisions(&rev, \"\");\n+\t\t\t\tsetup_revisions(0, NULL, &rev, NULL);\n+\t\t\t\trev.diffopt.output_format |=\n+\t\t\t\t\tDIFF_FORMAT_CALLBACK;\n+\t\t\t\trev.diffopt.format_callback = count_diff_files;\n+\t\t\t\trev.diffopt.format_callback_data = &cnt;\n+\t\t\t\trun_diff_files(&rev, 0);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Check how many unmerged entries are\n+\t\t\t\t * there.\n+\t\t\t\t */\n+\t\t\t\tcnt += count_unmerged_entries();\n+\n+\t\t\t\tif (best_cnt <= 0 || cnt <= best_cnt) {\n+\t\t\t\t\tbest_strategy =\n+\t\t\t\t\t\t&use_strategies.items[i];\n+\t\t\t\t\tbest_cnt = cnt;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (merge_was_ok)\n+\t\t\t\tbreak;\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Automerge succeeded. */\n+\t\twrite_tree_trivial(result_tree);\n+\t\tautomerge_was_ok = 1;\n+\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * If we have a resulting tree, that means the strategy module\n+\t * auto resolved the merge cleanly.\n+\t */\n+\tif (automerge_was_ok) {\n+\t\tstruct commit_list *parents = NULL, *j;\n+\t\tunsigned char result_commit[20];\n+\n+\t\tfree_commit_list(common);\n+\t\tif (allow_fast_forward) {\n+\t\t\tparents = remoteheads;\n+\t\t\tcommit_list_insert(lookup_commit(head), &parents);\n+\t\t\tparents = reduce_heads(parents);\n+\t\t} else {\n+\t\t\tstruct commit_list **pptr = &parents;\n+\n+\t\t\tpptr = &commit_list_insert(lookup_commit(head),\n+\t\t\t\tpptr)->next;\n+\t\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\t\tpptr = &commit_list_insert(j->item, pptr)->next;\n+\t\t}\n+\t\tfree_commit_list(remoteheads);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tcommit_tree_trivial(merge_msg.buf, result_tree, parents,\n+\t\t\tresult_commit);\n+\t\tfree_commit_list(parents);\n+\t\tstrbuf_addf(&buf, \"Merge made by %s.\", wt_strategy->path);\n+\t\tfinish(result_commit, buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Pick the result from the best strategy and have the user fix\n+\t * it up.\n+\t */\n+\tif (!best_strategy) {\n+\t\trestore_state();\n+\t\tif (use_strategies.nr > 1)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"No merge strategy handled the merge.\\n\");\n+\t\telse\n+\t\t\tfprintf(stderr, \"Merge with strategy %s failed.\\n\",\n+\t\t\t\tuse_strategies.items[0].path);\n+\t\treturn 2;\n+\t} else if (best_strategy == wt_strategy)\n+\t\t; /* We already have its result in the working tree. */\n+\telse {\n+\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\trestore_state();\n+\t\tprintf(\"Using the %s to prepare resolving by hand.\\n\",\n+\t\t\tbest_strategy->path);\n+\t\ttry_merge_strategy(best_strategy->path, common, &head_arg);\n+\t}\n+\n+\tif (squash)\n+\t\tfinish(NULL, NULL);\n+\telse {\n+\t\tint fd;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\tstrbuf_addf(&buf, \"%s\\n\",\n+\t\t\t\tsha1_to_hex(j->item->object.sha1));\n+\t\tfd = open(git_path(\"MERGE_HEAD\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\",\n+\t\t\t\tgit_path(\"MERGE_HEAD\"));\n+\t\tif (write_in_full(fd, buf.buf, buf.len) != buf.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_HEAD\"));\n+\t\tclose(fd);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tfd = open(git_path(\"MERGE_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tif (write_in_full(fd, merge_msg.buf, merge_msg.len) !=\n+\t\t\tmerge_msg.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_MSG\"));\n+\t\tclose(fd);\n+\t}\n+\n+\tif (merge_was_ok) {\n+\t\tfprintf(stderr, \"Automatic merge went well; \"\n+\t\t\t\"stopped before committing as requested\\n\");\n+\t\treturn 0;\n+\t} else {\n+\t\tFILE *fp;\n+\t\tint pos;\n+\t\tconst char *argv_rerere[] = { \"rerere\", NULL };\n+\n+\t\tfp = fopen(git_path(\"MERGE_MSG\"), \"a\");\n+\t\tif (!fp)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tfprintf(fp, \"\\nConflicts:\\n\");\n+\t\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\t\tstruct cache_entry *ce = active_cache[pos];\n+\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tfprintf(fp, \"\\t%s\\n\", ce->name);\n+\t\t\t\twhile (pos + 1 < active_nr &&\n+\t\t\t\t\t!strcmp(ce->name,\n+\t\t\t\t\tactive_cache[pos + 1]->name))\n+\t\t\t\t\tpos++;\n+\t\t\t}\n+\t\t}\n+\t\tfclose(fp);\n+\t\trun_command_v_opt(argv_rerere, RUN_GIT_CMD);\n+\t\tprintf(\"Automatic merge failed; \"\n+\t\t\t\"fix conflicts and then commit the result.\\n\");\n+\t\treturn 1;\n+\t}\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 2b01fea..8bf5280 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -60,6 +60,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);\n extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);\n+extern int cmd_merge(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_base(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_file(int argc, const char **argv, const char *prefix);\ndiff --git a/git-merge.sh b/contrib/examples/git-merge.sh\nsimilarity index 100%\nrename from git-merge.sh\nrename to contrib/examples/git-merge.sh\ndiff --git a/git.c b/git.c\nindex 2fbe96b..770aadd 100644\n--- a/git.c\n+++ b/git.c\n@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"ls-remote\", cmd_ls_remote },\n \t\t{ \"mailinfo\", cmd_mailinfo },\n \t\t{ \"mailsplit\", cmd_mailsplit },\n+\t\t{ \"merge\", cmd_merge, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n \t\t{ \"merge-file\", cmd_merge_file },\n \t\t{ \"merge-ours\", cmd_merge_ours, RUN_SETUP },\n-- \n1.5.6\n"},{"id":"81528","messageId":"7vk5gal3my.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"7ea320cd49601de0e331777f82528876101fd946.1214581610.git.vmiklos@frugalware.org","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-28T02:00:53Z","receivedAt":"2008-06-28T02:00:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Do you really think these two patches belong to the series, I seriously\nhave to wonder?\n\nThe series uses strbuf_initf() from 7 call sites to save a few lines per\neach call site, and for that you have a 140-line patch to strbuf.c (in\naggregate between this one and the \"Oops, I need to support this, too\" one\nafter this).\n\nThis new code that is not essential to the series need to be carefully\nvetted to avoid risks of potentially affecting existing 70+ users of\nstrbuf_addf(), which used to use (hopefully more trustworthy) vsnprintf()\nfrom the system, but now uses the new and unproven strbuf_vaddf() that\nhas \"Only supports %<these>\" disclaimer at the top.\n\nI am not saying the strbuf_vaddf() patch is not worth considering.  It\nmight help platforms without a working vsnprintf().  But its merit needs\nto be defended separately, and I do not want \"merge in C\" series to be\ntaken hostage by it.\n\nOther than that, I did not see anything obviously wrong in the diff\nbetween the previous round and this series.\n\nThanks.\n"},{"id":"81531","messageId":"20080628023355.GH2058@genesis.frugalware.org","threadId":"14186","inReplyTo":"7vk5gal3my.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-28T02:33:55Z","receivedAt":"2008-06-28T02:33:55Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Fri, Jun 27, 2008 at 07:00:53PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Do you really think these two patches belong to the series, I seriously\n> have to wonder?\n\nWell, no. I was suggested to include it there, but you have reason about\nit should be a different series.\n\n> Other than that, I did not see anything obviously wrong in the diff\n> between the previous round and this series.\n\nOK, I'll send the main patch without the strbuf_initf() calls, and then\nyou can just fetch from git/vmiklos.git or drop Dscho's two patches from\nmy topic branch.\n\nThanks.\n"},{"id":"81532","messageId":"ed4b7f44474d6b9398fb0752740ede264bea25b2.1214620551.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"20080628023355.GH2058@genesis.frugalware.org","subject":"[PATCH 13/13] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-28T02:38:19Z","receivedAt":"2008-06-28T02:38:19Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Sat, Jun 28, 2008 at 04:33:55AM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:\n> OK, I'll send the main patch without the strbuf_initf() calls\n\nHere it is.\n\n Makefile                                      |    2 +-\n builtin-merge.c                               | 1148 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git.c                                         |    1 +\n 5 files changed, 1151 insertions(+), 1 deletions(-)\n create mode 100644 builtin-merge.c\n rename git-merge.sh => contrib/examples/git-merge.sh (100%)\n\ndiff --git a/Makefile b/Makefile\nindex 3584b8c..dab29b0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n-SCRIPT_SH += git-merge.sh\n SCRIPT_SH += git-merge-stupid.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-parse-remote.sh\n@@ -514,6 +513,7 @@ BUILTIN_OBJS += builtin-ls-remote.o\n BUILTIN_OBJS += builtin-ls-tree.o\n BUILTIN_OBJS += builtin-mailinfo.o\n BUILTIN_OBJS += builtin-mailsplit.o\n+BUILTIN_OBJS += builtin-merge.o\n BUILTIN_OBJS += builtin-merge-base.o\n BUILTIN_OBJS += builtin-merge-file.o\n BUILTIN_OBJS += builtin-merge-ours.o\ndiff --git a/builtin-merge.c b/builtin-merge.c\nnew file mode 100644\nindex 0000000..39bfbd1\n--- /dev/null\n+++ b/builtin-merge.c\n@@ -0,0 +1,1148 @@\n+/*\n+ * Builtin \"git merge\"\n+ *\n+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>\n+ *\n+ * Based on git-merge.sh by Junio C Hamano.\n+ */\n+\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"builtin.h\"\n+#include \"run-command.h\"\n+#include \"path-list.h\"\n+#include \"diff.h\"\n+#include \"refs.h\"\n+#include \"commit.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+#include \"unpack-trees.h\"\n+#include \"cache-tree.h\"\n+#include \"dir.h\"\n+#include \"utf8.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+\n+enum strategy {\n+\tDEFAULT_TWOHEAD = 1,\n+\tDEFAULT_OCTOPUS = 2,\n+\tNO_FAST_FORWARD = 4,\n+\tNO_TRIVIAL = 8\n+};\n+\n+static const char * const builtin_merge_usage[] = {\n+\t\"git-merge [options] <remote>...\",\n+\t\"git-merge [options] <msg> HEAD <remote>\",\n+\tNULL\n+};\n+\n+static int show_diffstat = 1, option_log, squash;\n+static int option_commit = 1, allow_fast_forward = 1;\n+static int allow_trivial = 1, have_message;\n+static struct strbuf merge_msg;\n+static struct commit_list *remoteheads;\n+static unsigned char head[20];\n+static struct path_list use_strategies;\n+static const char *branch;\n+\n+static struct path_list_item strategy_items[] = {\n+\t{ \"recur\",      (void *)NO_TRIVIAL },\n+\t{ \"recursive\",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },\n+\t{ \"octopus\",    (void *)DEFAULT_OCTOPUS },\n+\t{ \"resolve\",    (void *)0 },\n+\t{ \"stupid\",     (void *)0 },\n+\t{ \"ours\",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+\t{ \"subtree\",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+};\n+static struct path_list strategies = { strategy_items,\n+\tARRAY_SIZE(strategy_items), 0, 0 };\n+\n+static const char *pull_twohead, *pull_octopus;\n+\n+static int option_parse_message(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tstruct strbuf *buf = opt->value;\n+\n+\tif (unset)\n+\t\tstrbuf_setlen(buf, 0);\n+\telse {\n+\t\tstrbuf_addf(buf, \"%s\\n\\n\", arg);\n+\t\thave_message = 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static struct path_list_item *unsorted_path_list_lookup(const char *path,\n+\tstruct path_list *list)\n+{\n+\tint i;\n+\n+\tif (!path)\n+\t\treturn NULL;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tif (!strcmp(path, list->items[i].path))\n+\t\t\treturn &list->items[i];\n+\treturn NULL;\n+}\n+\n+static inline void path_list_append_strategy(\n+\t\tstruct path_list_item *item)\n+{\n+\tpath_list_append(item->path, &use_strategies)->util = item->util;\n+}\n+\n+static int option_parse_strategy(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tint i;\n+\tstruct path_list_item *item =\n+\t\tunsorted_path_list_lookup(arg, &strategies);\n+\n+\tif (unset)\n+\t\treturn 0;\n+\n+\tif (item)\n+\t\tpath_list_append_strategy(item);\n+\telse {\n+\t\tstruct strbuf err;\n+\t\tstrbuf_init(&err, 0);\n+\t\tfor (i = 0; i < strategies.nr; i++)\n+\t\t\tstrbuf_addf(&err, \" %s\", strategies.items[i].path);\n+\t\tfprintf(stderr, \"Could not find merge strategy '%s'.\\n\", arg);\n+\t\tfprintf(stderr, \"Available strategies are:%s.\\n\", err.buf);\n+\t\texit(1);\n+\t}\n+\treturn 0;\n+}\n+\n+static int option_parse_n(const struct option *opt,\n+\t\tconst char *arg, int unset)\n+{\n+\tshow_diffstat = unset;\n+\treturn 0;\n+}\n+\n+static struct option builtin_merge_options[] = {\n+\t{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,\n+\t\t\"do not show a diffstat at the end of the merge\",\n+\t\tPARSE_OPT_NOARG, option_parse_n },\n+\tOPT_BOOLEAN(0, \"stat\", &show_diffstat,\n+\t\t\"show a diffstat at the end of the merge\"),\n+\tOPT_BOOLEAN(0, \"summary\", &show_diffstat, \"(synonym to --stat)\"),\n+\tOPT_BOOLEAN(0, \"log\", &option_log,\n+\t\t\"add list of one-line log to merge commit message\"),\n+\tOPT_BOOLEAN(0, \"squash\", &squash,\n+\t\t\"create a single commit instead of doing a merge\"),\n+\tOPT_BOOLEAN(0, \"commit\", &option_commit,\n+\t\t\"perform a commit if the merge succeeds (default)\"),\n+\tOPT_BOOLEAN(0, \"ff\", &allow_fast_forward,\n+\t\t\"allow fast forward (default)\"),\n+\tOPT_CALLBACK('s', \"strategy\", &use_strategies, \"strategy\",\n+\t\t\"merge strategy to use\", option_parse_strategy),\n+\tOPT_CALLBACK('m', \"message\", &merge_msg, \"message\",\n+\t\t\"message to be used for the merge commit (if any)\",\n+\t\toption_parse_message),\n+\tOPT_END()\n+};\n+\n+/* Cleans up metadata that is uninteresting after a succeeded merge. */\n+static void dropsave(void)\n+{\n+\tunlink(git_path(\"MERGE_HEAD\"));\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tunlink(git_path(\"MERGE_STASH\"));\n+}\n+\n+static void save_state(void)\n+{\n+\tint fd;\n+\tstruct child_process stash;\n+\tconst char *argv[] = {\"stash\", \"create\", NULL};\n+\n+\tfd = open(git_path(\"MERGE_STASH\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"MERGE_STASH\"));\n+\tmemset(&stash, 0, sizeof(stash));\n+\tstash.argv = argv;\n+\tstash.out = fd;\n+\tstash.git_cmd = 1;\n+\trun_command(&stash);\n+}\n+\n+static void reset_hard(unsigned const char *sha1, int verbose)\n+{\n+\tstruct tree *tree;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.reset = 1;\n+\tif (verbose)\n+\t\topts.verbose_update = 1;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\tdie(\"failed to unpack %s tree object\", sha1_to_hex(sha1));\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tif (unpack_trees(1, &t, &opts))\n+\t\texit(128); /* We've already reported the error, finish dying */\n+}\n+\n+static void restore_state(void)\n+{\n+\tstruct strbuf sb;\n+\tconst char *args[] = { \"stash\", \"apply\", NULL, NULL };\n+\n+\tif (access(git_path(\"MERGE_STASH\"), R_OK) < 0)\n+\t\treturn;\n+\n+\treset_hard(head, 1);\n+\n+\tstrbuf_init(&sb, 0);\n+\tif (strbuf_read_file(&sb, git_path(\"MERGE_STASH\"), 0) < 0)\n+\t\tdie(\"could not read MERGE_STASH: %s\", strerror(errno));\n+\targs[2] = sb.buf;\n+\n+\t/*\n+\t * It is OK to ignore error here, for example when there was\n+\t * nothing to restore.\n+\t */\n+\trun_command_v_opt(args, RUN_GIT_CMD);\n+\n+\tstrbuf_release(&sb);\n+\trefresh_cache(REFRESH_QUIET);\n+}\n+\n+/* This is called when no merge was necessary. */\n+static void finish_up_to_date(const char *msg)\n+{\n+\tprintf(\"%s%s\\n\", squash ? \" (nothing to squash)\" : \"\", msg);\n+\tdropsave();\n+}\n+\n+static void squash_message(void)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf out;\n+\tstruct commit_list *j;\n+\tint fd;\n+\n+\tprintf(\"Squash commit -- not updating HEAD\\n\");\n+\tfd = open(git_path(\"SQUASH_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"SQUASH_MSG\"));\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.ignore_merges = 1;\n+\trev.commit_format = CMIT_FMT_MEDIUM;\n+\n+\tcommit = lookup_commit(head);\n+\tcommit->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&rev, &commit->object, NULL);\n+\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tadd_pending_object(&rev, &j->item->object, NULL);\n+\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\tif (prepare_revision_walk(&rev))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\tstrbuf_init(&out, 0);\n+\tstrbuf_addstr(&out, \"Squashed commit of the following:\\n\");\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstrbuf_addch(&out, '\\n');\n+\t\tstrbuf_addf(&out, \"commit %s\\n\",\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\t\tpretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,\n+\t\t\tNULL, NULL, rev.date_mode, 0);\n+\t}\n+\twrite(fd, out.buf, out.len);\n+\tclose(fd);\n+\tstrbuf_release(&out);\n+}\n+\n+static int run_hook(const char *name)\n+{\n+\tstruct child_process hook;\n+\tconst char *argv[3], *env[2];\n+\tchar index[PATH_MAX];\n+\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\n+\n+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", get_index_file());\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\tif (squash)\n+\t\targv[1] = \"1\";\n+\telse\n+\t\targv[1] = \"0\";\n+\targv[2] = NULL;\n+\n+\tmemset(&hook, 0, sizeof(hook));\n+\thook.argv = argv;\n+\thook.no_stdin = 1;\n+\thook.stdout_to_stderr = 1;\n+\thook.env = env;\n+\n+\treturn run_command(&hook);\n+}\n+\n+static void finish(const unsigned char *new_head, const char *msg)\n+{\n+\tstruct strbuf reflog_message;\n+\n+\tstrbuf_init(&reflog_message, 0);\n+\tif (!msg)\n+\t\tstrbuf_addstr(&reflog_message, getenv(\"GIT_REFLOG_ACTION\"));\n+\telse {\n+\t\tprintf(\"%s\\n\", msg);\n+\t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n+\t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n+\t}\n+\tif (squash) {\n+\t\tsquash_message();\n+\t} else {\n+\t\tif (!merge_msg.len)\n+\t\t\tprintf(\"No merge message -- not updating HEAD\\n\");\n+\t\telse {\n+\t\t\tconst char *argv_gc_auto[] = { \"gc\", \"--auto\", NULL };\n+\t\t\tupdate_ref(reflog_message.buf, \"HEAD\",\n+\t\t\t\tnew_head, head, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\t\t/*\n+\t\t\t * We ignore errors in 'gc --auto', since the\n+\t\t\t * user should see them.\n+\t\t\t */\n+\t\t\trun_command_v_opt(argv_gc_auto, RUN_GIT_CMD);\n+\t\t}\n+\t}\n+\tif (new_head && show_diffstat) {\n+\t\tstruct diff_options opts;\n+\t\tdiff_setup(&opts);\n+\t\topts.output_format |=\n+\t\t\tDIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;\n+\t\topts.detect_rename = DIFF_DETECT_RENAME;\n+\t\tif (diff_use_color_default > 0)\n+\t\t\tDIFF_OPT_SET(&opts, COLOR_DIFF);\n+\t\tdiff_tree_sha1(head, new_head, \"\", &opts);\n+\t\tdiffcore_std(&opts);\n+\t\tdiff_flush(&opts);\n+\t}\n+\n+\t/* Run a post-merge hook */\n+\trun_hook(\"post-merge\");\n+\n+\tstrbuf_release(&reflog_message);\n+}\n+\n+/* Get the name for the merge commit's message. */\n+static void merge_name(const char *remote, struct strbuf *msg)\n+{\n+\tstruct object *remote_head;\n+\tunsigned char branch_head[20], buf_sha[20];\n+\tstruct strbuf buf;\n+\tchar *ptr;\n+\tint len = 0;\n+\n+\tmemset(branch_head, 0, sizeof(branch_head));\n+\tremote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);\n+\tif (!remote_head)\n+\t\treturn;\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addstr(&buf, \"refs/heads/\");\n+\tstrbuf_addstr(&buf, remote);\n+\tget_sha1(buf.buf, branch_head);\n+\n+\tif (!hashcmp(remote_head->sha1, branch_head)) {\n+\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n+\t\t\tsha1_to_hex(branch_head), remote);\n+\t\treturn;\n+\t}\n+\t/* See if remote matches <name>~<number>, or <name>^ */\n+\tptr = strrchr(remote, '^');\n+\tif (ptr && ptr[1] == '\\0')\n+\t\tlen = ptr-remote;\n+\telse {\n+\t\tptr = strrchr(remote, '~');\n+\t\tif (ptr && ptr[1] != '0' && isdigit(ptr[1])) {\n+\t\t\tlen = ptr-remote;\n+\t\t\tptr++;\n+\t\t\tfor (ptr++; *ptr; ptr++)\n+\t\t\t\tif (!isdigit(*ptr)) {\n+\t\t\t\t\tlen = 0;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t}\n+\t}\n+\tif (len) {\n+\t\tstruct strbuf truname = STRBUF_INIT;\n+\t\tstrbuf_addstr(&truname, remote);\n+\t\tstrbuf_setlen(&truname, len);\n+\t\tif (!get_sha1(truname.buf, buf_sha)) {\n+\t\t\tstrbuf_addf(msg,\n+\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n+\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(remote, \"FETCH_HEAD\") &&\n+\t\t\t!access(git_path(\"FETCH_HEAD\"), R_OK)) {\n+\t\tFILE *fp;\n+\t\tstruct strbuf line;\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_init(&line, 0);\n+\t\tfp = fopen(git_path(\"FETCH_HEAD\"), \"r\");\n+\t\tif (!fp)\n+\t\t\tdie(\"could not open %s for reading: %s\",\n+\t\t\t\tgit_path(\"FETCH_HEAD\"), strerror(errno));\n+\t\tstrbuf_getline(&line, fp, '\\n');\n+\t\tfclose(fp);\n+\t\tptr = strstr(line.buf, \"\\tnot-for-merge\\t\");\n+\t\tif (ptr)\n+\t\t\tstrbuf_remove(&line, ptr-line.buf+1, 13);\n+\t\tstrbuf_addbuf(msg, &line);\n+\t\tstrbuf_release(&line);\n+\t\treturn;\n+\t}\n+\tstrbuf_addf(msg, \"%s\\t\\tcommit '%s'\\n\",\n+\t\tsha1_to_hex(remote_head->sha1), remote);\n+}\n+\n+int git_merge_config(const char *k, const char *v, void *cb)\n+{\n+\tif (branch && !prefixcmp(k, \"branch.\") &&\n+\t\t!prefixcmp(k + 7, branch) &&\n+\t\t!strcmp(k + 7 + strlen(branch), \".mergeoptions\")) {\n+\t\tconst char **argv;\n+\t\tint argc;\n+\t\tchar *buf;\n+\n+\t\tbuf = xstrdup(v);\n+\t\targc = split_cmdline(buf, &argv);\n+\t\targv = xrealloc(argv, sizeof(*argv) * (argc + 2));\n+\t\tmemmove(argv + 1, argv, sizeof(*argv) * (argc + 1));\n+\t\targc++;\n+\t\tparse_options(argc, argv, builtin_merge_options,\n+\t\t\t      builtin_merge_usage, 0);\n+\t\tfree(buf);\n+\t}\n+\n+\tif (!strcmp(k, \"merge.diffstat\") || !strcmp(k, \"merge.stat\"))\n+\t\tshow_diffstat = git_config_bool(k, v);\n+\telse if (!strcmp(k, \"pull.twohead\"))\n+\t\treturn git_config_string(&pull_twohead, k, v);\n+\telse if (!strcmp(k, \"pull.octopus\"))\n+\t\treturn git_config_string(&pull_octopus, k, v);\n+\treturn 0;\n+}\n+\n+static int read_tree_trivial(unsigned char *common, unsigned char *head,\n+\tunsigned char *one)\n+{\n+\tint i, nr_trees = 0;\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.trivial_merges_only = 1;\n+\topts.merge = 1;\n+\ttrees[nr_trees] = parse_tree_indirect(common);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(one);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\topts.fn = threeway_merge;\n+\tcache_tree_free(&active_cache_tree);\n+\topts.head_idx = 2;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static int commit_tree_trivial(const char *msg, unsigned const char *tree,\n+\t\tstruct commit_list *parents, unsigned char *ret)\n+{\n+\tstruct commit_list *i;\n+\tstruct strbuf buf;\n+\tint encoding_is_utf8;\n+\n+\t/* Not having i18n.commitencoding is the same as having utf-8 */\n+\tencoding_is_utf8 = is_encoding_utf8(git_commit_encoding);\n+\n+\tstrbuf_init(&buf, 8192); /* should avoid reallocs for the headers */\n+\tstrbuf_addf(&buf, \"tree %s\\n\", sha1_to_hex(tree));\n+\n+\tfor (i = parents; i; i = i->next)\n+\t\tstrbuf_addf(&buf, \"parent %s\\n\",\n+\t\t\tsha1_to_hex(i->item->object.sha1));\n+\n+\t/* Person/date information */\n+\tstrbuf_addf(&buf, \"author %s\\n\",\n+\t\tgit_author_info(IDENT_ERROR_ON_NO_NAME));\n+\tstrbuf_addf(&buf, \"committer %s\\n\",\n+\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME));\n+\tif (!encoding_is_utf8)\n+\t\tstrbuf_addf(&buf, \"encoding %s\\n\", git_commit_encoding);\n+\tstrbuf_addch(&buf, '\\n');\n+\n+\t/* And add the comment */\n+\tstrbuf_addstr(&buf, msg);\n+\n+\twrite_sha1_file(buf.buf, buf.len, commit_type, ret);\n+\tstrbuf_release(&buf);\n+\treturn *ret;\n+}\n+\n+static void write_tree_trivial(unsigned char *sha1)\n+{\n+\tif (write_cache_as_tree(sha1, 0, NULL))\n+\t\tdie(\"git write-tree failed to write a tree\");\n+}\n+\n+static int try_merge_strategy(char *strategy, struct commit_list *common,\n+\tstruct strbuf *head_arg)\n+{\n+\tconst char **args;\n+\tint i = 0, ret;\n+\tstruct commit_list *j;\n+\tstruct strbuf buf;\n+\n+\targs = xmalloc((4 + commit_list_count(common) +\n+\t\t\tcommit_list_count(remoteheads)) * sizeof(char *));\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"merge-%s\", strategy);\n+\targs[i++] = buf.buf;\n+\tfor (j = common; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i++] = \"--\";\n+\targs[i++] = head_arg->buf;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i] = NULL;\n+\tret = run_command_v_opt(args, RUN_GIT_CMD);\n+\tstrbuf_release(&buf);\n+\ti = 1;\n+\tfor (j = common; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\ti += 2;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\tfree(args);\n+\treturn -ret;\n+}\n+\n+static void count_diff_files(struct diff_queue_struct *q,\n+\t\tstruct diff_options *opt, void *data)\n+{\n+\tint *count = data;\n+\n+\t(*count) += q->nr;\n+}\n+\n+static int count_unmerged_entries(void)\n+{\n+\tconst struct index_state *state = &the_index;\n+\tint i, ret = 0;\n+\n+\tfor (i = 0; i < state->cache_nr; i++)\n+\t\tif (ce_stage(state->cache[i]))\n+\t\t\tret++;\n+\n+\treturn ret;\n+}\n+\n+static int merge_one_remote(unsigned char *head, unsigned char *remote)\n+{\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tint i, fd, nr_trees = 0;\n+\tstruct dir_struct dir;\n+\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\n+\tif (read_cache_unmerged())\n+\t\tdie(\"you need to resolve your current index first\");\n+\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&trees, 0, sizeof(trees));\n+\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(&t, 0, sizeof(t));\n+\tdir.show_ignored = 1;\n+\tdir.exclude_per_dir = \".gitignore\";\n+\topts.dir = &dir;\n+\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.merge = 1;\n+\topts.fn = twoway_merge;\n+\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(remote);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\tcommit_locked_index(lock_file))\n+\t\tdie(\"unable to write new index file\");\n+\treturn 0;\n+}\n+\n+static void split_merge_strategies(const char *string, struct path_list *list)\n+{\n+\tchar *p, *q, *buf;\n+\n+\tif (!string)\n+\t\treturn;\n+\n+\tlist->strdup_paths = 1;\n+\tbuf = xstrdup(string);\n+\tq = buf;\n+\tfor (;;) {\n+\t\tp = strchr(q, ' ');\n+\t\tif (!p) {\n+\t\t\tpath_list_append(q, list);\n+\t\t\tfree(buf);\n+\t\t\treturn;\n+\t\t} else {\n+\t\t\t*p = '\\0';\n+\t\t\tpath_list_append(q, list);\n+\t\t\tq = ++p;\n+\t\t}\n+\t}\n+}\n+\n+static void add_strategies(const char *string, enum strategy strategy)\n+{\n+\tstruct path_list list;\n+\tint i;\n+\n+\tmemset(&list, 0, sizeof(list));\n+\tsplit_merge_strategies(string, &list);\n+\tif (list.nr) {\n+\t\tfor (i = 0; i < list.nr; i++) {\n+\t\t\tstruct path_list_item *item;\n+\n+\t\t\titem = unsorted_path_list_lookup(list.items[i].path,\n+\t\t\t\t&strategies);\n+\t\t\tif (item)\n+\t\t\t\tpath_list_append_strategy(item);\n+\t\t}\n+\t\treturn;\n+\t}\n+\tfor (i = 0; i < strategies.nr; i++)\n+\t\tif ((enum strategy)strategies.items[i].util & strategy)\n+\t\t\tpath_list_append_strategy(&strategies.items[i]);\n+}\n+\n+int cmd_merge(int argc, const char **argv, const char *prefix)\n+{\n+\tunsigned char sha1[20], result_tree[20];\n+\tstruct object *second_token = NULL;\n+\tstruct strbuf buf, head_arg;\n+\tint flag, head_invalid, i, single_strategy;\n+\tint best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;\n+\tstruct commit_list *common = NULL;\n+\tstruct path_list_item *best_strategy = NULL, *wt_strategy = NULL;\n+\tstruct commit_list **remotes = &remoteheads;\n+\n+\tsetup_work_tree();\n+\tif (unmerged_cache())\n+\t\tdie(\"You are in the middle of a conflicted merge.\");\n+\n+\t/*\n+\t * Check if we are _not_ on a detached HEAD, i.e. if there is a\n+\t * current branch.\n+\t */\n+\tbranch = resolve_ref(\"HEAD\", sha1, 0, &flag);\n+\tif (branch && flag & REF_ISSYMREF) {\n+\t\tconst char *ptr = skip_prefix(branch, \"refs/heads/\");\n+\t\tif (ptr)\n+\t\t\tbranch = ptr;\n+\t}\n+\n+\tgit_config(git_merge_config, NULL);\n+\n+\t/* for color.diff and diff.color */\n+\tgit_config(git_diff_ui_config, NULL);\n+\n+\t/* for color.ui */\n+\tif (diff_use_color_default == -1)\n+\t\tdiff_use_color_default = git_use_color_default;\n+\n+\targc = parse_options(argc, argv, builtin_merge_options,\n+\t\t\tbuiltin_merge_usage, 0);\n+\n+\tif (squash) {\n+\t\tif (!allow_fast_forward)\n+\t\t\tdie(\"You cannot combine --squash with --no-ff.\");\n+\t\toption_commit = 0;\n+\t}\n+\n+\tif (!argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\t/*\n+\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n+\t * the way we can tell it is to see if the second token is HEAD,\n+\t * but some people might have misused the interface and used a\n+\t * committish that is the same as HEAD there instead.\n+\t * Traditional format never would have \"-m\" so it is an\n+\t * additional safety measure to check for it.\n+\t */\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_init(&head_arg, 0);\n+\tif (argc > 1)\n+\t\tsecond_token = peel_to_type(argv[1], 0, NULL, OBJ_COMMIT);\n+\thead_invalid = get_sha1(\"HEAD\", head);\n+\n+\tif (!have_message && second_token &&\n+\t\t!hashcmp(second_token->sha1, head)) {\n+\t\tstrbuf_addstr(&merge_msg, argv[0]);\n+\t\tstrbuf_addstr(&head_arg, argv[1]);\n+\t\targv += 2;\n+\t\targc -= 2;\n+\t} else if (head_invalid) {\n+\t\tstruct object *remote_head;\n+\t\t/*\n+\t\t * If the merged head is a valid one there is no reason\n+\t\t * to forbid \"git merge\" into a branch yet to be born.\n+\t\t * We do the same for \"git pull\".\n+\t\t */\n+\t\tif (argc != 1)\n+\t\t\tdie(\"Can merge only exactly one commit into \"\n+\t\t\t\t\"empty head\");\n+\t\tremote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);\n+\t\tif (!remote_head)\n+\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n+\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\treset_hard(remote_head->sha1, 0);\n+\t\treturn 0;\n+\t} else {\n+\t\t/* We are invoked directly as the first-class UI. */\n+\t\tstrbuf_addstr(&head_arg, \"HEAD\");\n+\t\t/*\n+\t\t * All the rest are the commits being merged;\n+\t\t * prepare the standard merge summary message to\n+\t\t * be appended to the given message.  If remote\n+\t\t * is invalid we will die later in the common\n+\t\t * codepath so we discard the error in this\n+\t\t * loop.\n+\t\t */\n+\t\tstruct strbuf msg;\n+\n+\t\tstrbuf_init(&msg, 0);\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tmerge_name(argv[i], &msg);\n+\t\tfmt_merge_msg(option_log, &msg, &merge_msg);\n+\t\tif (merge_msg.len)\n+\t\t\tstrbuf_setlen(&merge_msg, merge_msg.len-1);\n+\t}\n+\n+\tif (head_invalid || !argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\tstrbuf_addstr(&buf, \"merge\");\n+\tfor (i = 0; i < argc; i++)\n+\t\tstrbuf_addf(&buf, \" %s\", argv[i]);\n+\tsetenv(\"GIT_REFLOG_ACTION\", buf.buf, 0);\n+\tstrbuf_reset(&buf);\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tstruct object *o;\n+\n+\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n+\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n+\t\t\tremotes)->next;\n+\n+\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n+\t\tsetenv(buf.buf, argv[i], 1);\n+\t\tstrbuf_reset(&buf);\n+\t}\n+\n+\tif (!use_strategies.nr) {\n+\t\tif (!remoteheads->next)\n+\t\t\tadd_strategies(pull_twohead, DEFAULT_TWOHEAD);\n+\t\telse\n+\t\t\tadd_strategies(pull_octopus, DEFAULT_OCTOPUS);\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tif ((unsigned int)use_strategies.items[i].util &\n+\t\t\tNO_FAST_FORWARD)\n+\t\t\tallow_fast_forward = 0;\n+\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n+\t\t\tallow_trivial = 0;\n+\t}\n+\n+\tif (!remoteheads->next)\n+\t\tcommon = get_merge_bases(lookup_commit(head),\n+\t\t\t\tremoteheads->item, 1);\n+\telse {\n+\t\tstruct commit_list *list = remoteheads;\n+\t\tcommit_list_insert(lookup_commit(head), &list);\n+\t\tcommon = get_octopus_merge_bases(list);\n+\t\tfree(list);\n+\t}\n+\n+\tupdate_ref(\"updating ORIG_HEAD\", \"ORIG_HEAD\", head, NULL, 0,\n+\t\tDIE_ON_ERR);\n+\n+\tif (!common)\n+\t\t; /* No common ancestors found. We need a real merge. */\n+\telse if (!remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1,\n+\t\tremoteheads->item->object.sha1)) {\n+\t\t/*\n+\t\t * If head can reach all the remote heads then we are up\n+\t\t * to date.\n+\t\t */\n+\t\tfinish_up_to_date(\"Already up-to-date.\");\n+\t\treturn 0;\n+\t} else if (allow_fast_forward && !remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1, head)) {\n+\t\t/* Again the most common case of merging one remote. */\n+\t\tstruct strbuf msg;\n+\t\tstruct object *o;\n+\t\tchar hex[41];\n+\n+\t\tstrcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));\n+\n+\t\tprintf(\"Updating %s..%s\\n\",\n+\t\t\thex,\n+\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n+\t\t\tDEFAULT_ABBREV));\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tstrbuf_init(&msg, 0);\n+\t\tstrbuf_addstr(&msg, \"Fast forward\");\n+\t\tif (have_message)\n+\t\t\tstrbuf_addstr(&msg,\n+\t\t\t\t\" (no commit created; -m option ignored)\");\n+\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n+\t\t\t0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\treturn 0;\n+\n+\t\tif (merge_one_remote(head, remoteheads->item->object.sha1))\n+\t\t\treturn 0;\n+\n+\t\tfinish(o->sha1, msg.buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t} else if (!remoteheads->next && common->next)\n+\t\t;\n+\t\t/*\n+\t\t * We are not doing octopus and not fast forward.  Need\n+\t\t * a real merge.\n+\t\t */\n+\telse if (!remoteheads->next && option_commit) {\n+\t\t/*\n+\t\t * We are not doing octopus, not fast forward, and have\n+\t\t * only one common.\n+\t\t */\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tif (allow_trivial) {\n+\t\t\t/* See if it is really trivial. */\n+\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n+\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n+\t\t\t\t\thead, remoteheads->item->object.sha1)) {\n+\t\t\t\tunsigned char result_tree[20],\n+\t\t\t\t\tresult_commit[20];\n+\t\t\t\tstruct commit_list parent;\n+\n+\t\t\t\twrite_tree_trivial(result_tree);\n+\t\t\t\tprintf(\"Wonderful.\\n\");\n+\t\t\t\tparent.item = remoteheads->item;\n+\t\t\t\tparent.next = NULL;\n+\t\t\t\tcommit_tree_trivial(merge_msg.buf,\n+\t\t\t\t\tresult_tree, &parent,\n+\t\t\t\t\tresult_commit);\n+\t\t\t\tfinish(result_commit, \"In-index merge\");\n+\t\t\t\tdropsave();\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tprintf(\"Nope.\\n\");\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * An octopus.  If we can reach all the remote we are up\n+\t\t * to date.\n+\t\t */\n+\t\tint up_to_date = 1;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next) {\n+\t\t\tstruct commit_list *common_one;\n+\n+\t\t\t/*\n+\t\t\t * Here we *have* to calculate the individual\n+\t\t\t * merge_bases again, otherwise \"git merge HEAD^\n+\t\t\t * HEAD^^\" would be missed.\n+\t\t\t */\n+\t\t\tcommon_one = get_merge_bases(lookup_commit(head),\n+\t\t\t\tj->item, 1);\n+\t\t\tif (hashcmp(common_one->item->object.sha1,\n+\t\t\t\tj->item->object.sha1)) {\n+\t\t\t\tup_to_date = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (up_to_date) {\n+\t\t\tfinish_up_to_date(\"Already up-to-date. Yeeah!\");\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/* We are going to make a new commit. */\n+\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\n+\t/*\n+\t * At this point, we need a real merge.  No matter what strategy\n+\t * we use, it would operate on the index, possibly affecting the\n+\t * working tree, and when resolved cleanly, have the desired\n+\t * tree in the index -- this means that the index must be in\n+\t * sync with the head commit.  The strategies are responsible\n+\t * to ensure this.\n+\t */\n+\tif (use_strategies.nr != 1) {\n+\t\t/*\n+\t\t * Stash away the local changes so that we can try more\n+\t\t * than one.\n+\t\t */\n+\t\tsave_state();\n+\t\tsingle_strategy = 0;\n+\t} else {\n+\t\tunlink(git_path(\"MERGE_STASH\"));\n+\t\tsingle_strategy = 1;\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tint ret;\n+\t\tif (i) {\n+\t\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\t\trestore_state();\n+\t\t}\n+\t\tif (!single_strategy)\n+\t\t\tprintf(\"Trying merge strategy %s...\\n\",\n+\t\t\t\tuse_strategies.items[i].path);\n+\t\t/*\n+\t\t * Remember which strategy left the state in the working\n+\t\t * tree.\n+\t\t */\n+\t\twt_strategy = &use_strategies.items[i];\n+\n+\t\tret = try_merge_strategy(use_strategies.items[i].path,\n+\t\t\tcommon, &head_arg);\n+\t\tif (!option_commit && !ret) {\n+\t\t\tmerge_was_ok = 1;\n+\t\t\t/*\n+\t\t\t * This is necessary here just to avoid writing\n+\t\t\t * the tree, but later we will *not* exit with\n+\t\t\t * status code 1 because merge_was_ok is set.\n+\t\t\t */\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * The backend exits with 1 when conflicts are\n+\t\t\t * left to be resolved, with 2 when it does not\n+\t\t\t * handle the given merge at all.\n+\t\t\t */\n+\t\t\tif (ret == 1) {\n+\t\t\t\tint cnt = 0;\n+\t\t\t\tstruct rev_info rev;\n+\n+\t\t\t\tif (read_cache() < 0)\n+\t\t\t\t\tdie(\"failed to read the cache\");\n+\n+\t\t\t\t/* Check how many files differ. */\n+\t\t\t\tinit_revisions(&rev, \"\");\n+\t\t\t\tsetup_revisions(0, NULL, &rev, NULL);\n+\t\t\t\trev.diffopt.output_format |=\n+\t\t\t\t\tDIFF_FORMAT_CALLBACK;\n+\t\t\t\trev.diffopt.format_callback = count_diff_files;\n+\t\t\t\trev.diffopt.format_callback_data = &cnt;\n+\t\t\t\trun_diff_files(&rev, 0);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Check how many unmerged entries are\n+\t\t\t\t * there.\n+\t\t\t\t */\n+\t\t\t\tcnt += count_unmerged_entries();\n+\n+\t\t\t\tif (best_cnt <= 0 || cnt <= best_cnt) {\n+\t\t\t\t\tbest_strategy =\n+\t\t\t\t\t\t&use_strategies.items[i];\n+\t\t\t\t\tbest_cnt = cnt;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (merge_was_ok)\n+\t\t\t\tbreak;\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Automerge succeeded. */\n+\t\twrite_tree_trivial(result_tree);\n+\t\tautomerge_was_ok = 1;\n+\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * If we have a resulting tree, that means the strategy module\n+\t * auto resolved the merge cleanly.\n+\t */\n+\tif (automerge_was_ok) {\n+\t\tstruct commit_list *parents = NULL, *j;\n+\t\tunsigned char result_commit[20];\n+\n+\t\tfree_commit_list(common);\n+\t\tif (allow_fast_forward) {\n+\t\t\tparents = remoteheads;\n+\t\t\tcommit_list_insert(lookup_commit(head), &parents);\n+\t\t\tparents = reduce_heads(parents);\n+\t\t} else {\n+\t\t\tstruct commit_list **pptr = &parents;\n+\n+\t\t\tpptr = &commit_list_insert(lookup_commit(head),\n+\t\t\t\tpptr)->next;\n+\t\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\t\tpptr = &commit_list_insert(j->item, pptr)->next;\n+\t\t}\n+\t\tfree_commit_list(remoteheads);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tcommit_tree_trivial(merge_msg.buf, result_tree, parents,\n+\t\t\tresult_commit);\n+\t\tfree_commit_list(parents);\n+\t\tstrbuf_addf(&buf, \"Merge made by %s.\", wt_strategy->path);\n+\t\tfinish(result_commit, buf.buf);\n+\t\tstrbuf_release(&buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * Pick the result from the best strategy and have the user fix\n+\t * it up.\n+\t */\n+\tif (!best_strategy) {\n+\t\trestore_state();\n+\t\tif (use_strategies.nr > 1)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"No merge strategy handled the merge.\\n\");\n+\t\telse\n+\t\t\tfprintf(stderr, \"Merge with strategy %s failed.\\n\",\n+\t\t\t\tuse_strategies.items[0].path);\n+\t\treturn 2;\n+\t} else if (best_strategy == wt_strategy)\n+\t\t; /* We already have its result in the working tree. */\n+\telse {\n+\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\trestore_state();\n+\t\tprintf(\"Using the %s to prepare resolving by hand.\\n\",\n+\t\t\tbest_strategy->path);\n+\t\ttry_merge_strategy(best_strategy->path, common, &head_arg);\n+\t}\n+\n+\tif (squash)\n+\t\tfinish(NULL, NULL);\n+\telse {\n+\t\tint fd;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\tstrbuf_addf(&buf, \"%s\\n\",\n+\t\t\t\tsha1_to_hex(j->item->object.sha1));\n+\t\tfd = open(git_path(\"MERGE_HEAD\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\",\n+\t\t\t\tgit_path(\"MERGE_HEAD\"));\n+\t\tif (write_in_full(fd, buf.buf, buf.len) != buf.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_HEAD\"));\n+\t\tclose(fd);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tfd = open(git_path(\"MERGE_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tif (write_in_full(fd, merge_msg.buf, merge_msg.len) !=\n+\t\t\tmerge_msg.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_MSG\"));\n+\t\tclose(fd);\n+\t}\n+\n+\tif (merge_was_ok) {\n+\t\tfprintf(stderr, \"Automatic merge went well; \"\n+\t\t\t\"stopped before committing as requested\\n\");\n+\t\treturn 0;\n+\t} else {\n+\t\tFILE *fp;\n+\t\tint pos;\n+\t\tconst char *argv_rerere[] = { \"rerere\", NULL };\n+\n+\t\tfp = fopen(git_path(\"MERGE_MSG\"), \"a\");\n+\t\tif (!fp)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tfprintf(fp, \"\\nConflicts:\\n\");\n+\t\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\t\tstruct cache_entry *ce = active_cache[pos];\n+\n+\t\t\tif (ce_stage(ce)) {\n+\t\t\t\tfprintf(fp, \"\\t%s\\n\", ce->name);\n+\t\t\t\twhile (pos + 1 < active_nr &&\n+\t\t\t\t\t!strcmp(ce->name,\n+\t\t\t\t\tactive_cache[pos + 1]->name))\n+\t\t\t\t\tpos++;\n+\t\t\t}\n+\t\t}\n+\t\tfclose(fp);\n+\t\trun_command_v_opt(argv_rerere, RUN_GIT_CMD);\n+\t\tprintf(\"Automatic merge failed; \"\n+\t\t\t\"fix conflicts and then commit the result.\\n\");\n+\t\treturn 1;\n+\t}\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 2b01fea..8bf5280 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -60,6 +60,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);\n extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);\n+extern int cmd_merge(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_base(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_file(int argc, const char **argv, const char *prefix);\ndiff --git a/git-merge.sh b/contrib/examples/git-merge.sh\nsimilarity index 100%\nrename from git-merge.sh\nrename to contrib/examples/git-merge.sh\ndiff --git a/git.c b/git.c\nindex 2fbe96b..770aadd 100644\n--- a/git.c\n+++ b/git.c\n@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"ls-remote\", cmd_ls_remote },\n \t\t{ \"mailinfo\", cmd_mailinfo },\n \t\t{ \"mailsplit\", cmd_mailsplit },\n+\t\t{ \"merge\", cmd_merge, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n \t\t{ \"merge-file\", cmd_merge_file },\n \t\t{ \"merge-ours\", cmd_merge_ours, RUN_SETUP },\n-- \n1.5.6\n"},{"id":"81572","messageId":"alpine.DEB.1.00.0806281832510.9925@racer","threadId":"14186","inReplyTo":"7vk5gal3my.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-28T17:33:49Z","receivedAt":"2008-06-28T17:33:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 27 Jun 2008, Junio C Hamano wrote:\n\n> Do you really think these two patches belong to the series, I seriously \n> have to wonder?\n\nHeh.  I suggested it, as I thought this was a perfect excuse to introduce \nstrbuf_initf(), which has been on my wishlist since long.\n\nAnyway, I'm fine with having them only in my personal fork.\n\nCiao,\nDscho\n"},{"id":"81599","messageId":"7vprq0fzum.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"ed4b7f44474d6b9398fb0752740ede264bea25b2.1214620551.git.vmiklos@frugalware.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-29T07:46:09Z","receivedAt":"2008-06-29T07:46:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n> ---\n>\n> On Sat, Jun 28, 2008 at 04:33:55AM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:\n>> OK, I'll send the main patch without the strbuf_initf() calls\n>\n> Here it is.\n\nSorry, this file is huge and very time consuming to review.  Here are some\npieces.  The parts I did not comment I haven't looked at.\n\n> +/* Get the name for the merge commit's message. */\n> +static void merge_name(const char *remote, struct strbuf *msg)\n> +{\n> +\tstruct object *remote_head;\n> +\tunsigned char branch_head[20], buf_sha[20];\n> +\tstruct strbuf buf;\n> +\tchar *ptr;\n> +\tint len = 0;\n> +\n> +\tmemset(branch_head, 0, sizeof(branch_head));\n> +\tremote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);\n> +\tif (!remote_head)\n> +\t\treturn;\n\nHmm.  This is a faithful translation of scripted version, but I wonder\nwhat should happen when we got a non-commit here...\n\n> +\n> +\tstrbuf_init(&buf, 0);\n> +\tstrbuf_addstr(&buf, \"refs/heads/\");\n> +\tstrbuf_addstr(&buf, remote);\n> +\tget_sha1(buf.buf, branch_head);\n\nThis does not correspond to the computation of $bh in the scripted version\nthat makes sure \"remote\" is actually a bare name of branch, e.g. \"master\",\nwithout any adornment like \"master~5^3~8.  Your code would succeed and\nleave the same object name in branch_head[] as remote_head->sha1, wouldn't\nit?\n\n> +\tif (!hashcmp(remote_head->sha1, branch_head)) {\n> +\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n> +\t\t\tsha1_to_hex(branch_head), remote);\n> +\t\treturn;\n> +\t}\n> +\t/* See if remote matches <name>~<number>, or <name>^ */\n\nThe scripted version did not handle <name>^, so this is an extension.\nDon't you want also handle <name>^^^ if we are extending it?\n\n> +\tptr = strrchr(remote, '^');\n> +\tif (ptr && ptr[1] == '\\0')\n> +\t\tlen = ptr-remote;\n> +\telse {\n> +\t\tptr = strrchr(remote, '~');\n> +\t\tif (ptr && ptr[1] != '0' && isdigit(ptr[1])) {\n> +\t\t\tlen = ptr-remote;\n> +\t\t\tptr++;\n> +\t\t\tfor (ptr++; *ptr; ptr++)\n> +\t\t\t\tif (!isdigit(*ptr)) {\n> +\t\t\t\t\tlen = 0;\n> +\t\t\t\t\tbreak;\n> +\t\t\t\t}\n> +\t\t}\n> +\t}\n> +\tif (len) {\n> +\t\tstruct strbuf truname = STRBUF_INIT;\n> +\t\tstrbuf_addstr(&truname, remote);\n> +\t\tstrbuf_setlen(&truname, len);\n> +\t\tif (!get_sha1(truname.buf, buf_sha)) {\n\nAgain, isn't this wrong?  You are not making sure truname is the name of\nexisting local branch.  HEAD@{7}~23 will pass get_sha1() but you are not\nmerging an early part of HEAD@{7} branch.\n\n> +\t\t\tstrbuf_addf(msg,\n> +\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n> +\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n> +\t\t\treturn;\n> +\t\t}\n> +\t}\n\n> +int cmd_merge(int argc, const char **argv, const char *prefix)\n> +{\n\nThis is an ultra-huge function.  I wonder if it can further split up to\nmake it easier to maintain.\n\n> +\tunsigned char sha1[20], result_tree[20];\n> +\tstruct object *second_token = NULL;\n> +\tstruct strbuf buf, head_arg;\n> +\tint flag, head_invalid, i, single_strategy;\n> +\tint best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;\n> +\tstruct commit_list *common = NULL;\n> +\tstruct path_list_item *best_strategy = NULL, *wt_strategy = NULL;\n> +\tstruct commit_list **remotes = &remoteheads;\n> +\n> +\tsetup_work_tree();\n> +\tif (unmerged_cache())\n> +\t\tdie(\"You are in the middle of a conflicted merge.\");\n> +\n> +\t/*\n> +\t * Check if we are _not_ on a detached HEAD, i.e. if there is a\n> +\t * current branch.\n> +\t */\n> +\tbranch = resolve_ref(\"HEAD\", sha1, 0, &flag);\n\nYou are resolving for non-reading, so before an initial commit, you won't\nget any error from this call, which is good.  And sha1[] will be cleared.\nOtherwise you learn where the HEAD is in sha1[] at this point.\n\n> +\tif (branch && flag & REF_ISSYMREF) {\n> +\t\tconst char *ptr = skip_prefix(branch, \"refs/heads/\");\n> +\t\tif (ptr)\n> +\t\t\tbranch = ptr;\n> +\t}\n> +\n> +\tgit_config(git_merge_config, NULL);\n> +\n> +\t/* for color.diff and diff.color */\n> +\tgit_config(git_diff_ui_config, NULL);\n> +\n> +\t/* for color.ui */\n> +\tif (diff_use_color_default == -1)\n> +\t\tdiff_use_color_default = git_use_color_default;\n> +\n> +\targc = parse_options(argc, argv, builtin_merge_options,\n> +\t\t\tbuiltin_merge_usage, 0);\n> +\n> +\tif (squash) {\n> +\t\tif (!allow_fast_forward)\n> +\t\t\tdie(\"You cannot combine --squash with --no-ff.\");\n> +\t\toption_commit = 0;\n> +\t}\n> +\n> +\tif (!argc)\n> +\t\tusage_with_options(builtin_merge_usage,\n> +\t\t\tbuiltin_merge_options);\n> +\n> +\t/*\n> +\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n> +\t * the way we can tell it is to see if the second token is HEAD,\n> +\t * but some people might have misused the interface and used a\n> +\t * committish that is the same as HEAD there instead.\n> +\t * Traditional format never would have \"-m\" so it is an\n> +\t * additional safety measure to check for it.\n> +\t */\n> +\tstrbuf_init(&buf, 0);\n> +\tstrbuf_init(&head_arg, 0);\n> +\tif (argc > 1)\n> +\t\tsecond_token = peel_to_type(argv[1], 0, NULL, OBJ_COMMIT);\n\nIf the second token was a string that could resolve to an object name that\ndoes not peel to commit (say \"merge -m 'HEAD^{tree}' other\"), you will get\na complaint fro mpeel-to-type \"I expected a commit but you gave something\nelse\".  You (or more likely Dscho) might have said \"that won't matter in\npractice\", but I think you should really do get_sha1() followed by\nlookup_commit_reference_gently() here to avoid the errors.\n\n> +\thead_invalid = get_sha1(\"HEAD\", head);\n\nYou've already done this earlier with resolve_ref() haven't you?\n\n> +\tif (!have_message && second_token &&\n> +\t\t!hashcmp(second_token->sha1, head)) {\n\nIsn't this wrong if head_invalid is true?\n\n> +\t\tstrbuf_addstr(&merge_msg, argv[0]);\n> +\t\tstrbuf_addstr(&head_arg, argv[1]);\n> +\t\targv += 2;\n> +\t\targc -= 2;\n\nI do not think there is any point using strbuf for head_arg.  Shouldn't it\nsimply be a \"const char *\"?\n\n> +\t} else if (head_invalid) {\n> +\t\tstruct object *remote_head;\n> +\t\t/*\n> +\t\t * If the merged head is a valid one there is no reason\n> +\t\t * to forbid \"git merge\" into a branch yet to be born.\n> +\t\t * We do the same for \"git pull\".\n> +\t\t */\n> +\t\tif (argc != 1)\n> +\t\t\tdie(\"Can merge only exactly one commit into \"\n> +\t\t\t\t\"empty head\");\n> +\t\tremote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);\n\nHere we _do_ expect it to be a commit, so peel_to_type() is fine.\n\n> +\t\tif (!remote_head)\n> +\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n> +\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n> +\t\t\t\tDIE_ON_ERR);\n> +\t\treset_hard(remote_head->sha1, 0);\n> +\t\treturn 0;\n\nMakes one wonder reset_hard() (aka \"read-tree --reset -u HEAD\") ever fail\nand return here (iow, without calling die()).  The answer is luckily no\nin this case, but it is somewhat unnerving to reviewers.\n\n> +\t} else {\n> +\t\t/* We are invoked directly as the first-class UI. */\n> +\t\tstrbuf_addstr(&head_arg, \"HEAD\");\n> +\t\t/*\n> +\t\t * All the rest are the commits being merged;\n> +\t\t * prepare the standard merge summary message to\n> +\t\t * be appended to the given message.  If remote\n> +\t\t * is invalid we will die later in the common\n> +\t\t * codepath so we discard the error in this\n> +\t\t * loop.\n> +\t\t */\n> +\t\tstruct strbuf msg;\n\nDecl-after-statement.\n\n> +\t\tstrbuf_init(&msg, 0);\n> +\t\tfor (i = 0; i < argc; i++)\n> +\t\t\tmerge_name(argv[i], &msg);\n> +\t\tfmt_merge_msg(option_log, &msg, &merge_msg);\n> +\t\tif (merge_msg.len)\n> +\t\t\tstrbuf_setlen(&merge_msg, merge_msg.len-1);\n> +\t}\n> +\n> +\tif (head_invalid || !argc)\n> +\t\tusage_with_options(builtin_merge_usage,\n> +\t\t\tbuiltin_merge_options);\n> +\tstrbuf_addstr(&buf, \"merge\");\n> +\tfor (i = 0; i < argc; i++)\n> +\t\tstrbuf_addf(&buf, \" %s\", argv[i]);\n> +\tsetenv(\"GIT_REFLOG_ACTION\", buf.buf, 0);\n> +\tstrbuf_reset(&buf);\n> +\n> +\tfor (i = 0; i < argc; i++) {\n> +\t\tstruct object *o;\n> +\n> +\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n> +\t\tif (!o)\n> +\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n> +\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n> +\t\t\tremotes)->next;\n> +\n> +\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n> +\t\tsetenv(buf.buf, argv[i], 1);\n> +\t\tstrbuf_reset(&buf);\n> +\t}\n> +\n> +\tif (!use_strategies.nr) {\n> +\t\tif (!remoteheads->next)\n> +\t\t\tadd_strategies(pull_twohead, DEFAULT_TWOHEAD);\n> +\t\telse\n> +\t\t\tadd_strategies(pull_octopus, DEFAULT_OCTOPUS);\n> +\t}\n> +\n> +\tfor (i = 0; i < use_strategies.nr; i++) {\n> +\t\tif ((unsigned int)use_strategies.items[i].util &\n> +\t\t\tNO_FAST_FORWARD)\n> +\t\t\tallow_fast_forward = 0;\n> +\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n> +\t\t\tallow_trivial = 0;\n\nCan we abstract out these ugly casts?  Any code that use path_list to\nstore anything but list of paths (i.e. some value keyed with string) tends\nto have this readability issue.\n\n> +\t}\n> +\n> +\tif (!remoteheads->next)\n> +\t\tcommon = get_merge_bases(lookup_commit(head),\n> +\t\t\t\tremoteheads->item, 1);\n> +\telse {\n> +\t\tstruct commit_list *list = remoteheads;\n> +\t\tcommit_list_insert(lookup_commit(head), &list);\n> +\t\tcommon = get_octopus_merge_bases(list);\n> +\t\tfree(list);\n> +\t}\n> +\n> +\tupdate_ref(\"updating ORIG_HEAD\", \"ORIG_HEAD\", head, NULL, 0,\n> +\t\tDIE_ON_ERR);\n> +\n> +\tif (!common)\n> +\t\t; /* No common ancestors found. We need a real merge. */\n> +\telse if (!remoteheads->next &&\n> +\t\t!hashcmp(common->item->object.sha1,\n> +\t\tremoteheads->item->object.sha1)) {\n\nWouldn't the latter be \"common->item == remoteheads->item\" simply?\n\nYou do not have the check to make sure there is only one common ancestor\n(scripted version compares $common and $1 textually to achieve this), and\nchecking only the first one of them.  Is this correct?\n\n> +\t\t/*\n> +\t\t * If head can reach all the remote heads then we are up\n> +\t\t * to date.\n> +\t\t */\n\nThe comment is wrong --- you are doing \"... but first the most common case\nof merging one remote\" here.\n\n> +\t\tfinish_up_to_date(\"Already up-to-date.\");\n> +\t\treturn 0;\n> +\t} else if (allow_fast_forward && !remoteheads->next &&\n> +\t\t!hashcmp(common->item->object.sha1, head)) {\n> +\t\t/* Again the most common case of merging one remote. */\n\nHere again you are not checking there is only one common, and checking\nonly the first one of them.\n\n> +\t\tstruct strbuf msg;\n> +\t\tstruct object *o;\n> +\t\tchar hex[41];\n> +\n> +\t\tstrcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));\n> +\n> +\t\tprintf(\"Updating %s..%s\\n\",\n> +\t\t\thex,\n> +\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n> +\t\t\tDEFAULT_ABBREV));\n> +\t\trefresh_cache(REFRESH_QUIET);\n> +\t\tstrbuf_init(&msg, 0);\n> +\t\tstrbuf_addstr(&msg, \"Fast forward\");\n> +\t\tif (have_message)\n> +\t\t\tstrbuf_addstr(&msg,\n> +\t\t\t\t\" (no commit created; -m option ignored)\");\n> +\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n> +\t\t\t0, NULL, OBJ_COMMIT);\n> +\t\tif (!o)\n> +\t\t\treturn 0;\n> +\n> +\t\tif (merge_one_remote(head, remoteheads->item->object.sha1))\n> +\t\t\treturn 0;\n\nIsn't \"merge_one_remote()\" just a \"git checkout\" after fast-forward?  The\nfunction feels misnamed.\n\n> +\t\tfinish(o->sha1, msg.buf);\n> +\t\tdropsave();\n> +\t\treturn 0;\n> +\t} else if (!remoteheads->next && common->next)\n> +\t\t;\n\nHere you are checking common->next but earlier if/elseif chain didn't so\nit is too late.\n\n> +\t\t/*\n> +\t\t * We are not doing octopus and not fast forward.  Need\n> +\t\t * a real merge.\n> +\t\t */\n> +\telse if (!remoteheads->next && option_commit) {\n> +\t\t/*\n> +\t\t * We are not doing octopus, not fast forward, and have\n> +\t\t * only one common.\n\nHere again you did not check \"have only one common\" did you?\n\n> +\t\t */\n> +\t\trefresh_cache(REFRESH_QUIET);\n> +\t\tif (allow_trivial) {\n> +\t\t\t/* See if it is really trivial. */\n> +\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n> +\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n> +\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n> +\t\t\t\t\thead, remoteheads->item->object.sha1)) {\n> +\t\t\t\tunsigned char result_tree[20],\n> +\t\t\t\t\tresult_commit[20];\n> +\t\t\t\tstruct commit_list parent;\n> +\n> +\t\t\t\twrite_tree_trivial(result_tree);\n> +\t\t\t\tprintf(\"Wonderful.\\n\");\n> +\t\t\t\tparent.item = remoteheads->item;\n> +\t\t\t\tparent.next = NULL;\n> +\t\t\t\tcommit_tree_trivial(merge_msg.buf,\n> +\t\t\t\t\tresult_tree, &parent,\n> +\t\t\t\t\tresult_commit);\n> +\t\t\t\tfinish(result_commit, \"In-index merge\");\n> +\t\t\t\tdropsave();\n> +\t\t\t\treturn 0;\n> +\t\t\t}\n> +\t\t\tprintf(\"Nope.\\n\");\n> +\t\t}\n\nThere weren't any good way to squelch error messages selectively from the\ntrivial one in the scripted version and that is the only reason we\nsurround read-tree with \"Trying...\" and \"Wonderful/Nope.\".  Literal\ntranslation to make sure you get identical output in the first round of\nthis series is good, but after the code stabilizes, we may want to squelch\nthese messages.  Something to keep in mind but not now.\n"},{"id":"81622","messageId":"7vskuwek9w.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"alpine.DEB.1.00.0806281832510.9925@racer","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-29T08:07:55Z","receivedAt":"2008-06-29T08:07:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Heh.  I suggested it, as I thought this was a perfect excuse to introduce \n> strbuf_initf(), which has been on my wishlist since long.\n\nWe may want to consider the change for its own merit, if there is a reason\nfor the vaddf patch, such as \"this is needed because platform X and Y do\nnot have working vsnprintf()\".  Perhaps we add compat/* something.\n\n> Anyway, I'm fine with having them only in my personal fork.\n\nIt will cost you some \"trust point\" next time you try to sneak something\nin as a part of a largely unrelated topic.  Please don't.\n"},{"id":"81623","messageId":"g47g31$6ca$1@ger.gmane.org","threadId":"14186","inReplyTo":"7vprq0fzum.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-06-29T08:11:13Z","receivedAt":"2008-06-29T08:11:13Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n\n>> +             if (!remote_head)\n>> +                     die(\"%s - not something we can merge\", argv[0]);\n>> +             update_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n>> +                             DIE_ON_ERR);\n>> +             reset_hard(remote_head->sha1, 0);\n>> +             return 0;\n> \n> Makes one wonder reset_hard() (aka \"read-tree --reset -u HEAD\") ever fail\n> and return here (iow, without calling die()).  The answer is luckily no\n> in this case, but it is somewhat unnerving to reviewers.\n\nDo we have some guidelines on how to mark such non-returning calls,\nor how to mark unreachable code (splint, formerly lclint, uses for\nthis /*@ unreachable @*/ annotation)?\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"81637","messageId":"48678E90.1030608@free.fr","threadId":"14186","inReplyTo":"99b931de28ab3017269d15c334c83e242d025489.1214581610.git.vmiklos@frugalware.org","subject":"Re: [PATCH 04/15] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Olivier Marin","fromEmail":"dkr+ml.git@free.fr","sentAt":"2008-06-29T13:30:56Z","receivedAt":"2008-06-29T13:30:56Z","isPatch":true,"sender":{"key":"dkr+ml.git@free.fr","avatar":null},"body":"Miklos Vajna a écrit :\n> \n\n[...]\n\n> +test_expect_success 'setup' '\n> +\techo c0 >c0.c &&\n> +\tgit add c0.c &&\n> +\tgit commit -m c0 &&\n> +\tgit tag c0 &&\n> +\techo c1 >c1.c &&\n> +\tgit add c1.c &&\n> +\tgit commit -m c1 &&\n> +\tgit tag c1 &&\n> +\tgit reset --hard c0 &&\n> +\techo c2 >c2.c &&\n> +\tgit add c2.c &&\n> +\tgit commit -m c2 &&\n> +\tgit tag c2\n\nMissing &&?\n\n> +\tgit reset --hard c0 &&\n> +\techo c3 >c3.c &&\n> +\tgit add c3.c &&\n> +\tgit commit -m c3 &&\n> +\tgit tag c3\n> +'\n\n[...]\n\n> +test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '\n> +\tgit reset --hard c1 &&\n> +\tgit config pull.octopus \"recursive octopus\" &&\n> +\tgit merge c2 c3 &&\n> +\ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\n> +\ttest \"$(git rev-parse c1)\" = \"$(git rev-parse HEAD^1)\" &&\n> +\ttest \"$(git rev-parse c2)\" = \"$(git rev-parse HEAD^2)\" &&\n> +\ttest \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\"\n\nDuplicate the next line?\n\n> +\ttest \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\" &&\n> +\tgit diff --exit-code &&\n> +\ttest -f c0.c &&\n> +\ttest -f c1.c &&\n> +\ttest -f c2.c &&\n> +\ttest -f c3.c\n> +'\n> +\n> +conflict_count()\n> +{\n> +\teval $1=`{\n> +\t\tgit diff-files --name-only\n> +\t\tgit ls-files --unmerged\n> +\t} | wc -l`\n> +}\n> +\n> +# c4 - c5\n> +#    \\ c6\n> +#\n> +# There are two conflicts here:\n> +#\n> +# 1) Because foo.c is renamed to bar.c, recursive will handle this,\n> +# resolve won't.\n> +#\n> +# 2) One in conflict.c and that will always fail.\n> +\n> +test_expect_success 'setup conflicted merge' '\n> +\tgit reset --hard c0 &&\n> +\techo A >conflict.c &&\n> +\tgit add conflict.c &&\n> +\techo contents >foo.c &&\n> +\tgit add foo.c &&\n> +\tgit commit -m c4 &&\n> +\tgit tag c4 &&\n> +\techo B >conflict.c &&\n> +\tgit add conflict.c &&\n> +\tgit mv foo.c bar.c &&\n> +\tgit commit -m c5 &&\n> +\tgit tag c5 &&\n> +\tgit reset --hard c4 &&\n> +\techo C >conflict.c &&\n> +\tgit add conflict.c &&\n> +\techo secondline >> foo.c &&\n> +\tgit add foo.c &&\n> +\tgit commit -m c6 &&\n> +\tgit tag c6\n> +'\n> +\n> +# First do the merge with resolve and recursive then verify that\n> +# recusive is choosen.\n> +\n> +test_expect_success 'merge picks up the best result' '\n> +\tgit config pull.twohead \"recursive resolve\" &&\n> +\tgit reset --hard c5 &&\n> +\tgit merge -s resolve c6\n> +\tconflict_count resolve_count &&\n> +\tgit reset --hard c5 &&\n> +\tgit merge -s recursive c6\n> +\tconflict_count recursive_count &&\n> +\tgit reset --hard c5 &&\n> +\tgit merge c6\n> +\tconflict_count auto_count &&\n> +\ttest \"$auto_count\" = \"$recursive_count\"\n> +'\n\nShould not \"$auto_count\" != \"$resolve_count\" also be tested to be\nsure that recursive has been choosen?\n\nOlivier.\n"},{"id":"81638","messageId":"alpine.DEB.1.00.0806291436520.9925@racer","threadId":"14186","inReplyTo":"7vskuwek9w.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-29T13:40:57Z","receivedAt":"2008-06-29T13:40:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 29 Jun 2008, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > Anyway, I'm fine with having them only in my personal fork.\n> \n> It will cost you some \"trust point\" next time you try to sneak something\n> in as a part of a largely unrelated topic.  Please don't.\n\nFine.  Fine!\n\nI thought it made the code more readable, and that it also might have \nhelped other sites where you have to strbuf_init() first, and often need \ncurly brackets only because of that.  Making for ultra-ugly code.\n\nPlease note that I also added a simple test case for the vaddf() function, \nso your \"trying to sneak through\" is only half as bad as you made it \nsound.\n\nAlso, please note that the situation with SNPRINTF_RETURNS_BOGUS works, \nbut is a thorn in this developers eye.\n\nAnyway, I will not try to post this patch again,\nDscho\n"},{"id":"81642","messageId":"486796C3.7040304@free.fr","threadId":"14186","inReplyTo":"58b2c36de6a6f51a562da303695482bea567f4bf.1214581610.git.vmiklos@frugalware.org","subject":"Re: [PATCH 01/15] Move split_cmdline() to alias.c","fromName":"Olivier Marin","fromEmail":"dkr+ml.git@free.fr","sentAt":"2008-06-29T14:05:55Z","receivedAt":"2008-06-29T14:05:55Z","isPatch":true,"sender":{"key":"dkr+ml.git@free.fr","avatar":null},"body":"Miklos Vajna a écrit :\n> split_cmdline() is currently used for aliases only, but later it can be\n> useful for other builtins as well. Move it to alias.c for now,\n> indicating that originally it's for aliases, but we'll have it in libgit\n> this way.\n\nThis function does not trim cmdline. \n\nPerhaps, the following patch can be inserted after 1/15.\n\n-- >8 --\nFrom: Olivier Marin <dkr@freesurf.fr>\nDate: Sat, 28 Jun 2008 13:06:21 +0200\nSubject: [PATCH] split_cmdline(): ignore whitespace at start/end of cmdline\n\nSigned-off-by: Olivier Marin <dkr@freesurf.fr>\n---\n alias.c |   11 +++++++----\n 1 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/alias.c b/alias.c\nindex ccb1108..7b69d18 100644\n--- a/alias.c\n+++ b/alias.c\n@@ -24,14 +24,16 @@ char *alias_lookup(const char *alias)\n \n int split_cmdline(char *cmdline, const char ***argv)\n {\n-       int src, dst, count = 0, size = 16;\n+       int src = 0, dst, count = 0, size = 16;\n        char quoted = 0;\n \n        *argv = xmalloc(sizeof(char*) * size);\n \n        /* split alias_string */\n-       (*argv)[count++] = cmdline;\n-       for (src = dst = 0; cmdline[src];) {\n+       while (cmdline[src] && isspace(cmdline[src]))\n+               src++;\n+       (*argv)[count++] = cmdline + src;\n+       for (dst = src; cmdline[src];) {\n                char c = cmdline[src];\n                if (!quoted && isspace(c)) {\n                        cmdline[dst++] = 0;\n@@ -42,7 +44,8 @@ int split_cmdline(char *cmdline, const char ***argv)\n                                size += 16;\n                                *argv = xrealloc(*argv, sizeof(char*) * size);\n                        }\n-                       (*argv)[count++] = cmdline + dst;\n+                       if (cmdline[src])\n+                               (*argv)[count++] = cmdline + dst;\n                } else if (!quoted && (c == '\\'' || c == '\"')) {\n                        quoted = c;\n                        src++;\n-- \n1.5.6.1.103.g191a2.dirty\n"},{"id":"81643","messageId":"alpine.DEB.1.00.0806291513450.9925@racer","threadId":"14186","inReplyTo":"486796C3.7040304@free.fr","subject":"Re: [PATCH 01/15] Move split_cmdline() to alias.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-29T14:15:39Z","receivedAt":"2008-06-29T14:15:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 29 Jun 2008, Olivier Marin wrote:\n\n> Miklos Vajna a écrit :\n> > split_cmdline() is currently used for aliases only, but later it can be\n> > useful for other builtins as well. Move it to alias.c for now,\n> > indicating that originally it's for aliases, but we'll have it in libgit\n> > this way.\n> \n> This function does not trim cmdline. \n\nAs the string comes either from the config (where it is trimmed), or from \nthe command line (where the user can be stup^W^Wask for whitespace \nexplicitely),  I do not see much merit in this patch.\n\nUnless you can provide an example where it fails, of course,\nDscho"},{"id":"81645","messageId":"48679C44.30702@free.fr","threadId":"14186","inReplyTo":"alpine.DEB.1.00.0806291513450.9925@racer","subject":"Re: [PATCH 01/15] Move split_cmdline() to alias.c","fromName":"Olivier Marin","fromEmail":"dkr+ml.git@free.fr","sentAt":"2008-06-29T14:29:24Z","receivedAt":"2008-06-29T14:29:24Z","isPatch":true,"sender":{"key":"dkr+ml.git@free.fr","avatar":null},"body":"Johannes Schindelin a écrit :\n> \n> As the string comes either from the config (where it is trimmed), or from \n> the command line (where the user can be stup^W^Wask for whitespace \n> explicitely),  I do not see much merit in this patch.\n\nYou are right, today it is not a problem because the usage is really limited\nbut Miklos's intention seems to make the function usable by everyone. As we\ndo not know how it will be used in the future, I think it is safer with my\npatch.\n\nOlivier.\n"},{"id":"81646","messageId":"alpine.DEB.1.00.0806291542070.9925@racer","threadId":"14186","inReplyTo":"48679C44.30702@free.fr","subject":"Re: [PATCH 01/15] Move split_cmdline() to alias.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-29T14:43:54Z","receivedAt":"2008-06-29T14:43:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 29 Jun 2008, Olivier Marin wrote:\n\n> Johannes Schindelin a écrit :\n> > \n> > As the string comes either from the config (where it is trimmed), or \n> > from the command line (where the user can be stup^W^Wask for \n> > whitespace explicitely), I do not see much merit in this patch.\n> \n> You are right, today it is not a problem because the usage is really \n> limited but Miklos's intention seems to make the function usable by \n> everyone. As we do not know how it will be used in the future, I think \n> it is safer with my patch.\n\nI am generally not a fan of crossing bridges miles before you reach them.\n\nBut if you want to keep running with this, you should add at least a few \ntests to show what you patch solves, and that it solves it.  Preferably \nwith a \"test_expect_failure\" patch, and then a patch fixing it and \nchanging that _failure to _success.\n\nHere's a starter:\n\n-- snipsnap --\ndiff --git a/Makefile b/Makefile\nindex d3e339a..83a9a30 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1229,7 +1229,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X test-strbuf$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X test-strbuf$X test-cmdline$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/test-cmdline.c b/test-cmdline.c\nnew file mode 100644\nindex 0000000..2f6d9eb\n--- /dev/null\n+++ b/test-cmdline.c\n@@ -0,0 +1,15 @@\n+#include \"cache.h\"\n+\n+int main(int argc, char **argv)\n+{\n+\tint i = 0;\n+\tconst char **new_argv;\n+\n+\tif (argc < 2 || (argc = split_cmdline(argv[1], &new_argv)) < 0)\n+\t\treturn 1;\n+\n+\twhile (i < argc)\n+\t\tprintf(\"arg %d: '%s'\\n\", i++, *new_argv++);\n+\n+\treturn 0;\n+}"},{"id":"81647","messageId":"1214751098-547-1-git-send-email-vmiklos@frugalware.org","threadId":"14186","inReplyTo":"48678E90.1030608@free.fr","subject":"[PATCH] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-29T14:51:38Z","receivedAt":"2008-06-29T14:51:38Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Test if the given strategies are used and test the case when multiple\nstrategies are configured using a space separated list.\n\nAlso test if the best strategy is picked if none is specified.  This is\ndone by adding a simple test case where recursive detects a rename, but\nresolve does not, and verify that finally merge will pick up the\nprevious.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\nOn Sun, Jun 29, 2008 at 03:30:56PM +0200, Olivier Marin <dkr+ml.git@free.fr> wrote:\n> > +   git tag c2\n>\n> Missing &&?\n\nFixed.\n\n> > +   test \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\"\n>\n> Duplicate the next line?\n>\n> > +   test \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\" &&\n\nYes, it was duplicated.\n\n> > +   test \"$auto_count\" = \"$recursive_count\"\n> > +'\n>\n> Should not \"$auto_count\" != \"$resolve_count\" also be tested to be\n> sure that recursive has been choosen?\n\nSure, changed.\n\nUpdated patch below.\n\n t/t7601-merge-pull-config.sh |  129 ++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 129 insertions(+), 0 deletions(-)\n create mode 100755 t/t7601-merge-pull-config.sh\n\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nnew file mode 100755\nindex 0000000..32585f8\n--- /dev/null\n+++ b/t/t7601-merge-pull-config.sh\n@@ -0,0 +1,129 @@\n+#!/bin/sh\n+\n+test_description='git-merge\n+\n+Testing pull.* configuration parsing.'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo c0 >c0.c &&\n+\tgit add c0.c &&\n+\tgit commit -m c0 &&\n+\tgit tag c0 &&\n+\techo c1 >c1.c &&\n+\tgit add c1.c &&\n+\tgit commit -m c1 &&\n+\tgit tag c1 &&\n+\tgit reset --hard c0 &&\n+\techo c2 >c2.c &&\n+\tgit add c2.c &&\n+\tgit commit -m c2 &&\n+\tgit tag c2 &&\n+\tgit reset --hard c0 &&\n+\techo c3 >c3.c &&\n+\tgit add c3.c &&\n+\tgit commit -m c3 &&\n+\tgit tag c3\n+'\n+\n+test_expect_success 'merge c1 with c2' '\n+\tgit reset --hard c1 &&\n+\ttest -f c0.c &&\n+\ttest -f c1.c &&\n+\ttest ! -f c2.c &&\n+\ttest ! -f c3.c &&\n+\tgit merge c2 &&\n+\ttest -f c1.c &&\n+\ttest -f c2.c\n+'\n+\n+test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '\n+\tgit reset --hard c1 &&\n+\tgit config pull.twohead ours &&\n+\tgit merge c2 &&\n+\ttest -f c1.c &&\n+\t! test -f c2.c\n+'\n+\n+test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '\n+\tgit reset --hard c1 &&\n+\tgit config pull.octopus \"recursive\" &&\n+\ttest_must_fail git merge c2 c3 &&\n+\ttest \"$(git rev-parse c1)\" = \"$(git rev-parse HEAD)\"\n+'\n+\n+test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '\n+\tgit reset --hard c1 &&\n+\tgit config pull.octopus \"recursive octopus\" &&\n+\tgit merge c2 c3 &&\n+\ttest \"$(git rev-parse c1)\" != \"$(git rev-parse HEAD)\" &&\n+\ttest \"$(git rev-parse c1)\" = \"$(git rev-parse HEAD^1)\" &&\n+\ttest \"$(git rev-parse c2)\" = \"$(git rev-parse HEAD^2)\" &&\n+\ttest \"$(git rev-parse c3)\" = \"$(git rev-parse HEAD^3)\" &&\n+\tgit diff --exit-code &&\n+\ttest -f c0.c &&\n+\ttest -f c1.c &&\n+\ttest -f c2.c &&\n+\ttest -f c3.c\n+'\n+\n+conflict_count()\n+{\n+\teval $1=`{\n+\t\tgit diff-files --name-only\n+\t\tgit ls-files --unmerged\n+\t} | wc -l`\n+}\n+\n+# c4 - c5\n+#    \\ c6\n+#\n+# There are two conflicts here:\n+#\n+# 1) Because foo.c is renamed to bar.c, recursive will handle this,\n+# resolve won't.\n+#\n+# 2) One in conflict.c and that will always fail.\n+\n+test_expect_success 'setup conflicted merge' '\n+\tgit reset --hard c0 &&\n+\techo A >conflict.c &&\n+\tgit add conflict.c &&\n+\techo contents >foo.c &&\n+\tgit add foo.c &&\n+\tgit commit -m c4 &&\n+\tgit tag c4 &&\n+\techo B >conflict.c &&\n+\tgit add conflict.c &&\n+\tgit mv foo.c bar.c &&\n+\tgit commit -m c5 &&\n+\tgit tag c5 &&\n+\tgit reset --hard c4 &&\n+\techo C >conflict.c &&\n+\tgit add conflict.c &&\n+\techo secondline >> foo.c &&\n+\tgit add foo.c &&\n+\tgit commit -m c6 &&\n+\tgit tag c6\n+'\n+\n+# First do the merge with resolve and recursive then verify that\n+# recusive is choosen.\n+\n+test_expect_success 'merge picks up the best result' '\n+\tgit config pull.twohead \"recursive resolve\" &&\n+\tgit reset --hard c5 &&\n+\tgit merge -s resolve c6\n+\tconflict_count resolve_count &&\n+\tgit reset --hard c5 &&\n+\tgit merge -s recursive c6\n+\tconflict_count recursive_count &&\n+\tgit reset --hard c5 &&\n+\tgit merge c6\n+\tconflict_count auto_count &&\n+\ttest \"$auto_count\" = \"$recursive_count\" &&\n+\ttest \"$auto_count\" != \"$resolve_count\"\n+'\n+\n+test_done\n-- \n1.5.6\n"},{"id":"81648","messageId":"20080629151124.GQ2058@genesis.frugalware.org","threadId":"14186","inReplyTo":"1214751098-547-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-29T15:11:24Z","receivedAt":"2008-06-29T15:11:24Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Sun, Jun 29, 2008 at 04:51:38PM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:\n> Signed-off-by: Junio C Hamano <gitster@pobox.com>\n\nOops, I forgot to remove this Signed-off-by. Fixed in my fork.\n"},{"id":"81664","messageId":"20080629201757.GA27370@steel.home","threadId":"14186","inReplyTo":"alpine.DEB.1.00.0806291436520.9925@racer","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-29T20:17:57Z","receivedAt":"2008-06-29T20:17:57Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Johannes Schindelin, Sun, Jun 29, 2008 15:40:57 +0200:\n> On Sun, 29 Jun 2008, Junio C Hamano wrote:\n> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> > \n> > > Anyway, I'm fine with having them only in my personal fork.\n> > \n> > It will cost you some \"trust point\" next time you try to sneak something\n> > in as a part of a largely unrelated topic.  Please don't.\n> \n> Fine.  Fine!\n> \n\nSee the positive side: you just won a \"please review me carefully\"\npoint. These have no negative side effects, usually.\n"},{"id":"81667","messageId":"7vvdzs7zwx.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"20080629201757.GA27370@steel.home","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-29T20:24:14Z","receivedAt":"2008-06-29T20:24:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alex Riesen <raa.lkml@gmail.com> writes:\n\n> Johannes Schindelin, Sun, Jun 29, 2008 15:40:57 +0200:\n>> On Sun, 29 Jun 2008, Junio C Hamano wrote:\n>> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>> > \n>> > > Anyway, I'm fine with having them only in my personal fork.\n>> > \n>> > It will cost you some \"trust point\" next time you try to sneak something\n>> > in as a part of a largely unrelated topic.  Please don't.\n>> \n>> Fine.  Fine!\n>\n> See the positive side: you just won a \"please review me carefully\"\n> point. These have no negative side effects, usually.\n\nHeh, he hasn't _yet_.  I said \"next time\" didn't I?\n"},{"id":"81668","messageId":"bd6139dc0806291330xcb910beo757e447702ccc668@mail.gmail.com","threadId":"14186","inReplyTo":"7vvdzs7zwx.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 11/15] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf()","fromName":"Sverre Rabbelier","fromEmail":"alturin@gmail.com","sentAt":"2008-06-29T20:30:11Z","receivedAt":"2008-06-29T20:30:11Z","isPatch":true,"sender":{"key":"alturin@gmail.com","avatar":null},"body":"On Sun, Jun 29, 2008 at 10:24 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Heh, he hasn't _yet_.  I said \"next time\" didn't I?\n\nNow you sound like the bad guy from \"Inspector Gadget\" :P. \"Next time\nDscho, next time!!!\"\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"81697","messageId":"20080630013612.GY2058@genesis.frugalware.org","threadId":"14186","inReplyTo":"7vprq0fzum.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-30T01:36:12Z","receivedAt":"2008-06-30T01:36:12Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Sun, Jun 29, 2008 at 12:46:09AM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > +/* Get the name for the merge commit's message. */\n> > +static void merge_name(const char *remote, struct strbuf *msg)\n> > +{\n> > +\tstruct object *remote_head;\n> > +\tunsigned char branch_head[20], buf_sha[20];\n> > +\tstruct strbuf buf;\n> > +\tchar *ptr;\n> > +\tint len = 0;\n> > +\n> > +\tmemset(branch_head, 0, sizeof(branch_head));\n> > +\tremote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);\n> > +\tif (!remote_head)\n> > +\t\treturn;\n> \n> Hmm.  This is a faithful translation of scripted version, but I wonder\n> what should happen when we got a non-commit here...\n\nHm, I think we do not consider that case normal, at least git fsck\n(since commit 6232f62) checks for it.\n\nI replaced the silent return with a die().\n\n> > +\n> > +\tstrbuf_init(&buf, 0);\n> > +\tstrbuf_addstr(&buf, \"refs/heads/\");\n> > +\tstrbuf_addstr(&buf, remote);\n> > +\tget_sha1(buf.buf, branch_head);\n> \n> This does not correspond to the computation of $bh in the scripted version\n> that makes sure \"remote\" is actually a bare name of branch, e.g. \"master\",\n> without any adornment like \"master~5^3~8.  Your code would succeed and\n> leave the same object name in branch_head[] as remote_head->sha1, wouldn't\n> it?\n\nI replaced it with a dwim_ref() call, to achieve the same behaviour.\n\n> > +\tif (!hashcmp(remote_head->sha1, branch_head)) {\n> > +\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n> > +\t\t\tsha1_to_hex(branch_head), remote);\n> > +\t\treturn;\n> > +\t}\n> > +\t/* See if remote matches <name>~<number>, or <name>^ */\n> \n> The scripted version did not handle <name>^, so this is an extension.\n> Don't you want also handle <name>^^^ if we are extending it?\n\nI did so, now it accepts <name>^, <name>^^, <name>^^^, etc.\n\n> > +\tptr = strrchr(remote, '^');\n> > +\tif (ptr && ptr[1] == '\\0')\n> > +\t\tlen = ptr-remote;\n> > +\telse {\n> > +\t\tptr = strrchr(remote, '~');\n> > +\t\tif (ptr && ptr[1] != '0' && isdigit(ptr[1])) {\n> > +\t\t\tlen = ptr-remote;\n> > +\t\t\tptr++;\n> > +\t\t\tfor (ptr++; *ptr; ptr++)\n> > +\t\t\t\tif (!isdigit(*ptr)) {\n> > +\t\t\t\t\tlen = 0;\n> > +\t\t\t\t\tbreak;\n> > +\t\t\t\t}\n> > +\t\t}\n> > +\t}\n> > +\tif (len) {\n> > +\t\tstruct strbuf truname = STRBUF_INIT;\n> > +\t\tstrbuf_addstr(&truname, remote);\n> > +\t\tstrbuf_setlen(&truname, len);\n> > +\t\tif (!get_sha1(truname.buf, buf_sha)) {\n> \n> Again, isn't this wrong?  You are not making sure truname is the name of\n> existing local branch.  HEAD@{7}~23 will pass get_sha1() but you are not\n> merging an early part of HEAD@{7} branch.\n\nNow I'm using dwim_ref() here as well.\n\n> > +\t\t\tstrbuf_addf(msg,\n> > +\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n> > +\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n> > +\t\t\treturn;\n> > +\t\t}\n> > +\t}\n> \n> > +int cmd_merge(int argc, const char **argv, const char *prefix)\n> > +{\n> \n> This is an ultra-huge function.  I wonder if it can further split up to\n> make it easier to maintain.\n\nYes, just like the scripted version. Hm no, that was even longer. (I\nmean I already introduced a lot of static functions to make the C\nequivalent of the \"main\" part of git-merge.sh shorter.) OK, it was 474\nlines long today, but now I introduced 3 new static functions and that\nmade it \"only\" 410 lines long.\n\n> > +\t/*\n> > +\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n> > +\t * the way we can tell it is to see if the second token is HEAD,\n> > +\t * but some people might have misused the interface and used a\n> > +\t * committish that is the same as HEAD there instead.\n> > +\t * Traditional format never would have \"-m\" so it is an\n> > +\t * additional safety measure to check for it.\n> > +\t */\n> > +\tstrbuf_init(&buf, 0);\n> > +\tstrbuf_init(&head_arg, 0);\n> > +\tif (argc > 1)\n> > +\t\tsecond_token = peel_to_type(argv[1], 0, NULL, OBJ_COMMIT);\n> \n> If the second token was a string that could resolve to an object name that\n> does not peel to commit (say \"merge -m 'HEAD^{tree}' other\"), you will get\n> a complaint fro mpeel-to-type \"I expected a commit but you gave something\n> else\".  You (or more likely Dscho) might have said \"that won't matter in\n> practice\", but I think you should really do get_sha1() followed by\n> lookup_commit_reference_gently() here to avoid the errors.\n\nFixed.\n\n> \n> > +\thead_invalid = get_sha1(\"HEAD\", head);\n> \n> You've already done this earlier with resolve_ref() haven't you?\n\nAh yes. I had the global 'head' and the local 'sha1' variable for the\nsame purpose, now I got rid of the local 'sha1' variable in cmd_merge()\nso resolve_ref() writes now to the 'head' variable and then this line is\nnot necessary, as I can write head_invalid right after resolve_ref().\n\n> \n> > +\tif (!have_message && second_token &&\n> > +\t\t!hashcmp(second_token->sha1, head)) {\n> \n> Isn't this wrong if head_invalid is true?\n\nif head_invalid is true, then 'head' will be filled with 0s, hashcmp()\nwill never return 0 so the condition will be never true. That's what the\nshell version:\n\n        head_commit=$(git rev-parse --verify \"HEAD\" 2>/dev/null)\n\ndoes as well.\n\n> > +\t\tstrbuf_addstr(&merge_msg, argv[0]);\n> > +\t\tstrbuf_addstr(&head_arg, argv[1]);\n> > +\t\targv += 2;\n> > +\t\targc -= 2;\n> \n> I do not think there is any point using strbuf for head_arg.  Shouldn't it\n> simply be a \"const char *\"?\n\nNow it is.\n\n> > +\t\tif (!remote_head)\n> > +\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n> > +\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n> > +\t\t\t\tDIE_ON_ERR);\n> > +\t\treset_hard(remote_head->sha1, 0);\n> > +\t\treturn 0;\n> \n> Makes one wonder reset_hard() (aka \"read-tree --reset -u HEAD\") ever fail\n> and return here (iow, without calling die()).  The answer is luckily no\n> in this case, but it is somewhat unnerving to reviewers.\n\nActually reset_hard does not return if an error occures:\n\n        if (unpack_trees(1, &t, &opts))\n                exit(128); /* We've already reported the error, finish dying */\n\nThat's exactly how we already have it in builtin-commit.\n\n> \n> > +\t} else {\n> > +\t\t/* We are invoked directly as the first-class UI. */\n> > +\t\tstrbuf_addstr(&head_arg, \"HEAD\");\n> > +\t\t/*\n> > +\t\t * All the rest are the commits being merged;\n> > +\t\t * prepare the standard merge summary message to\n> > +\t\t * be appended to the given message.  If remote\n> > +\t\t * is invalid we will die later in the common\n> > +\t\t * codepath so we discard the error in this\n> > +\t\t * loop.\n> > +\t\t */\n> > +\t\tstruct strbuf msg;\n> \n> Decl-after-statement.\n\nYou already fixed this. :-)\n\n> > +\tfor (i = 0; i < use_strategies.nr; i++) {\n> > +\t\tif ((unsigned int)use_strategies.items[i].util &\n> > +\t\t\tNO_FAST_FORWARD)\n> > +\t\t\tallow_fast_forward = 0;\n> > +\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n> > +\t\t\tallow_trivial = 0;\n> \n> Can we abstract out these ugly casts?  Any code that use path_list to\n> store anything but list of paths (i.e. some value keyed with string) tends\n> to have this readability issue.\n\nIf you don't cast, you can't use the & operator. If I change the\npath_list_item's util to be an unsigned number then I break fast-export.\nI think if we _really_ want to get rid of those casts, we could have\nsomething like:\n\ndiff --git a/path-list.h b/path-list.h\nindex ca2cbba..1f57e81 100644\n--- a/path-list.h\n+++ b/path-list.h\n@@ -4,6 +4,7 @@\n struct path_list_item {\n        char *path;\n        void *util;\n+       unsigned int flags;\n };\n struct path_list\n {\n\nBut I'm not sure if that's a good idea. Also, fast-export will still\nhave casts after such a change.\n\n> > +\tif (!common)\n> > +\t\t; /* No common ancestors found. We need a real merge. */\n> > +\telse if (!remoteheads->next &&\n> > +\t\t!hashcmp(common->item->object.sha1,\n> > +\t\tremoteheads->item->object.sha1)) {\n> \n> Wouldn't the latter be \"common->item == remoteheads->item\" simply?\n\nRight, changed.\n\n> You do not have the check to make sure there is only one common ancestor\n> (scripted version compares $common and $1 textually to achieve this), and\n> checking only the first one of them.  Is this correct?\n\nYes. I changed it to:\n\n        else if (!remoteheads->next && !common->next &&\n                        common->item == remoteheads->item) {\n\nAnd now I have the check.\n\n> \n> > +\t\t/*\n> > +\t\t * If head can reach all the remote heads then we are up\n> > +\t\t * to date.\n> > +\t\t */\n> \n> The comment is wrong --- you are doing \"... but first the most common case\n> of merging one remote\" here.\n\nI changed it to match the shell version:\n\n/*\n * If head can reach all the merge then we are up to\n * date.\n * but first the most common case of merging one remote.\n */\n\n> > +\t\tfinish_up_to_date(\"Already up-to-date.\");\n> > +\t\treturn 0;\n> > +\t} else if (allow_fast_forward && !remoteheads->next &&\n> > +\t\t!hashcmp(common->item->object.sha1, head)) {\n> > +\t\t/* Again the most common case of merging one remote. */\n> \n> Here again you are not checking there is only one common, and checking\n> only the first one of them.\n\nChanged to:\n\n        } else if (allow_fast_forward && !remoteheads->next &&\n                        !common->next &&\n                        !hashcmp(common->item->object.sha1, head)) {\n\nWhich should add the proper check.\n\n> > +\t\tif (merge_one_remote(head, remoteheads->item->object.sha1))\n> > +\t\t\treturn 0;\n> \n> Isn't \"merge_one_remote()\" just a \"git checkout\" after fast-forward?  The\n> function feels misnamed.\n\nThanks. I'm terribly bad at naming. Renamed to checkout_fast_forward().\n\n> > +\t\tfinish(o->sha1, msg.buf);\n> > +\t\tdropsave();\n> > +\t\treturn 0;\n> > +\t} else if (!remoteheads->next && common->next)\n> > +\t\t;\n> \n> Here you are checking common->next but earlier if/elseif chain didn't so\n> it is too late.\n\nNow, that I do, I think the condition is OK.\n\n> > +\telse if (!remoteheads->next && option_commit) {\n> > +\t\t/*\n> > +\t\t * We are not doing octopus, not fast forward, and have\n> > +\t\t * only one common.\n> \n> Here again you did not check \"have only one common\" did you?\n\nActually the shell version did not check here, either, but yes, I would\nhave to. Now I do.\n\n> > +\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n> > +\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n> > +\t\t\t\t\thead, remoteheads->item->object.sha1)) {\n> > +\t\t\t\tunsigned char result_tree[20],\n> > +\t\t\t\t\tresult_commit[20];\n> > +\t\t\t\tstruct commit_list parent;\n> > +\n> > +\t\t\t\twrite_tree_trivial(result_tree);\n> > +\t\t\t\tprintf(\"Wonderful.\\n\");\n> > +\t\t\t\tparent.item = remoteheads->item;\n> > +\t\t\t\tparent.next = NULL;\n> > +\t\t\t\tcommit_tree_trivial(merge_msg.buf,\n> > +\t\t\t\t\tresult_tree, &parent,\n> > +\t\t\t\t\tresult_commit);\n> > +\t\t\t\tfinish(result_commit, \"In-index merge\");\n> > +\t\t\t\tdropsave();\n> > +\t\t\t\treturn 0;\n> > +\t\t\t}\n> > +\t\t\tprintf(\"Nope.\\n\");\n> > +\t\t}\n> \n> There weren't any good way to squelch error messages selectively from the\n> trivial one in the scripted version and that is the only reason we\n> surround read-tree with \"Trying...\" and \"Wonderful/Nope.\".  Literal\n> translation to make sure you get identical output in the first round of\n> this series is good, but after the code stabilizes, we may want to squelch\n> these messages.  Something to keep in mind but not now.\n\nOK.\n"},{"id":"81698","messageId":"e8d1385cc49a06ca3fae28231ebc66a333ce4ffb.1214789764.git.vmiklos@frugalware.org","threadId":"14186","inReplyTo":"20080630013612.GY2058@genesis.frugalware.org","subject":"[PATCH 13/13] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-30T01:39:58Z","receivedAt":"2008-06-30T01:39:58Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Mentored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nInterdiff since the previous version:\n\n\tgit diff 9824cf3..8611c7b\n\nIndividual commits:\n\n\tgit log 8611c7b\n\n Makefile                                      |    2 +-\n builtin-merge.c                               | 1173 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git.c                                         |    1 +\n t/t7602-merge-octopus-many.sh                 |    2 +-\n 6 files changed, 1177 insertions(+), 2 deletions(-)\n create mode 100644 builtin-merge.c\n rename git-merge.sh => contrib/examples/git-merge.sh (100%)\n\ndiff --git a/Makefile b/Makefile\nindex b003e3e..3d8c3d2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -240,7 +240,6 @@ SCRIPT_SH += git-lost-found.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n-SCRIPT_SH += git-merge.sh\n SCRIPT_SH += git-merge-stupid.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-parse-remote.sh\n@@ -511,6 +510,7 @@ BUILTIN_OBJS += builtin-ls-remote.o\n BUILTIN_OBJS += builtin-ls-tree.o\n BUILTIN_OBJS += builtin-mailinfo.o\n BUILTIN_OBJS += builtin-mailsplit.o\n+BUILTIN_OBJS += builtin-merge.o\n BUILTIN_OBJS += builtin-merge-base.o\n BUILTIN_OBJS += builtin-merge-file.o\n BUILTIN_OBJS += builtin-merge-ours.o\ndiff --git a/builtin-merge.c b/builtin-merge.c\nnew file mode 100644\nindex 0000000..187038c\n--- /dev/null\n+++ b/builtin-merge.c\n@@ -0,0 +1,1173 @@\n+/*\n+ * Builtin \"git merge\"\n+ *\n+ * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org>\n+ *\n+ * Based on git-merge.sh by Junio C Hamano.\n+ */\n+\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"builtin.h\"\n+#include \"run-command.h\"\n+#include \"path-list.h\"\n+#include \"diff.h\"\n+#include \"refs.h\"\n+#include \"commit.h\"\n+#include \"diffcore.h\"\n+#include \"revision.h\"\n+#include \"unpack-trees.h\"\n+#include \"cache-tree.h\"\n+#include \"dir.h\"\n+#include \"utf8.h\"\n+#include \"log-tree.h\"\n+#include \"color.h\"\n+\n+enum strategy {\n+\tDEFAULT_TWOHEAD = 1,\n+\tDEFAULT_OCTOPUS = 2,\n+\tNO_FAST_FORWARD = 4,\n+\tNO_TRIVIAL = 8\n+};\n+\n+static const char * const builtin_merge_usage[] = {\n+\t\"git-merge [options] <remote>...\",\n+\t\"git-merge [options] <msg> HEAD <remote>\",\n+\tNULL\n+};\n+\n+static int show_diffstat = 1, option_log, squash;\n+static int option_commit = 1, allow_fast_forward = 1;\n+static int allow_trivial = 1, have_message;\n+static struct strbuf merge_msg;\n+static struct commit_list *remoteheads;\n+static unsigned char head[20];\n+static struct path_list use_strategies;\n+static const char *branch;\n+\n+static struct path_list_item strategy_items[] = {\n+\t{ \"recur\",      (void *)NO_TRIVIAL },\n+\t{ \"recursive\",  (void *)(DEFAULT_TWOHEAD | NO_TRIVIAL) },\n+\t{ \"octopus\",    (void *)DEFAULT_OCTOPUS },\n+\t{ \"resolve\",    (void *)0 },\n+\t{ \"stupid\",     (void *)0 },\n+\t{ \"ours\",       (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+\t{ \"subtree\",    (void *)(NO_FAST_FORWARD | NO_TRIVIAL) },\n+};\n+static struct path_list strategies = { strategy_items,\n+\tARRAY_SIZE(strategy_items), 0, 0 };\n+\n+static const char *pull_twohead, *pull_octopus;\n+\n+static int option_parse_message(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tstruct strbuf *buf = opt->value;\n+\n+\tif (unset)\n+\t\tstrbuf_setlen(buf, 0);\n+\telse {\n+\t\tstrbuf_addf(buf, \"%s\\n\\n\", arg);\n+\t\thave_message = 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static struct path_list_item *unsorted_path_list_lookup(const char *path,\n+\tstruct path_list *list)\n+{\n+\tint i;\n+\n+\tif (!path)\n+\t\treturn NULL;\n+\n+\tfor (i = 0; i < list->nr; i++)\n+\t\tif (!strcmp(path, list->items[i].path))\n+\t\t\treturn &list->items[i];\n+\treturn NULL;\n+}\n+\n+static inline void path_list_append_strategy(struct path_list_item *item)\n+{\n+\tpath_list_append(item->path, &use_strategies)->util = item->util;\n+}\n+\n+static int option_parse_strategy(const struct option *opt,\n+\tconst char *arg, int unset)\n+{\n+\tint i;\n+\tstruct path_list_item *item = unsorted_path_list_lookup(arg, &strategies);\n+\n+\tif (unset)\n+\t\treturn 0;\n+\n+\tif (item)\n+\t\tpath_list_append_strategy(item);\n+\telse {\n+\t\tstruct strbuf err;\n+\t\tstrbuf_init(&err, 0);\n+\t\tfor (i = 0; i < strategies.nr; i++)\n+\t\t\tstrbuf_addf(&err, \" %s\", strategies.items[i].path);\n+\t\tfprintf(stderr, \"Could not find merge strategy '%s'.\\n\", arg);\n+\t\tfprintf(stderr, \"Available strategies are:%s.\\n\", err.buf);\n+\t\texit(1);\n+\t}\n+\treturn 0;\n+}\n+\n+static int option_parse_n(const struct option *opt,\n+\t\tconst char *arg, int unset)\n+{\n+\tshow_diffstat = unset;\n+\treturn 0;\n+}\n+\n+static struct option builtin_merge_options[] = {\n+\t{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,\n+\t\t\"do not show a diffstat at the end of the merge\",\n+\t\tPARSE_OPT_NOARG, option_parse_n },\n+\tOPT_BOOLEAN(0, \"stat\", &show_diffstat,\n+\t\t\"show a diffstat at the end of the merge\"),\n+\tOPT_BOOLEAN(0, \"summary\", &show_diffstat, \"(synonym to --stat)\"),\n+\tOPT_BOOLEAN(0, \"log\", &option_log,\n+\t\t\"add list of one-line log to merge commit message\"),\n+\tOPT_BOOLEAN(0, \"squash\", &squash,\n+\t\t\"create a single commit instead of doing a merge\"),\n+\tOPT_BOOLEAN(0, \"commit\", &option_commit,\n+\t\t\"perform a commit if the merge succeeds (default)\"),\n+\tOPT_BOOLEAN(0, \"ff\", &allow_fast_forward,\n+\t\t\"allow fast forward (default)\"),\n+\tOPT_CALLBACK('s', \"strategy\", &use_strategies, \"strategy\",\n+\t\t\"merge strategy to use\", option_parse_strategy),\n+\tOPT_CALLBACK('m', \"message\", &merge_msg, \"message\",\n+\t\t\"message to be used for the merge commit (if any)\",\n+\t\toption_parse_message),\n+\tOPT_END()\n+};\n+\n+/* Cleans up metadata that is uninteresting after a succeeded merge. */\n+static void dropsave(void)\n+{\n+\tunlink(git_path(\"MERGE_HEAD\"));\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tunlink(git_path(\"MERGE_STASH\"));\n+}\n+\n+static void save_state(void)\n+{\n+\tint fd;\n+\tstruct child_process stash;\n+\tconst char *argv[] = {\"stash\", \"create\", NULL};\n+\n+\tfd = open(git_path(\"MERGE_STASH\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"MERGE_STASH\"));\n+\tmemset(&stash, 0, sizeof(stash));\n+\tstash.argv = argv;\n+\tstash.out = fd;\n+\tstash.git_cmd = 1;\n+\trun_command(&stash);\n+}\n+\n+static void reset_hard(unsigned const char *sha1, int verbose)\n+{\n+\tstruct tree *tree;\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.reset = 1;\n+\tif (verbose)\n+\t\topts.verbose_update = 1;\n+\n+\ttree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\tdie(\"failed to unpack %s tree object\", sha1_to_hex(sha1));\n+\tparse_tree(tree);\n+\tinit_tree_desc(&t, tree->buffer, tree->size);\n+\tif (unpack_trees(1, &t, &opts))\n+\t\texit(128); /* We've already reported the error, finish dying */\n+}\n+\n+static void restore_state(void)\n+{\n+\tstruct strbuf sb;\n+\tconst char *args[] = { \"stash\", \"apply\", NULL, NULL };\n+\n+\tif (access(git_path(\"MERGE_STASH\"), R_OK) < 0)\n+\t\treturn;\n+\n+\treset_hard(head, 1);\n+\n+\tstrbuf_init(&sb, 0);\n+\tif (strbuf_read_file(&sb, git_path(\"MERGE_STASH\"), 0) < 0)\n+\t\tdie(\"could not read MERGE_STASH: %s\", strerror(errno));\n+\targs[2] = sb.buf;\n+\n+\t/*\n+\t * It is OK to ignore error here, for example when there was\n+\t * nothing to restore.\n+\t */\n+\trun_command_v_opt(args, RUN_GIT_CMD);\n+\n+\tstrbuf_release(&sb);\n+\trefresh_cache(REFRESH_QUIET);\n+}\n+\n+/* This is called when no merge was necessary. */\n+static void finish_up_to_date(const char *msg)\n+{\n+\tprintf(\"%s%s\\n\", squash ? \" (nothing to squash)\" : \"\", msg);\n+\tdropsave();\n+}\n+\n+static void squash_message(void)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf out;\n+\tstruct commit_list *j;\n+\tint fd;\n+\n+\tprintf(\"Squash commit -- not updating HEAD\\n\");\n+\tfd = open(git_path(\"SQUASH_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"Could not write to %s\", git_path(\"SQUASH_MSG\"));\n+\n+\tinit_revisions(&rev, NULL);\n+\trev.ignore_merges = 1;\n+\trev.commit_format = CMIT_FMT_MEDIUM;\n+\n+\tcommit = lookup_commit(head);\n+\tcommit->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&rev, &commit->object, NULL);\n+\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tadd_pending_object(&rev, &j->item->object, NULL);\n+\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\tif (prepare_revision_walk(&rev))\n+\t\tdie(\"revision walk setup failed\");\n+\n+\tstrbuf_init(&out, 0);\n+\tstrbuf_addstr(&out, \"Squashed commit of the following:\\n\");\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tstrbuf_addch(&out, '\\n');\n+\t\tstrbuf_addf(&out, \"commit %s\\n\",\n+\t\t\tsha1_to_hex(commit->object.sha1));\n+\t\tpretty_print_commit(rev.commit_format, commit, &out, rev.abbrev,\n+\t\t\tNULL, NULL, rev.date_mode, 0);\n+\t}\n+\twrite(fd, out.buf, out.len);\n+\tclose(fd);\n+\tstrbuf_release(&out);\n+}\n+\n+static int run_hook(const char *name)\n+{\n+\tstruct child_process hook;\n+\tconst char *argv[3], *env[2];\n+\tchar index[PATH_MAX];\n+\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\n+\n+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", get_index_file());\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\tif (squash)\n+\t\targv[1] = \"1\";\n+\telse\n+\t\targv[1] = \"0\";\n+\targv[2] = NULL;\n+\n+\tmemset(&hook, 0, sizeof(hook));\n+\thook.argv = argv;\n+\thook.no_stdin = 1;\n+\thook.stdout_to_stderr = 1;\n+\thook.env = env;\n+\n+\treturn run_command(&hook);\n+}\n+\n+static void finish(const unsigned char *new_head, const char *msg)\n+{\n+\tstruct strbuf reflog_message;\n+\n+\tstrbuf_init(&reflog_message, 0);\n+\tif (!msg)\n+\t\tstrbuf_addstr(&reflog_message, getenv(\"GIT_REFLOG_ACTION\"));\n+\telse {\n+\t\tprintf(\"%s\\n\", msg);\n+\t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n+\t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n+\t}\n+\tif (squash) {\n+\t\tsquash_message();\n+\t} else {\n+\t\tif (!merge_msg.len)\n+\t\t\tprintf(\"No merge message -- not updating HEAD\\n\");\n+\t\telse {\n+\t\t\tconst char *argv_gc_auto[] = { \"gc\", \"--auto\", NULL };\n+\t\t\tupdate_ref(reflog_message.buf, \"HEAD\",\n+\t\t\t\tnew_head, head, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\t\t/*\n+\t\t\t * We ignore errors in 'gc --auto', since the\n+\t\t\t * user should see them.\n+\t\t\t */\n+\t\t\trun_command_v_opt(argv_gc_auto, RUN_GIT_CMD);\n+\t\t}\n+\t}\n+\tif (new_head && show_diffstat) {\n+\t\tstruct diff_options opts;\n+\t\tdiff_setup(&opts);\n+\t\topts.output_format |=\n+\t\t\tDIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;\n+\t\topts.detect_rename = DIFF_DETECT_RENAME;\n+\t\tif (diff_use_color_default > 0)\n+\t\t\tDIFF_OPT_SET(&opts, COLOR_DIFF);\n+\t\tdiff_tree_sha1(head, new_head, \"\", &opts);\n+\t\tdiffcore_std(&opts);\n+\t\tdiff_flush(&opts);\n+\t}\n+\n+\t/* Run a post-merge hook */\n+\trun_hook(\"post-merge\");\n+\n+\tstrbuf_release(&reflog_message);\n+}\n+\n+/* Get the name for the merge commit's message. */\n+static void merge_name(const char *remote, struct strbuf *msg)\n+{\n+\tstruct object *remote_head;\n+\tunsigned char branch_head[20], buf_sha[20];\n+\tstruct strbuf buf;\n+\tchar *ptr;\n+\tint len = 0;\n+\tchar *ref;\n+\n+\tmemset(branch_head, 0, sizeof(branch_head));\n+\tremote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);\n+\tif (!remote_head)\n+\t\tdie(\"'%s' does not point to a commit\", remote);\n+\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addstr(&buf, \"refs/heads/\");\n+\tstrbuf_addstr(&buf, remote);\n+\tdwim_ref(buf.buf, buf.len, branch_head, &ref);\n+\n+\tif (!hashcmp(remote_head->sha1, branch_head)) {\n+\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n+\t\t\tsha1_to_hex(branch_head), remote);\n+\t\treturn;\n+\t}\n+\t/* See if remote matches <name>~<number>, or <name>^ */\n+\tptr = strrchr(remote, '^');\n+\tif (ptr && ptr[1] == '\\0') {\n+\t\tlen = strlen(remote);\n+\t\twhile ((ptr = (char *)memrchr(remote, '^', len)))\n+\t\t\tif (ptr && ptr[1] == '\\0')\n+\t\t\t\tlen = ptr - remote - 1;\n+\t\t\telse\n+\t\t\t\tbreak;\n+\t}\n+\telse {\n+\t\tptr = strrchr(remote, '~');\n+\t\tif (ptr && ptr[1] != '0' && isdigit(ptr[1])) {\n+\t\t\tlen = ptr-remote;\n+\t\t\tptr++;\n+\t\t\tfor (ptr++; *ptr; ptr++)\n+\t\t\t\tif (!isdigit(*ptr)) {\n+\t\t\t\t\tlen = 0;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t}\n+\t}\n+\tif (len) {\n+\t\tstruct strbuf truname = STRBUF_INIT;\n+\t\tstrbuf_addstr(&truname, remote);\n+\t\tstrbuf_setlen(&truname, len);\n+\t\tif (dwim_ref(truname.buf, truname.len, buf_sha, &ref)) {\n+\t\t\tstrbuf_addf(msg,\n+\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n+\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (!strcmp(remote, \"FETCH_HEAD\") &&\n+\t\t\t!access(git_path(\"FETCH_HEAD\"), R_OK)) {\n+\t\tFILE *fp;\n+\t\tstruct strbuf line;\n+\t\tchar *ptr;\n+\n+\t\tstrbuf_init(&line, 0);\n+\t\tfp = fopen(git_path(\"FETCH_HEAD\"), \"r\");\n+\t\tif (!fp)\n+\t\t\tdie(\"could not open %s for reading: %s\",\n+\t\t\t\tgit_path(\"FETCH_HEAD\"), strerror(errno));\n+\t\tstrbuf_getline(&line, fp, '\\n');\n+\t\tfclose(fp);\n+\t\tptr = strstr(line.buf, \"\\tnot-for-merge\\t\");\n+\t\tif (ptr)\n+\t\t\tstrbuf_remove(&line, ptr-line.buf+1, 13);\n+\t\tstrbuf_addbuf(msg, &line);\n+\t\tstrbuf_release(&line);\n+\t\treturn;\n+\t}\n+\tstrbuf_addf(msg, \"%s\\t\\tcommit '%s'\\n\",\n+\t\tsha1_to_hex(remote_head->sha1), remote);\n+}\n+\n+int git_merge_config(const char *k, const char *v, void *cb)\n+{\n+\tif (branch && !prefixcmp(k, \"branch.\") &&\n+\t\t!prefixcmp(k + 7, branch) &&\n+\t\t!strcmp(k + 7 + strlen(branch), \".mergeoptions\")) {\n+\t\tconst char **argv;\n+\t\tint argc;\n+\t\tchar *buf;\n+\n+\t\tbuf = xstrdup(v);\n+\t\targc = split_cmdline(buf, &argv);\n+\t\targv = xrealloc(argv, sizeof(*argv) * (argc + 2));\n+\t\tmemmove(argv + 1, argv, sizeof(*argv) * (argc + 1));\n+\t\targc++;\n+\t\tparse_options(argc, argv, builtin_merge_options,\n+\t\t\t      builtin_merge_usage, 0);\n+\t\tfree(buf);\n+\t}\n+\n+\tif (!strcmp(k, \"merge.diffstat\") || !strcmp(k, \"merge.stat\"))\n+\t\tshow_diffstat = git_config_bool(k, v);\n+\telse if (!strcmp(k, \"pull.twohead\"))\n+\t\treturn git_config_string(&pull_twohead, k, v);\n+\telse if (!strcmp(k, \"pull.octopus\"))\n+\t\treturn git_config_string(&pull_octopus, k, v);\n+\treturn 0;\n+}\n+\n+static int read_tree_trivial(unsigned char *common, unsigned char *head,\n+\tunsigned char *one)\n+{\n+\tint i, nr_trees = 0;\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = -1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.trivial_merges_only = 1;\n+\topts.merge = 1;\n+\ttrees[nr_trees] = parse_tree_indirect(common);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(one);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\topts.fn = threeway_merge;\n+\tcache_tree_free(&active_cache_tree);\n+\topts.head_idx = 2;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\treturn 0;\n+}\n+\n+static int commit_tree_trivial(const char *msg, unsigned const char *tree,\n+\t\tstruct commit_list *parents, unsigned char *ret)\n+{\n+\tstruct commit_list *i;\n+\tstruct strbuf buf;\n+\tint encoding_is_utf8;\n+\n+\t/* Not having i18n.commitencoding is the same as having utf-8 */\n+\tencoding_is_utf8 = is_encoding_utf8(git_commit_encoding);\n+\n+\tstrbuf_init(&buf, 8192); /* should avoid reallocs for the headers */\n+\tstrbuf_addf(&buf, \"tree %s\\n\", sha1_to_hex(tree));\n+\n+\tfor (i = parents; i; i = i->next)\n+\t\tstrbuf_addf(&buf, \"parent %s\\n\",\n+\t\t\tsha1_to_hex(i->item->object.sha1));\n+\n+\t/* Person/date information */\n+\tstrbuf_addf(&buf, \"author %s\\n\",\n+\t\tgit_author_info(IDENT_ERROR_ON_NO_NAME));\n+\tstrbuf_addf(&buf, \"committer %s\\n\",\n+\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME));\n+\tif (!encoding_is_utf8)\n+\t\tstrbuf_addf(&buf, \"encoding %s\\n\", git_commit_encoding);\n+\tstrbuf_addch(&buf, '\\n');\n+\n+\t/* And add the comment */\n+\tstrbuf_addstr(&buf, msg);\n+\n+\twrite_sha1_file(buf.buf, buf.len, commit_type, ret);\n+\tstrbuf_release(&buf);\n+\treturn *ret;\n+}\n+\n+static void write_tree_trivial(unsigned char *sha1)\n+{\n+\tif (write_cache_as_tree(sha1, 0, NULL))\n+\t\tdie(\"git write-tree failed to write a tree\");\n+}\n+\n+static int try_merge_strategy(char *strategy, struct commit_list *common,\n+\tconst char *head_arg)\n+{\n+\tconst char **args;\n+\tint i = 0, ret;\n+\tstruct commit_list *j;\n+\tstruct strbuf buf;\n+\n+\targs = xmalloc((4 + commit_list_count(common) +\n+\t\t\tcommit_list_count(remoteheads)) * sizeof(char *));\n+\tstrbuf_init(&buf, 0);\n+\tstrbuf_addf(&buf, \"merge-%s\", strategy);\n+\targs[i++] = buf.buf;\n+\tfor (j = common; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i++] = \"--\";\n+\targs[i++] = head_arg;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\targs[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));\n+\targs[i] = NULL;\n+\tret = run_command_v_opt(args, RUN_GIT_CMD);\n+\tstrbuf_release(&buf);\n+\ti = 1;\n+\tfor (j = common; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\ti += 2;\n+\tfor (j = remoteheads; j; j = j->next)\n+\t\tfree((void *)args[i++]);\n+\tfree(args);\n+\treturn -ret;\n+}\n+\n+static void count_diff_files(struct diff_queue_struct *q,\n+\t\tstruct diff_options *opt, void *data)\n+{\n+\tint *count = data;\n+\n+\t(*count) += q->nr;\n+}\n+\n+static int count_unmerged_entries(void)\n+{\n+\tconst struct index_state *state = &the_index;\n+\tint i, ret = 0;\n+\n+\tfor (i = 0; i < state->cache_nr; i++)\n+\t\tif (ce_stage(state->cache[i]))\n+\t\t\tret++;\n+\n+\treturn ret;\n+}\n+\n+static int checkout_fast_forward(unsigned char *head, unsigned char *remote)\n+{\n+\tstruct tree *trees[MAX_UNPACK_TREES];\n+\tstruct unpack_trees_options opts;\n+\tstruct tree_desc t[MAX_UNPACK_TREES];\n+\tint i, fd, nr_trees = 0;\n+\tstruct dir_struct dir;\n+\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\n+\tif (read_cache_unmerged())\n+\t\tdie(\"you need to resolve your current index first\");\n+\n+\tfd = hold_locked_index(lock_file, 1);\n+\n+\tmemset(&trees, 0, sizeof(trees));\n+\tmemset(&opts, 0, sizeof(opts));\n+\tmemset(&t, 0, sizeof(t));\n+\tdir.show_ignored = 1;\n+\tdir.exclude_per_dir = \".gitignore\";\n+\topts.dir = &dir;\n+\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.update = 1;\n+\topts.verbose_update = 1;\n+\topts.merge = 1;\n+\topts.fn = twoway_merge;\n+\n+\ttrees[nr_trees] = parse_tree_indirect(head);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\ttrees[nr_trees] = parse_tree_indirect(remote);\n+\tif (!trees[nr_trees++])\n+\t\treturn -1;\n+\tfor (i = 0; i < nr_trees; i++) {\n+\t\tparse_tree(trees[i]);\n+\t\tinit_tree_desc(t+i, trees[i]->buffer, trees[i]->size);\n+\t}\n+\tif (unpack_trees(nr_trees, t, &opts))\n+\t\treturn -1;\n+\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\tcommit_locked_index(lock_file))\n+\t\tdie(\"unable to write new index file\");\n+\treturn 0;\n+}\n+\n+static void split_merge_strategies(const char *string, struct path_list *list)\n+{\n+\tchar *p, *q, *buf;\n+\n+\tif (!string)\n+\t\treturn;\n+\n+\tlist->strdup_paths = 1;\n+\tbuf = xstrdup(string);\n+\tq = buf;\n+\tfor (;;) {\n+\t\tp = strchr(q, ' ');\n+\t\tif (!p) {\n+\t\t\tpath_list_append(q, list);\n+\t\t\tfree(buf);\n+\t\t\treturn;\n+\t\t} else {\n+\t\t\t*p = '\\0';\n+\t\t\tpath_list_append(q, list);\n+\t\t\tq = ++p;\n+\t\t}\n+\t}\n+}\n+\n+static void add_strategies(const char *string, enum strategy strategy)\n+{\n+\tstruct path_list list;\n+\tint i;\n+\n+\tmemset(&list, 0, sizeof(list));\n+\tsplit_merge_strategies(string, &list);\n+\tif (list.nr) {\n+\t\tfor (i = 0; i < list.nr; i++) {\n+\t\t\tstruct path_list_item *item;\n+\n+\t\t\titem = unsorted_path_list_lookup(list.items[i].path,\n+\t\t\t\t&strategies);\n+\t\t\tif (item)\n+\t\t\t\tpath_list_append_strategy(item);\n+\t\t}\n+\t\treturn;\n+\t}\n+\tfor (i = 0; i < strategies.nr; i++)\n+\t\tif ((enum strategy)strategies.items[i].util & strategy)\n+\t\t\tpath_list_append_strategy(&strategies.items[i]);\n+}\n+\n+static int merge_trivial(void)\n+{\n+\tunsigned char result_tree[20], result_commit[20];\n+\tstruct commit_list parent;\n+\n+\twrite_tree_trivial(result_tree);\n+\tprintf(\"Wonderful.\\n\");\n+\tparent.item = remoteheads->item;\n+\tparent.next = NULL;\n+\tcommit_tree_trivial(merge_msg.buf, result_tree, &parent,\n+\t\tresult_commit);\n+\tfinish(result_commit, \"In-index merge\");\n+\tdropsave();\n+\treturn 0;\n+}\n+\n+static int finish_automerge(struct commit_list *common,\n+\tunsigned char *result_tree,\n+\tstruct path_list_item *wt_strategy)\n+{\n+\tstruct commit_list *parents = NULL, *j;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tunsigned char result_commit[20];\n+\n+\tfree_commit_list(common);\n+\tif (allow_fast_forward) {\n+\t\tparents = remoteheads;\n+\t\tcommit_list_insert(lookup_commit(head), &parents);\n+\t\tparents = reduce_heads(parents);\n+\t} else {\n+\t\tstruct commit_list **pptr = &parents;\n+\n+\t\tpptr = &commit_list_insert(lookup_commit(head),\n+\t\t\t\tpptr)->next;\n+\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\tpptr = &commit_list_insert(j->item, pptr)->next;\n+\t}\n+\tfree_commit_list(remoteheads);\n+\tstrbuf_addch(&merge_msg, '\\n');\n+\tcommit_tree_trivial(merge_msg.buf, result_tree, parents,\n+\t\t\tresult_commit);\n+\tfree_commit_list(parents);\n+\tstrbuf_addf(&buf, \"Merge made by %s.\", wt_strategy->path);\n+\tfinish(result_commit, buf.buf);\n+\tstrbuf_release(&buf);\n+\tdropsave();\n+\treturn 0;\n+}\n+\n+static int suggest_conflicts(void)\n+{\n+\tFILE *fp;\n+\tint pos;\n+\n+\tfp = fopen(git_path(\"MERGE_MSG\"), \"a\");\n+\tif (!fp)\n+\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\tfprintf(fp, \"\\nConflicts:\\n\");\n+\tfor (pos = 0; pos < active_nr; pos++) {\n+\t\tstruct cache_entry *ce = active_cache[pos];\n+\n+\t\tif (ce_stage(ce)) {\n+\t\t\tfprintf(fp, \"\\t%s\\n\", ce->name);\n+\t\t\twhile (pos + 1 < active_nr &&\n+\t\t\t\t\t!strcmp(ce->name,\n+\t\t\t\t\t\tactive_cache[pos + 1]->name))\n+\t\t\t\tpos++;\n+\t\t}\n+\t}\n+\tfclose(fp);\n+\trerere();\n+\tprintf(\"Automatic merge failed; \"\n+\t\t\t\"fix conflicts and then commit the result.\\n\");\n+\treturn 1;\n+}\n+\n+int cmd_merge(int argc, const char **argv, const char *prefix)\n+{\n+\tunsigned char result_tree[20];\n+\tstruct commit *second_token = NULL;\n+\tstruct strbuf buf;\n+\tconst char *head_arg;\n+\tint flag, head_invalid = 0, i, single_strategy;\n+\tint best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;\n+\tstruct commit_list *common = NULL;\n+\tstruct path_list_item *best_strategy = NULL, *wt_strategy = NULL;\n+\tstruct commit_list **remotes = &remoteheads;\n+\n+\tsetup_work_tree();\n+\tif (unmerged_cache())\n+\t\tdie(\"You are in the middle of a conflicted merge.\");\n+\n+\t/*\n+\t * Check if we are _not_ on a detached HEAD, i.e. if there is a\n+\t * current branch.\n+\t */\n+\tbranch = resolve_ref(\"HEAD\", head, 0, &flag);\n+\tif (branch && flag & REF_ISSYMREF) {\n+\t\tconst char *ptr = skip_prefix(branch, \"refs/heads/\");\n+\t\tif (ptr)\n+\t\t\tbranch = ptr;\n+\t} else\n+\t\thead_invalid = 1;\n+\n+\tgit_config(git_merge_config, NULL);\n+\n+\t/* for color.diff and diff.color */\n+\tgit_config(git_diff_ui_config, NULL);\n+\n+\t/* for color.ui */\n+\tif (diff_use_color_default == -1)\n+\t\tdiff_use_color_default = git_use_color_default;\n+\n+\targc = parse_options(argc, argv, builtin_merge_options,\n+\t\t\tbuiltin_merge_usage, 0);\n+\n+\tif (squash) {\n+\t\tif (!allow_fast_forward)\n+\t\t\tdie(\"You cannot combine --squash with --no-ff.\");\n+\t\toption_commit = 0;\n+\t}\n+\n+\tif (!argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\t/*\n+\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n+\t * the way we can tell it is to see if the second token is HEAD,\n+\t * but some people might have misused the interface and used a\n+\t * committish that is the same as HEAD there instead.\n+\t * Traditional format never would have \"-m\" so it is an\n+\t * additional safety measure to check for it.\n+\t */\n+\tstrbuf_init(&buf, 0);\n+\tif (argc > 1) {\n+\t\tunsigned char second_sha1[20];\n+\n+\t\tif (get_sha1(argv[1], second_sha1))\n+\t\t\tdie(\"Not a valid ref: %s\", argv[1]);\n+\t\tsecond_token = lookup_commit_reference_gently(second_sha1, 0);\n+\t\tif (!second_token)\n+\t\t\tdie(\"'%s' is not a commit\", argv[1]);\n+\t}\n+\n+\tif (!have_message && second_token &&\n+\t\t!hashcmp(second_token->object.sha1, head)) {\n+\t\tstrbuf_addstr(&merge_msg, argv[0]);\n+\t\thead_arg = argv[1];\n+\t\targv += 2;\n+\t\targc -= 2;\n+\t} else if (head_invalid) {\n+\t\tstruct object *remote_head;\n+\t\t/*\n+\t\t * If the merged head is a valid one there is no reason\n+\t\t * to forbid \"git merge\" into a branch yet to be born.\n+\t\t * We do the same for \"git pull\".\n+\t\t */\n+\t\tif (argc != 1)\n+\t\t\tdie(\"Can merge only exactly one commit into \"\n+\t\t\t\t\"empty head\");\n+\t\tremote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);\n+\t\tif (!remote_head)\n+\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n+\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n+\t\t\t\tDIE_ON_ERR);\n+\t\treset_hard(remote_head->sha1, 0);\n+\t\treturn 0;\n+\t} else {\n+\t\tstruct strbuf msg;\n+\n+\t\t/* We are invoked directly as the first-class UI. */\n+\t\thead_arg = \"HEAD\";\n+\n+\t\t/*\n+\t\t * All the rest are the commits being merged;\n+\t\t * prepare the standard merge summary message to\n+\t\t * be appended to the given message.  If remote\n+\t\t * is invalid we will die later in the common\n+\t\t * codepath so we discard the error in this\n+\t\t * loop.\n+\t\t */\n+\t\tstrbuf_init(&msg, 0);\n+\t\tfor (i = 0; i < argc; i++)\n+\t\t\tmerge_name(argv[i], &msg);\n+\t\tfmt_merge_msg(option_log, &msg, &merge_msg);\n+\t\tif (merge_msg.len)\n+\t\t\tstrbuf_setlen(&merge_msg, merge_msg.len-1);\n+\t}\n+\n+\tif (head_invalid || !argc)\n+\t\tusage_with_options(builtin_merge_usage,\n+\t\t\tbuiltin_merge_options);\n+\n+\tstrbuf_addstr(&buf, \"merge\");\n+\tfor (i = 0; i < argc; i++)\n+\t\tstrbuf_addf(&buf, \" %s\", argv[i]);\n+\tsetenv(\"GIT_REFLOG_ACTION\", buf.buf, 0);\n+\tstrbuf_reset(&buf);\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tstruct object *o;\n+\n+\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n+\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n+\t\t\tremotes)->next;\n+\n+\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n+\t\tsetenv(buf.buf, argv[i], 1);\n+\t\tstrbuf_reset(&buf);\n+\t}\n+\n+\tif (!use_strategies.nr) {\n+\t\tif (!remoteheads->next)\n+\t\t\tadd_strategies(pull_twohead, DEFAULT_TWOHEAD);\n+\t\telse\n+\t\t\tadd_strategies(pull_octopus, DEFAULT_OCTOPUS);\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tif ((unsigned int)use_strategies.items[i].util &\n+\t\t\tNO_FAST_FORWARD)\n+\t\t\tallow_fast_forward = 0;\n+\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n+\t\t\tallow_trivial = 0;\n+\t}\n+\n+\tif (!remoteheads->next)\n+\t\tcommon = get_merge_bases(lookup_commit(head),\n+\t\t\t\tremoteheads->item, 1);\n+\telse {\n+\t\tstruct commit_list *list = remoteheads;\n+\t\tcommit_list_insert(lookup_commit(head), &list);\n+\t\tcommon = get_octopus_merge_bases(list);\n+\t\tfree(list);\n+\t}\n+\n+\tupdate_ref(\"updating ORIG_HEAD\", \"ORIG_HEAD\", head, NULL, 0,\n+\t\tDIE_ON_ERR);\n+\n+\tif (!common)\n+\t\t; /* No common ancestors found. We need a real merge. */\n+\telse if (!remoteheads->next && !common->next &&\n+\t\t\tcommon->item == remoteheads->item) {\n+\t\t/*\n+\t\t * If head can reach all the merge then we are up to date.\n+\t\t * but first the most common case of merging one remote.\n+\t\t */\n+\t\tfinish_up_to_date(\"Already up-to-date.\");\n+\t\treturn 0;\n+\t} else if (allow_fast_forward && !remoteheads->next &&\n+\t\t\t!common->next &&\n+\t\t\t!hashcmp(common->item->object.sha1, head)) {\n+\t\t/* Again the most common case of merging one remote. */\n+\t\tstruct strbuf msg;\n+\t\tstruct object *o;\n+\t\tchar hex[41];\n+\n+\t\tstrcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));\n+\n+\t\tprintf(\"Updating %s..%s\\n\",\n+\t\t\thex,\n+\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n+\t\t\tDEFAULT_ABBREV));\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tstrbuf_init(&msg, 0);\n+\t\tstrbuf_addstr(&msg, \"Fast forward\");\n+\t\tif (have_message)\n+\t\t\tstrbuf_addstr(&msg,\n+\t\t\t\t\" (no commit created; -m option ignored)\");\n+\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n+\t\t\t0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\treturn 0;\n+\n+\t\tif (checkout_fast_forward(head, remoteheads->item->object.sha1))\n+\t\t\treturn 0;\n+\n+\t\tfinish(o->sha1, msg.buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t} else if (!remoteheads->next && common->next)\n+\t\t;\n+\t\t/*\n+\t\t * We are not doing octopus and not fast forward.  Need\n+\t\t * a real merge.\n+\t\t */\n+\telse if (!remoteheads->next && !common->next && option_commit) {\n+\t\t/*\n+\t\t * We are not doing octopus, not fast forward, and have\n+\t\t * only one common.\n+\t\t */\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tif (allow_trivial) {\n+\t\t\t/* See if it is really trivial. */\n+\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n+\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n+\t\t\t\t\thead, remoteheads->item->object.sha1))\n+\t\t\t\treturn merge_trivial();\n+\t\t\tprintf(\"Nope.\\n\");\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * An octopus.  If we can reach all the remote we are up\n+\t\t * to date.\n+\t\t */\n+\t\tint up_to_date = 1;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next) {\n+\t\t\tstruct commit_list *common_one;\n+\n+\t\t\t/*\n+\t\t\t * Here we *have* to calculate the individual\n+\t\t\t * merge_bases again, otherwise \"git merge HEAD^\n+\t\t\t * HEAD^^\" would be missed.\n+\t\t\t */\n+\t\t\tcommon_one = get_merge_bases(lookup_commit(head),\n+\t\t\t\tj->item, 1);\n+\t\t\tif (hashcmp(common_one->item->object.sha1,\n+\t\t\t\tj->item->object.sha1)) {\n+\t\t\t\tup_to_date = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (up_to_date) {\n+\t\t\tfinish_up_to_date(\"Already up-to-date. Yeeah!\");\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/* We are going to make a new commit. */\n+\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\n+\t/*\n+\t * At this point, we need a real merge.  No matter what strategy\n+\t * we use, it would operate on the index, possibly affecting the\n+\t * working tree, and when resolved cleanly, have the desired\n+\t * tree in the index -- this means that the index must be in\n+\t * sync with the head commit.  The strategies are responsible\n+\t * to ensure this.\n+\t */\n+\tif (use_strategies.nr != 1) {\n+\t\t/*\n+\t\t * Stash away the local changes so that we can try more\n+\t\t * than one.\n+\t\t */\n+\t\tsave_state();\n+\t\tsingle_strategy = 0;\n+\t} else {\n+\t\tunlink(git_path(\"MERGE_STASH\"));\n+\t\tsingle_strategy = 1;\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tint ret;\n+\t\tif (i) {\n+\t\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\t\trestore_state();\n+\t\t}\n+\t\tif (!single_strategy)\n+\t\t\tprintf(\"Trying merge strategy %s...\\n\",\n+\t\t\t\tuse_strategies.items[i].path);\n+\t\t/*\n+\t\t * Remember which strategy left the state in the working\n+\t\t * tree.\n+\t\t */\n+\t\twt_strategy = &use_strategies.items[i];\n+\n+\t\tret = try_merge_strategy(use_strategies.items[i].path,\n+\t\t\tcommon, head_arg);\n+\t\tif (!option_commit && !ret) {\n+\t\t\tmerge_was_ok = 1;\n+\t\t\t/*\n+\t\t\t * This is necessary here just to avoid writing\n+\t\t\t * the tree, but later we will *not* exit with\n+\t\t\t * status code 1 because merge_was_ok is set.\n+\t\t\t */\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * The backend exits with 1 when conflicts are\n+\t\t\t * left to be resolved, with 2 when it does not\n+\t\t\t * handle the given merge at all.\n+\t\t\t */\n+\t\t\tif (ret == 1) {\n+\t\t\t\tint cnt = 0;\n+\t\t\t\tstruct rev_info rev;\n+\n+\t\t\t\tif (read_cache() < 0)\n+\t\t\t\t\tdie(\"failed to read the cache\");\n+\n+\t\t\t\t/* Check how many files differ. */\n+\t\t\t\tinit_revisions(&rev, \"\");\n+\t\t\t\tsetup_revisions(0, NULL, &rev, NULL);\n+\t\t\t\trev.diffopt.output_format |=\n+\t\t\t\t\tDIFF_FORMAT_CALLBACK;\n+\t\t\t\trev.diffopt.format_callback = count_diff_files;\n+\t\t\t\trev.diffopt.format_callback_data = &cnt;\n+\t\t\t\trun_diff_files(&rev, 0);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Check how many unmerged entries are\n+\t\t\t\t * there.\n+\t\t\t\t */\n+\t\t\t\tcnt += count_unmerged_entries();\n+\n+\t\t\t\tif (best_cnt <= 0 || cnt <= best_cnt) {\n+\t\t\t\t\tbest_strategy =\n+\t\t\t\t\t\t&use_strategies.items[i];\n+\t\t\t\t\tbest_cnt = cnt;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (merge_was_ok)\n+\t\t\t\tbreak;\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Automerge succeeded. */\n+\t\twrite_tree_trivial(result_tree);\n+\t\tautomerge_was_ok = 1;\n+\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * If we have a resulting tree, that means the strategy module\n+\t * auto resolved the merge cleanly.\n+\t */\n+\tif (automerge_was_ok)\n+\t\treturn finish_automerge(common, result_tree, wt_strategy);\n+\n+\t/*\n+\t * Pick the result from the best strategy and have the user fix\n+\t * it up.\n+\t */\n+\tif (!best_strategy) {\n+\t\trestore_state();\n+\t\tif (use_strategies.nr > 1)\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"No merge strategy handled the merge.\\n\");\n+\t\telse\n+\t\t\tfprintf(stderr, \"Merge with strategy %s failed.\\n\",\n+\t\t\t\tuse_strategies.items[0].path);\n+\t\treturn 2;\n+\t} else if (best_strategy == wt_strategy)\n+\t\t; /* We already have its result in the working tree. */\n+\telse {\n+\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\trestore_state();\n+\t\tprintf(\"Using the %s to prepare resolving by hand.\\n\",\n+\t\t\tbest_strategy->path);\n+\t\ttry_merge_strategy(best_strategy->path, common, head_arg);\n+\t}\n+\n+\tif (squash)\n+\t\tfinish(NULL, NULL);\n+\telse {\n+\t\tint fd;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\tstrbuf_addf(&buf, \"%s\\n\",\n+\t\t\t\tsha1_to_hex(j->item->object.sha1));\n+\t\tfd = open(git_path(\"MERGE_HEAD\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\",\n+\t\t\t\tgit_path(\"MERGE_HEAD\"));\n+\t\tif (write_in_full(fd, buf.buf, buf.len) != buf.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_HEAD\"));\n+\t\tclose(fd);\n+\t\tstrbuf_addch(&merge_msg, '\\n');\n+\t\tfd = open(git_path(\"MERGE_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could open %s for writing\", git_path(\"MERGE_MSG\"));\n+\t\tif (write_in_full(fd, merge_msg.buf, merge_msg.len) !=\n+\t\t\tmerge_msg.len)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"MERGE_MSG\"));\n+\t\tclose(fd);\n+\t}\n+\n+\tif (merge_was_ok) {\n+\t\tfprintf(stderr, \"Automatic merge went well; \"\n+\t\t\t\"stopped before committing as requested\\n\");\n+\t\treturn 0;\n+\t} else\n+\t\treturn suggest_conflicts();\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 2b01fea..8bf5280 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -60,6 +60,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);\n extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);\n+extern int cmd_merge(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_base(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);\n extern int cmd_merge_file(int argc, const char **argv, const char *prefix);\ndiff --git a/git-merge.sh b/contrib/examples/git-merge.sh\nsimilarity index 100%\nrename from git-merge.sh\nrename to contrib/examples/git-merge.sh\ndiff --git a/git.c b/git.c\nindex 2fbe96b..770aadd 100644\n--- a/git.c\n+++ b/git.c\n@@ -271,6 +271,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"ls-remote\", cmd_ls_remote },\n \t\t{ \"mailinfo\", cmd_mailinfo },\n \t\t{ \"mailsplit\", cmd_mailsplit },\n+\t\t{ \"merge\", cmd_merge, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n \t\t{ \"merge-file\", cmd_merge_file },\n \t\t{ \"merge-ours\", cmd_merge_ours, RUN_SETUP },\ndiff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh\nindex f3a4bb2..fcb8285 100755\n--- a/t/t7602-merge-octopus-many.sh\n+++ b/t/t7602-merge-octopus-many.sh\n@@ -23,7 +23,7 @@ test_expect_success 'setup' '\n \tdone\n '\n \n-test_expect_failure 'merge c1 with c2, c3, c4, ... c29' '\n+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '\n \tgit reset --hard c1 &&\n \ti=2 &&\n \trefs=\"\" &&\n-- \n1.5.6.1\n"},{"id":"81715","messageId":"7vlk0n4h0a.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"20080630013612.GY2058@genesis.frugalware.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-30T05:40:53Z","receivedAt":"2008-06-30T05:40:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n>> > +\t\tif (!remote_head)\n>> > +\t\t\tdie(\"%s - not something we can merge\", argv[0]);\n>> > +\t\tupdate_ref(\"initial pull\", \"HEAD\", remote_head->sha1, NULL, 0,\n>> > +\t\t\t\tDIE_ON_ERR);\n>> > +\t\treset_hard(remote_head->sha1, 0);\n>> > +\t\treturn 0;\n>> \n>> Makes one wonder reset_hard() (aka \"read-tree --reset -u HEAD\") ever fail\n>> and return here (iow, without calling die()).  The answer is luckily no\n>> in this case, but it is somewhat unnerving to reviewers.\n>\n> Actually reset_hard does not return if an error occures:\n\nI know that; didn't I already say \"Luckily no\"?  The point was it was not\napparent from the above 6 lines alone.\n\n>> > +\tfor (i = 0; i < use_strategies.nr; i++) {\n>> > +\t\tif ((unsigned int)use_strategies.items[i].util &\n>> > +\t\t\tNO_FAST_FORWARD)\n>> > +\t\t\tallow_fast_forward = 0;\n>> > +\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n>> > +\t\t\tallow_trivial = 0;\n>> \n>> Can we abstract out these ugly casts?  Any code that use path_list to\n>> store anything but list of paths (i.e. some value keyed with string) tends\n>> to have this readability issue.\n>\n> If you don't cast, you can't use the & operator. If I change the\n> path_list_item's util to be an unsigned number then I break fast-export.\n> I think if we _really_ want to get rid of those casts, we could have\n> something like:\n\nNo, no, no.  That is not what I meant.\n\nThe places that use use_strategies in your code knows too much about the\ninternal implementation detail of path_list, while path_list pretends to\nbe a general purpose \"table keyed with string\" facility.  The fact is that\nthe table is not a very useful general purpose abstraction unless you are\npointing at some structures that exist regardless of your use of path_list\n(e.g. you have some \"struct object\" and you hold pointers in a path_list).\nIt does not work very well as an abstraction for use case like yours.\n\nWith something like:\n\n        static inline unsigned nth_strategy_flags(struct path_list *s, int nth)\n        {\n                return (unsigned) s->items[nth].util;\n        }\n\nthe checks would be more like:\n\n\tif (nth_strategy_flags(&use_strategies, i) & NO_FAST_FORWARD)\n\t\t...\n\nor even:\n\n        static inline check_nth_strategy_flags(struct path_list_item *i, unsigned flags)\n        {\n        \treturn !((unsigned) i->util & flags);\n        }\n\n        if (check_nth_strategy_flags(&use_strategies,items[i], NO_FAST_FORWARD)\n        \t...\t\n\neither of which would be much easier on the eye.\n\n\n> Actually the shell version did not check here, either, but yes, I would\n> have to. Now I do.\n\nThe scripted did not have to do so explicitly, as the one before that have\nalready caught \"more than one common\" case in the case arm before this\npart.  It is already known that \"have only one common\" condition is true\nwhen you reached the corresponding part of the scripted version.\n"},{"id":"81716","messageId":"7vd4lz4gtw.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"e8d1385cc49a06ca3fae28231ebc66a333ce4ffb.1214789764.git.vmiklos@frugalware.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-30T05:44:43Z","receivedAt":"2008-06-30T05:44:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> diff --git a/builtin-merge.c b/builtin-merge.c\n> new file mode 100644\n> index 0000000..187038c\n> --- /dev/null\n> +++ b/builtin-merge.c\n> @@ -0,0 +1,1173 @@\n> ...\n> +static void save_state(void)\n> +{\n> +\tint fd;\n> +\tstruct child_process stash;\n> +\tconst char *argv[] = {\"stash\", \"create\", NULL};\n> +\n> +\tfd = open(git_path(\"MERGE_STASH\"), O_WRONLY | O_CREAT, 0666);\n> +\tif (fd < 0)\n> +\t\tdie(\"Could not write to %s\", git_path(\"MERGE_STASH\"));\n> +\tmemset(&stash, 0, sizeof(stash));\n> +\tstash.argv = argv;\n> +\tstash.out = fd;\n> +\tstash.git_cmd = 1;\n> +\trun_command(&stash);\n> +}\n\nI first thought \"heh, that's clever\" until I noticed that we use \"stash\ncreate\" with \"stash apply\" these days instead of cpio for this.  I suspect\nthat we can do away without leaving the stash in this temporary file, but\nthat comment applies to the scripted version as well.\n\nBy the way, it would be consistent to name counterpart to dropsave in the\nscripted version as \"drop_save\" if you use \"save_state\" and \"restore_state\".\n\n> +static void reset_hard(unsigned const char *sha1, int verbose)\n> +{\n> +\tstruct tree *tree;\n> +\tstruct unpack_trees_options opts;\n> +\tstruct tree_desc t;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.head_idx = -1;\n> +\topts.src_index = &the_index;\n> +\topts.dst_index = &the_index;\n> +\topts.update = 1;\n> +\topts.reset = 1;\n> +\tif (verbose)\n> +\t\topts.verbose_update = 1;\n> +\n> +\ttree = parse_tree_indirect(sha1);\n> +\tif (!tree)\n> +\t\tdie(\"failed to unpack %s tree object\", sha1_to_hex(sha1));\n> +\tparse_tree(tree);\n> +\tinit_tree_desc(&t, tree->buffer, tree->size);\n> +\tif (unpack_trees(1, &t, &opts))\n> +\t\texit(128); /* We've already reported the error, finish dying */\n> +}\n\nIsn't this trashing all the cached stat info from the index?  If this is\nemulating \"reset --hard\", it also should set opts.merge and do\noneway_merge, after reading the current index in, I think.  Resetting the\nindex and the working tree is not particularly performance critical part,\nbut trashing the cached stat info would hurt the performance of everything\nthat reads the index after this function returns quite badly.  I suspect\nthat you might be better off forking the real thing (reset --hard) if you\ncannot get it right here.\n\n> +/* Get the name for the merge commit's message. */\n> +static void merge_name(const char *remote, struct strbuf *msg)\n> ...\n> +\tstrbuf_init(&buf, 0);\n> +\tstrbuf_addstr(&buf, \"refs/heads/\");\n> +\tstrbuf_addstr(&buf, remote);\n> +\tdwim_ref(buf.buf, buf.len, branch_head, &ref);\n> +\tif (!hashcmp(remote_head->sha1, branch_head)) {\n> +\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n> +\t\t\tsha1_to_hex(branch_head), remote);\n> +\t\treturn;\n> +\t}\n\nHmm, why not resolve_ref() so that it does not dwim at all?  The point of\nthe code is so that you can be confident that 'blah' *is* a local branch\nwhen you say \"branch 'blah'\".\n\n> +\t/* See if remote matches <name>~<number>, or <name>^ */\n> +\tptr = strrchr(remote, '^');\n> +\tif (ptr && ptr[1] == '\\0') {\n> +\t\tlen = strlen(remote);\n> +\t\twhile ((ptr = (char *)memrchr(remote, '^', len)))\n> +\t\t\tif (ptr && ptr[1] == '\\0')\n> +\t\t\t\tlen = ptr - remote - 1;\n> +\t\t\telse\n> +\t\t\t\tbreak;\n\nThat's a funny way to say:\n\n\tfor (len = 0, ptr = remote + strlen(remote);\n             remote < ptr && ptr[-1] == '^';\n             ptr--)\n\t\tlen++;\n\n> +\tif (len) {\n> +\t\tstruct strbuf truname = STRBUF_INIT;\n> +\t\tstrbuf_addstr(&truname, remote);\n> +\t\tstrbuf_setlen(&truname, len);\n> +\t\tif (dwim_ref(truname.buf, truname.len, buf_sha, &ref)) {\n> +\t\t\tstrbuf_addf(msg,\n> +\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n> +\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n> +\t\t\treturn;\n\nIsn't this wrong?  Giving \"v1.5.6~20\" to this code will strip ~20 and make\nremote = \"v1.5.6\", to which dwim_ref() will happily say Ok, and you end up\nsaying \"branch 'v1.5.6' (early part)\", don't you?\n\n> +static int read_tree_trivial(unsigned char *common, unsigned char *head,\n> +\tunsigned char *one)\n> +{\n> +\tint i, nr_trees = 0;\n> +\tstruct tree *trees[MAX_UNPACK_TREES];\n> +\tstruct tree_desc t[MAX_UNPACK_TREES];\n> +\tstruct unpack_trees_options opts;\n> +\n> +\tmemset(&opts, 0, sizeof(opts));\n> +\topts.head_idx = -1;\n\nIs this the correct head_idx value for this three-way merge?  I think it\nshould be 2 but please double check.\n\n> +static int commit_tree_trivial(const char *msg, unsigned const char *tree,\n> +\t\tstruct commit_list *parents, unsigned char *ret)\n> +{\n>  ...\n> +}\n\nWe may want to have another patch before this one to abstract most of\ncmd_commit_tree() out, perhaps?\n\n> +int cmd_merge(int argc, const char **argv, const char *prefix)\n> ...\n> +\t/*\n> +\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n> +\t * the way we can tell it is to see if the second token is HEAD,\n> +\t * but some people might have misused the interface and used a\n> +\t * committish that is the same as HEAD there instead.\n> +\t * Traditional format never would have \"-m\" so it is an\n> +\t * additional safety measure to check for it.\n> +\t */\n> +\tstrbuf_init(&buf, 0);\n> +\tif (argc > 1) {\n> +\t\tunsigned char second_sha1[20];\n> +\n> +\t\tif (get_sha1(argv[1], second_sha1))\n> +\t\t\tdie(\"Not a valid ref: %s\", argv[1]);\n> +\t\tsecond_token = lookup_commit_reference_gently(second_sha1, 0);\n> +\t\tif (!second_token)\n> +\t\t\tdie(\"'%s' is not a commit\", argv[1]);\n\nInteresting.\n\nThis _superficially_ is quite wrong, because the purpose of this part of\nthe code is to tell if we got old-style invocation, and we should not\nbarfing merely because what we got is _not_ old-style.  If it is not\nold-style, then it would be new-style, and the logic to tell if it is\nold-style should ideally not have much knowledge about the new-style\ninvocation to say \"hey, that's an incocrrect new-style invocation\".  By\nthe way, this part should probably be in a separate function:\n\n\tstatic int is_old_style_invocation(int ac, const char **gv);\n\nOld-style invocation of \"git merge\" (primarily by \"git pull\") was\nto call it as:\n\n\tgit merge \"message here\" HEAD $commit1 $commit2...\n\nand it checks the second token (\"HEAD\" in the above, but people can misuse\nthe interface to name the current branch name).  If the second token is\nnot a ref that resolves to a commit, all you know is that this is _not_ an\nold-style invocation, and calling the program with new-style is not a\ncrime.\n\nThe only reason this is wrong only superficially is because new style\ninvocation would always be:\n\n\tgit merge [options] $commit1 $commit2...\n\nafter stripping the options, and these seemingly wrong die() will complain\nwhen you try to create an Octopus with the new-style syntax and the\nparameter given as the second remote parent is not a commit.  So the logic\nis wrong, the fact that the user gets the same error message for incorrect\nold-style invocation (perhaps \"git merge <msg> HAED $commit\") and\nincorrect new-style invocation \"git merge $commit1 $nonsense\" is just an\naccident, and the end result does not hurt, but asks for a \"Huh? why does\nit check and complain only the second parent here but not the first one?\".\n\nIt is interesting, but feels quite dirty.\n\n> +\t}\n> +\n> +\tif (!have_message && second_token &&\n> +\t\t!hashcmp(second_token->object.sha1, head)) {\n\nYou need to know that resolve_ref() cleared head[] when head_invalid is\ntrue when reading this code to notice that, unlike the previous round of\nthis patch, it is Ok not to check head_invalid is fine here.  I somehow\nfeel it is an unnecessary optimization/obfuscation.\n\nBut once you have \"is_old_style_invocation\" suggested earlier, this part\nwould look much cleaner and the above comment would become unnecessary.\n\n> +\tfor (i = 0; i < argc; i++) {\n> +\t\tstruct object *o;\n> +\n> +\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n> +\t\tif (!o)\n> +\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n> +\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n> +\t\t\tremotes)->next;\n> +\n> +\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n> +\t\tsetenv(buf.buf, argv[i], 1);\n> +\t\tstrbuf_reset(&buf);\n> +\t}\n> +\n> +\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n> +\t\t\t0, NULL, OBJ_COMMIT);\n> +\t\tif (!o)\n> +\t\t\treturn 0;\n> +\n> +\t\tif (checkout_fast_forward(head, remoteheads->item->object.sha1))\n> +\t\t\treturn 0;\n\nWhen o does not peel well, or checkout_fast_forward() returns failure,\nthat would be a failure case, wouldn't it?  Why return 0?\n\nMaybe you misread \"exit\" in shell scripts?  It does not mean exit(0); it\nmeans \"exit with the same exit status as the last command\".  So\n\n\tnew_head=$(git rev-parse ...) &&\n        git read-tree -m -u ... &&\n        finish || exit\n\nwill exit non-zero if any of the commands chained by && fails.\n\n> +\t\tif (allow_trivial) {\n> +\t\t\t/* See if it is really trivial. */\n> +\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n> +\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n> +\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n> +\t\t\t\t\thead, remoteheads->item->object.sha1))\n> +\t\t\t\treturn merge_trivial();\n> +\t\t\tprintf(\"Nope.\\n\");\n\nNicer, much nicer.\n\n> +\t/*\n> +\t * At this point, we need a real merge.  No matter what strategy\n> +\t * we use, it would operate on the index, possibly affecting the\n> +\t * working tree, and when resolved cleanly, have the desired\n> +\t * tree in the index -- this means that the index must be in\n> +\t * sync with the head commit.  The strategies are responsible\n> +\t * to ensure this.\n> +\t */\n> +\tif (use_strategies.nr != 1) {\n> +\t\t/*\n> +\t\t * Stash away the local changes so that we can try more\n> +\t\t * than one.\n> +\t\t */\n> +\t\tsave_state();\n> +\t\tsingle_strategy = 0;\n> +\t} else {\n> +\t\tunlink(git_path(\"MERGE_STASH\"));\n> +\t\tsingle_strategy = 1;\n\nI think s/single_strategy/(use_strategies.nr == 1)/ in the remainder of the\ncode would be taking advantage of working in C ;-)\n\n> +\t\tif (ret) {\n> +\t\t\t/*\n> +\t\t\t * The backend exits with 1 when conflicts are\n> +\t\t\t * left to be resolved, with 2 when it does not\n> +\t\t\t * handle the given merge at all.\n> +\t\t\t */\n> +\t\t\tif (ret == 1) {\n\nProbably from here til ...\n\n> +\t\t\t\tint cnt = 0;\n> ...\n> +\t\t\t\tcnt += count_unmerged_entries();\n\n... here should be a separate \"evaluate_result()\" function.\n\n> +\t\t\t\tif (best_cnt <= 0 || cnt <= best_cnt) {\n> +\t\t\t\t\tbest_strategy =\n> +\t\t\t\t\t\t&use_strategies.items[i];\n> +\t\t\t\t\tbest_cnt = cnt;\n> +\t\t\t\t}\n> +\t\t\t}\n> +\t\t\tif (merge_was_ok)\n> +\t\t\t\tbreak;\n> +\t\t\telse\n> +\t\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\t/* Automerge succeeded. */\n> +\t\twrite_tree_trivial(result_tree);\n> +\t\tautomerge_was_ok = 1;\n> +\t\tbreak;\n> +\t}\n\nThe part of the file I did not comment in this message (except for the\n\"cast\" thing) looked straightforward enough.  I did not very carefully\nhunt for bugs, but I did not see anything obviously wrong.\n\nThanks.\n"},{"id":"81764","messageId":"81b0412b0806301041w70431da0u775d56920bf4a521@mail.gmail.com","threadId":"14186","inReplyTo":"7vd4lz4gtw.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-30T17:41:07Z","receivedAt":"2008-06-30T17:41:07Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"2008/6/30 Junio C Hamano <gitster@pobox.com>:\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n>> +     /* See if remote matches <name>~<number>, or <name>^ */\n>> +     ptr = strrchr(remote, '^');\n>> +     if (ptr && ptr[1] == '\\0') {\n>> +             len = strlen(remote);\n>> +             while ((ptr = (char *)memrchr(remote, '^', len)))\n>> +                     if (ptr && ptr[1] == '\\0')\n>> +                             len = ptr - remote - 1;\n>> +                     else\n>> +                             break;\n>\n> That's a funny way to say:\n>\n>        for (len = 0, ptr = remote + strlen(remote);\n>             remote < ptr && ptr[-1] == '^';\n>             ptr--)\n>                len++;\n>\n\nBesides, Cygwin has no memrchr\n"},{"id":"81790","messageId":"486961C7.2090603@free.fr","threadId":"14186","inReplyTo":"e8d1385cc49a06ca3fae28231ebc66a333ce4ffb.1214789764.git.vmiklos@frugalware.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Olivier Marin","fromEmail":"dkr+ml.git@free.fr","sentAt":"2008-06-30T22:44:23Z","receivedAt":"2008-06-30T22:44:23Z","isPatch":true,"sender":{"key":"dkr+ml.git@free.fr","avatar":null},"body":"Miklos Vajna a écrit :\n> \n> +static void finish(const unsigned char *new_head, const char *msg)\n> +{\n\n[...]\n\n> +\tif (new_head && show_diffstat) {\n> +\t\tstruct diff_options opts;\n> +\t\tdiff_setup(&opts);\n> +\t\topts.output_format |=\n> +\t\t\tDIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;\n> +\t\topts.detect_rename = DIFF_DETECT_RENAME;\n> +\t\tif (diff_use_color_default > 0)\n> +\t\t\tDIFF_OPT_SET(&opts, COLOR_DIFF);\n\nAh, I missed that. You should call diff_setup_done(), to finish diff_setup()\nand have a recursive diffstat.\n\nFor example:\n\n$ git checkout -b test c0f5c69\n$ git merge 2dce956\nUpdating c0f5c69..2dce956\nFast forward\n help.c |   22 ++++++++++++++--------\n 1 files changed, 14 insertions(+), 8 deletions(-)\n\nis wrong. Correct diffstat is:\n\n$ git diff --stat c0f5c69..2dce956\n Documentation/gitcli.txt        |   37 +++++++++++++++++++++++++++++++++----\n Documentation/gittutorial-2.txt |   10 +++++-----\n help.c                          |   22 ++++++++++++++--------\n t/test-lib.sh                   |    2 +-\n 4 files changed, 53 insertions(+), 18 deletions(-)\n\n> +\t\tdiff_tree_sha1(head, new_head, \"\", &opts);\n> +\t\tdiffcore_std(&opts);\n> +\t\tdiff_flush(&opts);\n> +\t}\n\n[...]\n\n> +int cmd_merge(int argc, const char **argv, const char *prefix)\n> +{\n\n[...]\n\n> +\tgit_config(git_merge_config, NULL);\n> +\n> +\t/* for color.diff and diff.color */\n> +\tgit_config(git_diff_ui_config, NULL);\n\nAlso, what about doing \"return git_diff_ui_config(k, v, cb)\" at the end of\ngit_merge_config() instead, to avoid parsing config files twice.\n\n> +\n> +\t/* for color.ui */\n> +\tif (diff_use_color_default == -1)\n> +\t\tdiff_use_color_default = git_use_color_default;\n> +\n\nOlivier.\n"},{"id":"81792","messageId":"20080630224833.GQ4729@genesis.frugalware.org","threadId":"14186","inReplyTo":"7vlk0n4h0a.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-30T22:48:33Z","receivedAt":"2008-06-30T22:48:33Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Sun, Jun 29, 2008 at 10:40:53PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > Actually reset_hard does not return if an error occures:\n> \n> I know that; didn't I already say \"Luckily no\"?  The point was it was not\n> apparent from the above 6 lines alone.\n\nAh, OK.\n\n> \n> >> > +\tfor (i = 0; i < use_strategies.nr; i++) {\n> >> > +\t\tif ((unsigned int)use_strategies.items[i].util &\n> >> > +\t\t\tNO_FAST_FORWARD)\n> >> > +\t\t\tallow_fast_forward = 0;\n> >> > +\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n> >> > +\t\t\tallow_trivial = 0;\n> >> \n> >> Can we abstract out these ugly casts?  Any code that use path_list to\n> >> store anything but list of paths (i.e. some value keyed with string) tends\n> >> to have this readability issue.\n> >\n> > If you don't cast, you can't use the & operator. If I change the\n> > path_list_item's util to be an unsigned number then I break fast-export.\n> > I think if we _really_ want to get rid of those casts, we could have\n> > something like:\n> \n> No, no, no.  That is not what I meant.\n> \n> The places that use use_strategies in your code knows too much about the\n> internal implementation detail of path_list, while path_list pretends to\n> be a general purpose \"table keyed with string\" facility.  The fact is that\n> the table is not a very useful general purpose abstraction unless you are\n> pointing at some structures that exist regardless of your use of path_list\n> (e.g. you have some \"struct object\" and you hold pointers in a path_list).\n> It does not work very well as an abstraction for use case like yours.\n> \n> With something like:\n> \n>         static inline unsigned nth_strategy_flags(struct path_list *s, int nth)\n>         {\n>                 return (unsigned) s->items[nth].util;\n>         }\n> \n> the checks would be more like:\n> \n> \tif (nth_strategy_flags(&use_strategies, i) & NO_FAST_FORWARD)\n> \t\t...\n> \n> or even:\n> \n>         static inline check_nth_strategy_flags(struct path_list_item *i, unsigned flags)\n>         {\n>         \treturn !((unsigned) i->util & flags);\n>         }\n> \n>         if (check_nth_strategy_flags(&use_strategies,items[i], NO_FAST_FORWARD)\n>         \t...\t\n> \n> either of which would be much easier on the eye.\n\nProbably this is subjective, but I think the previous form is easier to\nread, so I choose that one.\n"},{"id":"81795","messageId":"48696387.3070605@free.fr","threadId":"14186","inReplyTo":"alpine.DEB.1.00.0806291542070.9925@racer","subject":"Re: [PATCH 01/15] Move split_cmdline() to alias.c","fromName":"Olivier Marin","fromEmail":"dkr+ml.git@free.fr","sentAt":"2008-06-30T22:51:51Z","receivedAt":"2008-06-30T22:51:51Z","isPatch":true,"sender":{"key":"dkr+ml.git@free.fr","avatar":null},"body":"Johannes Schindelin a écrit :\n> \n> I am generally not a fan of crossing bridges miles before you reach them.\n> \n\nOK, as you want. :-) I don't really care about this patch, anyway.\n\nOlivier.\n"},{"id":"81799","messageId":"20080630225827.GR4729@genesis.frugalware.org","threadId":"14186","inReplyTo":"486961C7.2090603@free.fr","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-30T22:58:27Z","receivedAt":"2008-06-30T22:58:27Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Tue, Jul 01, 2008 at 12:44:23AM +0200, Olivier Marin <dkr+ml.git@free.fr> wrote:\n> > +\tif (new_head && show_diffstat) {\n> > +\t\tstruct diff_options opts;\n> > +\t\tdiff_setup(&opts);\n> > +\t\topts.output_format |=\n> > +\t\t\tDIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;\n> > +\t\topts.detect_rename = DIFF_DETECT_RENAME;\n> > +\t\tif (diff_use_color_default > 0)\n> > +\t\t\tDIFF_OPT_SET(&opts, COLOR_DIFF);\n> \n> Ah, I missed that. You should call diff_setup_done(), to finish diff_setup()\n> and have a recursive diffstat.\n> \n> For example:\n> \n> $ git checkout -b test c0f5c69\n> $ git merge 2dce956\n> Updating c0f5c69..2dce956\n> Fast forward\n>  help.c |   22 ++++++++++++++--------\n>  1 files changed, 14 insertions(+), 8 deletions(-)\n> \n> is wrong. Correct diffstat is:\n> \n> $ git diff --stat c0f5c69..2dce956\n>  Documentation/gitcli.txt        |   37 +++++++++++++++++++++++++++++++++----\n>  Documentation/gittutorial-2.txt |   10 +++++-----\n>  help.c                          |   22 ++++++++++++++--------\n>  t/test-lib.sh                   |    2 +-\n>  4 files changed, 53 insertions(+), 18 deletions(-)\n\nFixed in my working branch.\n\n> > +\tgit_config(git_merge_config, NULL);\n> > +\n> > +\t/* for color.diff and diff.color */\n> > +\tgit_config(git_diff_ui_config, NULL);\n> \n> Also, what about doing \"return git_diff_ui_config(k, v, cb)\" at the end of\n> git_merge_config() instead, to avoid parsing config files twice.\n\nSure, changed.\n\nThanks! :)\n"},{"id":"81816","messageId":"20080701021317.GS4729@genesis.frugalware.org","threadId":"14186","inReplyTo":"7vd4lz4gtw.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 13/13] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-07-01T02:13:17Z","receivedAt":"2008-07-01T02:13:17Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Sun, Jun 29, 2008 at 10:44:43PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > +static void save_state(void)\n> > +{\n> > +\tint fd;\n> > +\tstruct child_process stash;\n> > +\tconst char *argv[] = {\"stash\", \"create\", NULL};\n> > +\n> > +\tfd = open(git_path(\"MERGE_STASH\"), O_WRONLY | O_CREAT, 0666);\n> > +\tif (fd < 0)\n> > +\t\tdie(\"Could not write to %s\", git_path(\"MERGE_STASH\"));\n> > +\tmemset(&stash, 0, sizeof(stash));\n> > +\tstash.argv = argv;\n> > +\tstash.out = fd;\n> > +\tstash.git_cmd = 1;\n> > +\trun_command(&stash);\n> > +}\n> \n> I first thought \"heh, that's clever\" until I noticed that we use \"stash\n> create\" with \"stash apply\" these days instead of cpio for this.  I suspect\n> that we can do away without leaving the stash in this temporary file, but\n> that comment applies to the scripted version as well.\n\nWe can. I just did it. ;-)\n\n> By the way, it would be consistent to name counterpart to dropsave in the\n> scripted version as \"drop_save\" if you use \"save_state\" and \"restore_state\".\n\nOK, renamed.\n\n> \n> > +static void reset_hard(unsigned const char *sha1, int verbose)\n> > +{\n> > +\tstruct tree *tree;\n> > +\tstruct unpack_trees_options opts;\n> > +\tstruct tree_desc t;\n> > +\n> > +\tmemset(&opts, 0, sizeof(opts));\n> > +\topts.head_idx = -1;\n> > +\topts.src_index = &the_index;\n> > +\topts.dst_index = &the_index;\n> > +\topts.update = 1;\n> > +\topts.reset = 1;\n> > +\tif (verbose)\n> > +\t\topts.verbose_update = 1;\n> > +\n> > +\ttree = parse_tree_indirect(sha1);\n> > +\tif (!tree)\n> > +\t\tdie(\"failed to unpack %s tree object\", sha1_to_hex(sha1));\n> > +\tparse_tree(tree);\n> > +\tinit_tree_desc(&t, tree->buffer, tree->size);\n> > +\tif (unpack_trees(1, &t, &opts))\n> > +\t\texit(128); /* We've already reported the error, finish dying */\n> > +}\n> \n> Isn't this trashing all the cached stat info from the index?  If this is\n> emulating \"reset --hard\", it also should set opts.merge and do\n> oneway_merge, after reading the current index in, I think.  Resetting the\n> index and the working tree is not particularly performance critical part,\n> but trashing the cached stat info would hurt the performance of everything\n> that reads the index after this function returns quite badly.  I suspect\n> that you might be better off forking the real thing (reset --hard) if you\n> cannot get it right here.\n\nI just realized that builtin-reset forks read-tree as well, so I did\nalmost the same.\n\n> > +/* Get the name for the merge commit's message. */\n> > +static void merge_name(const char *remote, struct strbuf *msg)\n> > ...\n> > +\tstrbuf_init(&buf, 0);\n> > +\tstrbuf_addstr(&buf, \"refs/heads/\");\n> > +\tstrbuf_addstr(&buf, remote);\n> > +\tdwim_ref(buf.buf, buf.len, branch_head, &ref);\n> > +\tif (!hashcmp(remote_head->sha1, branch_head)) {\n> > +\t\tstrbuf_addf(msg, \"%s\\t\\tbranch '%s' of .\\n\",\n> > +\t\t\tsha1_to_hex(branch_head), remote);\n> > +\t\treturn;\n> > +\t}\n> \n> Hmm, why not resolve_ref() so that it does not dwim at all?  The point of\n> the code is so that you can be confident that 'blah' *is* a local branch\n> when you say \"branch 'blah'\".\n\nYes. I'm now using resolve_ref().\n\n> > +\t/* See if remote matches <name>~<number>, or <name>^ */\n> > +\tptr = strrchr(remote, '^');\n> > +\tif (ptr && ptr[1] == '\\0') {\n> > +\t\tlen = strlen(remote);\n> > +\t\twhile ((ptr = (char *)memrchr(remote, '^', len)))\n> > +\t\t\tif (ptr && ptr[1] == '\\0')\n> > +\t\t\t\tlen = ptr - remote - 1;\n> > +\t\t\telse\n> > +\t\t\t\tbreak;\n> \n> That's a funny way to say:\n> \n> \tfor (len = 0, ptr = remote + strlen(remote);\n>              remote < ptr && ptr[-1] == '^';\n>              ptr--)\n> \t\tlen++;\n\nAh, and this way I don't need memrchr(), which was pointed out to be\nproblemtic on Cygwin.\n\n> \n> > +\tif (len) {\n> > +\t\tstruct strbuf truname = STRBUF_INIT;\n> > +\t\tstrbuf_addstr(&truname, remote);\n> > +\t\tstrbuf_setlen(&truname, len);\n> > +\t\tif (dwim_ref(truname.buf, truname.len, buf_sha, &ref)) {\n> > +\t\t\tstrbuf_addf(msg,\n> > +\t\t\t\t\"%s\\t\\tbranch '%s' (early part) of .\\n\",\n> > +\t\t\t\tsha1_to_hex(remote_head->sha1), truname.buf);\n> > +\t\t\treturn;\n> \n> Isn't this wrong?  Giving \"v1.5.6~20\" to this code will strip ~20 and make\n> remote = \"v1.5.6\", to which dwim_ref() will happily say Ok, and you end up\n> saying \"branch 'v1.5.6' (early part)\", don't you?\n\nRight. Now I do\n\n        strbuf_addstr(&truname, \"refs/heads/\");\n\nBefore appending the remote name to truname, so that should exclude\ntags.\n\n> > +static int read_tree_trivial(unsigned char *common, unsigned char *head,\n> > +\tunsigned char *one)\n> > +{\n> > +\tint i, nr_trees = 0;\n> > +\tstruct tree *trees[MAX_UNPACK_TREES];\n> > +\tstruct tree_desc t[MAX_UNPACK_TREES];\n> > +\tstruct unpack_trees_options opts;\n> > +\n> > +\tmemset(&opts, 0, sizeof(opts));\n> > +\topts.head_idx = -1;\n> \n> Is this the correct head_idx value for this three-way merge?  I think it\n> should be 2 but please double check.\n\nYes, you are right. I just checked builtin-read-tree and it's 2, not -1.\n\n> > +static int commit_tree_trivial(const char *msg, unsigned const char *tree,\n> > +\t\tstruct commit_list *parents, unsigned char *ret)\n> > +{\n> >  ...\n> > +}\n> \n> We may want to have another patch before this one to abstract most of\n> cmd_commit_tree() out, perhaps?\n\nDone. And now builtin-merge uses commit_tree() as well.\n\n> > +int cmd_merge(int argc, const char **argv, const char *prefix)\n> > ...\n> > +\t/*\n> > +\t * This could be traditional \"merge <msg> HEAD <commit>...\"  and\n> > +\t * the way we can tell it is to see if the second token is HEAD,\n> > +\t * but some people might have misused the interface and used a\n> > +\t * committish that is the same as HEAD there instead.\n> > +\t * Traditional format never would have \"-m\" so it is an\n> > +\t * additional safety measure to check for it.\n> > +\t */\n> > +\tstrbuf_init(&buf, 0);\n> > +\tif (argc > 1) {\n> > +\t\tunsigned char second_sha1[20];\n> > +\n> > +\t\tif (get_sha1(argv[1], second_sha1))\n> > +\t\t\tdie(\"Not a valid ref: %s\", argv[1]);\n> > +\t\tsecond_token = lookup_commit_reference_gently(second_sha1, 0);\n> > +\t\tif (!second_token)\n> > +\t\t\tdie(\"'%s' is not a commit\", argv[1]);\n> \n> Interesting.\n> \n> This _superficially_ is quite wrong, because the purpose of this part of\n> the code is to tell if we got old-style invocation, and we should not\n> barfing merely because what we got is _not_ old-style.  If it is not\n> old-style, then it would be new-style, and the logic to tell if it is\n> old-style should ideally not have much knowledge about the new-style\n> invocation to say \"hey, that's an incocrrect new-style invocation\".  By\n> the way, this part should probably be in a separate function:\n> \n> \tstatic int is_old_style_invocation(int ac, const char **gv);\n\nOK, I broke out is_old_style_invocation() from cmd_merge().\n\n> Old-style invocation of \"git merge\" (primarily by \"git pull\") was\n> to call it as:\n> \n> \tgit merge \"message here\" HEAD $commit1 $commit2...\n> \n> and it checks the second token (\"HEAD\" in the above, but people can misuse\n> the interface to name the current branch name).  If the second token is\n> not a ref that resolves to a commit, all you know is that this is _not_ an\n> old-style invocation, and calling the program with new-style is not a\n> crime.\n> \n> The only reason this is wrong only superficially is because new style\n> invocation would always be:\n> \n> \tgit merge [options] $commit1 $commit2...\n> \n> after stripping the options, and these seemingly wrong die() will complain\n> when you try to create an Octopus with the new-style syntax and the\n> parameter given as the second remote parent is not a commit.  So the logic\n> is wrong, the fact that the user gets the same error message for incorrect\n> old-style invocation (perhaps \"git merge <msg> HAED $commit\") and\n> incorrect new-style invocation \"git merge $commit1 $nonsense\" is just an\n> accident, and the end result does not hurt, but asks for a \"Huh? why does\n> it check and complain only the second parent here but not the first one?\".\n> \n> It is interesting, but feels quite dirty.\n\nNow if the second token is a valid SHA1 then I die() if it's not a\ncommit, but otherwise I just assume it's a new-style invocation.\n\n> > +\tif (!have_message && second_token &&\n> > +\t\t!hashcmp(second_token->object.sha1, head)) {\n> \n> You need to know that resolve_ref() cleared head[] when head_invalid is\n> true when reading this code to notice that, unlike the previous round of\n> this patch, it is Ok not to check head_invalid is fine here.  I somehow\n> feel it is an unnecessary optimization/obfuscation.\n> \n> But once you have \"is_old_style_invocation\" suggested earlier, this part\n> would look much cleaner and the above comment would become unnecessary.\n\nYes, now it's just:\n\n       if (!have_message && is_old_style_invocation(argc, argv)) {\n\n> \n> > +\tfor (i = 0; i < argc; i++) {\n> > +\t\tstruct object *o;\n> > +\n> > +\t\to = peel_to_type(argv[i], 0, NULL, OBJ_COMMIT);\n> > +\t\tif (!o)\n> > +\t\t\tdie(\"%s - not something we can merge\", argv[i]);\n> > +\t\tremotes = &commit_list_insert(lookup_commit(o->sha1),\n> > +\t\t\tremotes)->next;\n> > +\n> > +\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n> > +\t\tsetenv(buf.buf, argv[i], 1);\n> > +\t\tstrbuf_reset(&buf);\n> > +\t}\n> > +\n> > +\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n> > +\t\t\t0, NULL, OBJ_COMMIT);\n> > +\t\tif (!o)\n> > +\t\t\treturn 0;\n> > +\n> > +\t\tif (checkout_fast_forward(head, remoteheads->item->object.sha1))\n> > +\t\t\treturn 0;\n> \n> When o does not peel well, or checkout_fast_forward() returns failure,\n> that would be a failure case, wouldn't it?  Why return 0?\n> \n> Maybe you misread \"exit\" in shell scripts?  It does not mean exit(0); it\n> means \"exit with the same exit status as the last command\".  So\n> \n> \tnew_head=$(git rev-parse ...) &&\n>         git read-tree -m -u ... &&\n>         finish || exit\n> \n> will exit non-zero if any of the commands chained by && fails.\n\nThanks, that was the case. I thought \"false || exit\" exits with status\ncode 0.\n\n> > +\t/*\n> > +\t * At this point, we need a real merge.  No matter what strategy\n> > +\t * we use, it would operate on the index, possibly affecting the\n> > +\t * working tree, and when resolved cleanly, have the desired\n> > +\t * tree in the index -- this means that the index must be in\n> > +\t * sync with the head commit.  The strategies are responsible\n> > +\t * to ensure this.\n> > +\t */\n> > +\tif (use_strategies.nr != 1) {\n> > +\t\t/*\n> > +\t\t * Stash away the local changes so that we can try more\n> > +\t\t * than one.\n> > +\t\t */\n> > +\t\tsave_state();\n> > +\t\tsingle_strategy = 0;\n> > +\t} else {\n> > +\t\tunlink(git_path(\"MERGE_STASH\"));\n> > +\t\tsingle_strategy = 1;\n> \n> I think s/single_strategy/(use_strategies.nr == 1)/ in the remainder of the\n> code would be taking advantage of working in C ;-)\n\nI dropped single_strategy.\n\n> \n> > +\t\tif (ret) {\n> > +\t\t\t/*\n> > +\t\t\t * The backend exits with 1 when conflicts are\n> > +\t\t\t * left to be resolved, with 2 when it does not\n> > +\t\t\t * handle the given merge at all.\n> > +\t\t\t */\n> > +\t\t\tif (ret == 1) {\n> \n> Probably from here til ...\n> \n> > +\t\t\t\tint cnt = 0;\n> > ...\n> > +\t\t\t\tcnt += count_unmerged_entries();\n> \n> ... here should be a separate \"evaluate_result()\" function.\n\nDone.\n"},{"id":"82238","messageId":"e2b179460807040934m27e752e7s4f6a786d45d3bc53@mail.gmail.com","threadId":"14186","inReplyTo":"99b931de28ab3017269d15c334c83e242d025489.1214581610.git.vmiklos@frugalware.org","subject":"Re: [PATCH 04/15] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Mike Ralphson","fromEmail":"mike.ralphson@gmail.com","sentAt":"2008-07-04T16:34:05Z","receivedAt":"2008-07-04T16:34:05Z","isPatch":true,"sender":{"key":"mike.ralphson@gmail.com","avatar":"https://avatars.githubusercontent.com/u/21603?v=4"},"body":"2008/6/27 Miklos Vajna <vmiklos@frugalware.org>:\n>\n> Test if the given strategies are used and test the case when multiple\n> strategies are configured using a space separated list.\n>\n> Also test if the best strategy is picked if none is specified.  This is\n> done by adding a simple test case where recursive detects a rename, but\n> resolve does not, and verify that finally merge will pick up the\n> previous.\n>\n> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n> ---\n>  t/t7601-merge-pull-config.sh |  129 ++++++++++++++++++++++++++++++++++++++++++\n\n> +\n> +conflict_count()\n> +{\n> +       eval $1=`{\n> +               git diff-files --name-only\n> +               git ls-files --unmerged\n> +       } | wc -l`\n> +}\n> +\n\nThis here causes the test to fail on AIX (and likely other OS, such as\napparently OSX) where wc -l outputs whitespace. See\nhttp://article.gmane.org/gmane.comp.version-control.git/80450\n\nHere we want the line count not just a return value, so is the\nfollowing acceptable?\n\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nindex 32585f8..9b6097d 100755\n--- a/t/t7601-merge-pull-config.sh\n+++ b/t/t7601-merge-pull-config.sh\n@@ -73,7 +73,7 @@ conflict_count()\n        eval $1=`{\n                git diff-files --name-only\n                git ls-files --unmerged\n-       } | wc -l`\n+       } | wc -l | tr -d \\ `\n }\n\n # c4 - c5\n\nSigned-off-by: Mike Ralphson <mike@abacus.co.uk>\n\nAnyway, I thought we preferred $() to backticks?\n\nI do apologise for not being around for the earlier comedy breakage on\nAIX, my sysadmins decided to 'improve' our firewall rules which cut my\nautomated builds off just after the final rc for 1.5.6, then I was on\nvacation miles from any computers.\n\nMike\n"},{"id":"82263","messageId":"20080705002634.GF4729@genesis.frugalware.org","threadId":"14186","inReplyTo":"e2b179460807040934m27e752e7s4f6a786d45d3bc53@mail.gmail.com","subject":"Re: [PATCH 04/15] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-07-05T00:26:34Z","receivedAt":"2008-07-05T00:26:34Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Fri, Jul 04, 2008 at 05:34:05PM +0100, Mike Ralphson <mike.ralphson@gmail.com> wrote:\n> > +conflict_count()\n> > +{\n> > +       eval $1=`{\n> > +               git diff-files --name-only\n> > +               git ls-files --unmerged\n> > +       } | wc -l`\n> > +}\n> > +\n> \n> This here causes the test to fail on AIX (and likely other OS, such as\n> apparently OSX) where wc -l outputs whitespace. See\n> http://article.gmane.org/gmane.comp.version-control.git/80450\n> \n> Here we want the line count not just a return value, so is the\n> following acceptable?\n> \n> diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\n> index 32585f8..9b6097d 100755\n> --- a/t/t7601-merge-pull-config.sh\n> +++ b/t/t7601-merge-pull-config.sh\n> @@ -73,7 +73,7 @@ conflict_count()\n>         eval $1=`{\n>                 git diff-files --name-only\n>                 git ls-files --unmerged\n> -       } | wc -l`\n> +       } | wc -l | tr -d \\ `\n>  }\n\nAt least it does not break the test for me on Linux. But haven't this\ncause you problems in git-merge.sh?  I copied this code chunk from\nthere.\n\n> Anyway, I thought we preferred $() to backticks?\n\nSee above, but sure, we can.\n"},{"id":"82264","messageId":"1215217920-8506-1-git-send-email-vmiklos@frugalware.org","threadId":"14186","inReplyTo":"20080705002634.GF4729@genesis.frugalware.org","subject":"[PATCH] Fix t7601-merge-pull-config.sh on AIX","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-07-05T00:32:00Z","receivedAt":"2008-07-05T00:32:00Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"The test failed on AIX (and likely other OS, such as apparently OSX)\nwhere wc -l outputs whitespace.\n\nSigned-off-by: Mike Ralphson <mike@abacus.co.uk>\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nHere is the same, with backticks avoided, and with a proper commit\nmessage.\n\n t/t7601-merge-pull-config.sh |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nindex 32585f8..12f71ad 100755\n--- a/t/t7601-merge-pull-config.sh\n+++ b/t/t7601-merge-pull-config.sh\n@@ -70,10 +70,10 @@ test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octo\n \n conflict_count()\n {\n-\teval $1=`{\n+\teval $1=$({\n \t\tgit diff-files --name-only\n \t\tgit ls-files --unmerged\n-\t} | wc -l`\n+\t} | wc -l | tr -d \\ )\n }\n \n # c4 - c5\n-- \n1.5.6.1\n"},{"id":"82265","messageId":"7vk5g1t81c.fsf@gitster.siamese.dyndns.org","threadId":"14186","inReplyTo":"1215217920-8506-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Fix t7601-merge-pull-config.sh on AIX","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-05T01:49:03Z","receivedAt":"2008-07-05T01:49:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\n> index 32585f8..12f71ad 100755\n> --- a/t/t7601-merge-pull-config.sh\n> +++ b/t/t7601-merge-pull-config.sh\n> @@ -70,10 +70,10 @@ test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octo\n>  \n>  conflict_count()\n>  {\n> -\teval $1=`{\n> +\teval $1=$({\n>  \t\tgit diff-files --name-only\n>  \t\tgit ls-files --unmerged\n> -\t} | wc -l`\n> +\t} | wc -l | tr -d \\ )\n>  }\n\nIn any case, this feels like an unnecessary use of eval.  The call site\nyou have look like this:\n\n\tconflict_count resolve_count\n\nbut it is more natural if you are programming in shell to call it like:\n\n\tresolve_count=$(count_conflicts)\n\nand it is more natural to write count_conflicts like this:\n\n\tcount_conflicts () {\n\t\t{\n                \tgit diff-files --name-only --diff-filter=U\n                        git ls-files --unmerged\n                } | wc -l\n        }\n\nBut I am puzzled about the alledged *breakage* -- look at your call\nsites.\n\n        reset --hard\n\t.. do a merge ..\n        conflict_count count_one\n\n\treset --hard\n        .. do another merge ..\n        conlict_count count_two\n\n\treset --hard\n        .. do yet another merge ..\n        conlict_count count_three\n\n\ttest \"$count_three\" = \"$count_two\"\n\nAt any point, you do not do numerical comparison, and I do not think extra\nwhitespace from other \"wc\" implementations matter, as long as they are\nconsistent.\n\nIf you are going to do numerical comparison in later versions, you can\njust drop the dq around parameters of test:\n\n\ttest $count_three = $count_two\n"}]}