{"thread":{"id":"13902","subject":"[PATCH 03/10] Move parse-options's skip_prefix() to git-compat-util.h","startedAt":"2008-06-11T20:50:24Z","lastAt":"2008-06-12T16:59:58Z","messageCount":14,"participants":["Miklos Vajna","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"79494","messageId":"cover.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":null,"subject":"[PATCH 00/10] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:24Z","receivedAt":"2008-06-11T20:50:24Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Hi,\n\nIt was almost a week ago I posted the previous series here and there\nwere numerous improvements since then.\n\nA short changelog:\n\n - introducing filter_independent() in commit.c\n\n - avoiding commit_list_append()\n\n - using parseopt's skip_prefix()\n\n - avoiding unnecessary strbuf_addf() in builtin-fmt-merge-msg.c\n\nAs always, comments are welcome. I hope I addressed all the issues which\nwere pointed out in the previous thread. If this is not the case, please\ncorrect me.\n\nThanks.\n\nMiklos Vajna (10):\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  parseopt: add a new PARSE_OPT_ARGV0_IS_AN_OPTION option\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  Introduce filter_independent() in commit.c\n  Build in merge\n\n Makefile                                      |    2 +-\n alias.c                                       |   54 ++\n builtin-fmt-merge-msg.c                       |  157 ++--\n builtin-merge-recursive.c                     |    8 -\n builtin-merge.c                               | 1128 +++++++++++++++++++++++++\n builtin-read-tree.c                           |   24 -\n builtin-remote.c                              |   39 +-\n builtin.h                                     |    4 +\n cache.h                                       |    3 +\n commit.c                                      |   56 ++\n commit.h                                      |    3 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git-compat-util.h                             |    6 +\n git.c                                         |   54 +--\n parse-options.c                               |   11 +-\n parse-options.h                               |    1 +\n read-cache.c                                  |   31 +\n t/t7601-merge-pull-config.sh                  |   72 ++\n 18 files changed, 1482 insertions(+), 171 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"},{"id":"79492","messageId":"4818a0b71b7f6b44ef49621045a5871458ba5c38.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"cover.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 01/10] Move split_cmdline() to alias.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:25Z","receivedAt":"2008-06-11T20:50:25Z","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>\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 0a63c0e..18ec38c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -832,5 +832,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 15a0e71..0a0b11e 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.rc2.dirty\n"},{"id":"79493","messageId":"dedcc8309fed3282df455bec7fbc9d0f8275b74b.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"4818a0b71b7f6b44ef49621045a5871458ba5c38.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 02/10] Move commit_list_count() to commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:26Z","receivedAt":"2008-06-11T20:50:26Z","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>\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 4aa28a1..98b09fb 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.rc2.dirty\n"},{"id":"79491","messageId":"9a5a141bd92756b529514579818a2c59a1a03899.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"dedcc8309fed3282df455bec7fbc9d0f8275b74b.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 03/10] Move parse-options's skip_prefix() to git-compat-util.h","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:27Z","receivedAt":"2008-06-11T20:50:27Z","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>\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 c49f00f..2bf0593 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@@ -504,10 +512,15 @@ static int show_or_prune(int argc, const char **argv, int prune)\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 c04e8ba..6470d6c 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 acf3fe3..b98833c 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.rc2.dirty\n"},{"id":"79496","messageId":"c72207b02d37f3d8c064534264219f4dd38c7559.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"9a5a141bd92756b529514579818a2c59a1a03899.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 04/10] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:28Z","receivedAt":"2008-06-11T20:50:28Z","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\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n t/t7601-merge-pull-config.sh |   72 ++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 72 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..c0b550e\n--- /dev/null\n+++ b/t/t7601-merge-pull-config.sh\n@@ -0,0 +1,72 @@\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+test_done\n-- \n1.5.6.rc2.dirty\n"},{"id":"79500","messageId":"7b7cdd9349feee76ad97243df807f8f360484083.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"c72207b02d37f3d8c064534264219f4dd38c7559.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 05/10] parseopt: add a new PARSE_OPT_ARGV0_IS_AN_OPTION option","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:29Z","receivedAt":"2008-06-11T20:50:29Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This new option tells parse-options not to ignore argv[0]. This is\nuseful when argv cames from split_cmdline(), as in that case argv[0]\ncontains a valuable option as well.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n parse-options.c |    5 +++++\n parse-options.h |    1 +\n 2 files changed, 6 insertions(+), 0 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex b98833c..1d01a86 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -249,6 +249,11 @@ int parse_options(int argc, const char **argv, const struct option *options,\n {\n \tstruct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };\n \n+\tif (flags & PARSE_OPT_ARGV0_IS_AN_OPTION) {\n+\t\targs.argv = argv;\n+\t\targs.argc = argc;\n+\t}\n+\n \tfor (; args.argc; args.argc--, args.argv++) {\n \t\tconst char *arg = args.argv[0];\n \ndiff --git a/parse-options.h b/parse-options.h\nindex 4ee443d..3238401 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -20,6 +20,7 @@ enum parse_opt_type {\n enum parse_opt_flags {\n \tPARSE_OPT_KEEP_DASHDASH = 1,\n \tPARSE_OPT_STOP_AT_NON_OPTION = 2,\n+\tPARSE_OPT_ARGV0_IS_AN_OPTION = 4,\n };\n \n enum parse_opt_option_flags {\n-- \n1.5.6.rc2.dirty\n"},{"id":"79498","messageId":"50a291213098f6da9869354c5ede54c2695570a8.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"7b7cdd9349feee76ad97243df807f8f360484083.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 06/10] Move read_cache_unmerged() to read-cache.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:30Z","receivedAt":"2008-06-11T20:50:30Z","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>\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 18ec38c..54368cc 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@@ -357,6 +358,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 8e5fbb6..ea23726 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1394,3 +1394,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.rc2.dirty\n"},{"id":"79495","messageId":"276ad490f3730bf6bb061d7b3d84975ac902b0a7.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"50a291213098f6da9869354c5ede54c2695570a8.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 07/10] git-fmt-merge-msg: make it usable from other builtins","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:31Z","receivedAt":"2008-06-11T20:50:31Z","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 |  157 +++++++++++++++++++++++++++--------------------\n builtin.h               |    3 +\n 2 files changed, 94 insertions(+), 66 deletions(-)\n\ndiff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c\nindex b892621..91f08e9 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,129 @@ 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+\tfor (;;) {\n+\t\tint len;\n+\t\tchar *newline;\n+\n+\t\tif (pos >= in->len)\n+\t\t\tbreak;\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.rc2.dirty\n"},{"id":"79497","messageId":"2acf029a4230f840e9f3f936e1949e01c774b4a2.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"276ad490f3730bf6bb061d7b3d84975ac902b0a7.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 08/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:32Z","receivedAt":"2008-06-11T20:50:32Z","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>\n---\n commit.c |   32 ++++++++++++++++++++++++++++++++\n commit.h |    1 +\n 2 files changed, 33 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex bbf9c75..13fa39d 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -600,6 +600,38 @@ 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, int cleanup)\n+{\n+\tstruct commit_list *i, *j, *k, *ret = NULL;\n+\n+\tfor (i = in; i; i = i->next) {\n+\t\tif (!ret)\n+\t\t\tcommit_list_append(i->item, &ret);\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, cleanup);\n+\t\t\t\t/*\n+\t\t\t\t * Now we just append bases to new, but\n+\t\t\t\t * calling commit_list_append() for each\n+\t\t\t\t * item would be expensive, so do it by\n+\t\t\t\t * hand.\n+\t\t\t\t */\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..ca858ed 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, int cleanup);\n \n extern int register_shallow(const unsigned char *sha1);\n extern int unregister_shallow(const unsigned char *sha1);\n-- \n1.5.6.rc2.dirty\n"},{"id":"79499","messageId":"456e3b118a83f446e9dc85e5aa4a2c6686a91626.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"2acf029a4230f840e9f3f936e1949e01c774b4a2.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 09/10] Introduce filter_independent() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:33Z","receivedAt":"2008-06-11T20:50:33Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This is similar to git-show-branch --independent: It filters out commits\nwhich are reachable from any other item from the input list.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n commit.c |   16 ++++++++++++++++\n commit.h |    1 +\n 2 files changed, 17 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 13fa39d..54079df 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -710,3 +710,19 @@ 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 *filter_independent(unsigned char *head,\n+\tstruct commit_list *heads)\n+{\n+\tstruct commit_list *i, *bases, *ret = NULL;\n+\tstruct commit_list **pptr = &ret;\n+\n+\tcommit_list_insert(lookup_commit(head), &heads);\n+\n+\tbases = get_octopus_merge_bases(heads, 0);\n+\n+\tfor (i = heads; i; i = i->next)\n+\t\tif (!(i->item->object.flags & RESULT))\n+\t\t\tpptr = &commit_list_insert(i->item, pptr)->next;\n+\treturn ret;\n+}\ndiff --git a/commit.h b/commit.h\nindex ca858ed..22839ac 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -131,6 +131,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,\n \t\tint depth, int shallow_flag, int not_shallow_flag);\n \n int in_merge_bases(struct commit *, struct commit **, int);\n+struct commit_list *filter_independent(unsigned char *head, struct commit_list *heads);\n \n extern int interactive_add(int argc, const char **argv, const char *prefix);\n extern int rerere(void);\n-- \n1.5.6.rc2.dirty\n"},{"id":"79501","messageId":"0d44808a12ec3762d05eb3777fb5ca4e97e011bd.1213217187.git.vmiklos@frugalware.org","threadId":"13902","inReplyTo":"456e3b118a83f446e9dc85e5aa4a2c6686a91626.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH 10/10] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T20:50:34Z","receivedAt":"2008-06-11T20:50:34Z","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                               | 1128 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git.c                                         |    1 +\n 5 files changed, 1131 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 1937507..2830cca 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..54561ea\n--- /dev/null\n+++ b/builtin-merge.c\n@@ -0,0 +1,1128 @@\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+\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_addstr(buf, 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(const char *path, void *util,\n+\tstruct path_list *list)\n+{\n+\tpath_list_append(path, list)->util = 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 *list = opt->value;\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(arg, item->util, list);\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 sucesses (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()\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()\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()\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+\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+\tif (squash)\n+\t\tprintf(\"%s (nothing to squash)\\n\", msg);\n+\telse\n+\t\tprintf(\"%s\\n\", msg);\n+\tdropsave();\n+}\n+\n+static void squash_message(int out_fd)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tstruct strbuf out;\n+\tstruct commit_list *j;\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\tj->item->object.flags &= ~UNINTERESTING;\n+\t\tadd_pending_object(&rev, &j->item->object, NULL);\n+\t}\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(out_fd, out.buf, out.len);\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+\tsnprintf(index, sizeof(index), \"GIT_INDEX_FILE=%s\", get_index_file());\n+\tenv[0] = index;\n+\tenv[1] = NULL;\n+\n+\targv[0] = git_path(\"hooks/%s\", name);\n+\tif (squash)\n+\t\targv[1] = \"1\";\n+\telse\n+\t\targv[1] = \"0\";\n+\targv[2] = NULL;\n+\n+\tif (access(argv[0], X_OK) < 0)\n+\t\treturn 0;\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+\tconst char *argv_gc_auto[] = { \"gc\", \"--auto\", NULL };\n+\tstruct diff_options opts;\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\tint fd;\n+\t\tprintf(\"Squash commit -- not updating HEAD\\n\");\n+\t\tfd = open(git_path(\"SQUASH_MSG\"), O_WRONLY | O_CREAT, 0666);\n+\t\tif (fd < 0)\n+\t\t\tdie(\"Could not write to %s\", git_path(\"SQUASH_MSG\"));\n+\t\tsquash_message(fd);\n+\t\tclose(fd);\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\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\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\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 match = 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\tmatch = 1;\n+\telse {\n+\t\tptr = strrchr(remote, '~');\n+\t\tif (ptr && *(ptr+1) != '0' && isdigit(*(ptr+1))) {\n+\t\t\tptr++;\n+\t\t\tmatch = 1;\n+\t\t\twhile (*(++ptr))\n+\t\t\t\tif (!isdigit(*ptr)) {\n+\t\t\t\t\tmatch = 0;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t}\n+\t}\n+\tif (match) {\n+\t\tstruct strbuf truname;\n+\t\tstrbuf_addstr(&truname, remote);\n+\t\tstrbuf_setlen(&truname, strrchr(truname.buf, '~')-truname.buf);\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!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 == NULL)\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\tparse_options(argc, argv, builtin_merge_options,\n+\t\t\t\tbuiltin_merge_usage,\n+\t\t\t\tPARSE_OPT_ARGV0_IS_AN_OPTION);\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;\n+\n+\tmemset(&lock_file, 0, sizeof(lock_file));\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 = xcalloc(1, sizeof(*opts.dir));\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+\twhile (1) {\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(list.items[i].path,\n+\t\t\t\t\titem->util, &use_strategies);\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].path,\n+\t\t\t\tstrategies.items[i].util,\n+\t\t\t\t&use_strategies);\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+\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 == 0)\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\tif (!merge_msg.len) {\n+\t\t\t/*\n+\t\t\t * All the rest are the commits being merged;\n+\t\t\t * prepare the standard merge summary message to\n+\t\t\t * be appended to the given message.  If remote\n+\t\t\t * is invalid we will die later in the common\n+\t\t\t * codepath so we discard the error in this\n+\t\t\t * loop.\n+\t\t\t */\n+\t\t\tstruct strbuf msg;\n+\n+\t\t\tstrbuf_init(&msg, 0);\n+\t\t\tfor (i = 0; i < argc; i++)\n+\t\t\t\tmerge_name(argv[i], &msg);\n+\t\t\tfmt_merge_msg(option_log, &msg, &merge_msg);\n+\t\t\tif (merge_msg.len)\n+\t\t\t\tstrbuf_setlen(&merge_msg, merge_msg.len-1);\n+\t\t}\n+\t}\n+\n+\tif (head_invalid || argc == 0)\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, 1);\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 merge then we are up to\n+\t\t * 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+\n+\t\tprintf(\"Updating %s..%s\\n\",\n+\t\t\tfind_unique_abbrev(head, DEFAULT_ABBREV),\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 and not fast forward.  Need\n+\t\t * a real merge.\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\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\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\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 = filter_independent(head, remoteheads);\n+\t\telse {\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 0a0b11e..b389dd5 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.rc2.dirty\n"},{"id":"79504","messageId":"1213219039-1211-1-git-send-email-vmiklos@frugalware.org","threadId":"13902","inReplyTo":"2acf029a4230f840e9f3f936e1949e01c774b4a2.1213217187.git.vmiklos@frugalware.org","subject":"[PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-11T21:17:19Z","receivedAt":"2008-06-11T21:17:19Z","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>\n---\n\nOn Wed, Jun 11, 2008 at 10:50:32PM +0200, Miklos Vajna <vmiklos@frugalware.org> wrote:\n> +                     commit_list_append(i->item, &ret);\n\nOops, sorry. When I cherry-picked the old get_octopus_merge_bases(), I\npicked the version that did not use commit_list_insert() here yet.\n\nHere is the correct version, which one I have in my working branch.\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..f9410d8 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, int cleanup)\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, cleanup);\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..ca858ed 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, int cleanup);\n \n extern int register_shallow(const unsigned char *sha1);\n extern int unregister_shallow(const unsigned char *sha1);\n-- \n1.5.6.rc2.dirty\n"},{"id":"79528","messageId":"7vprqna5t6.fsf@gitster.siamese.dyndns.org","threadId":"13902","inReplyTo":"1213219039-1211-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-11T23:51:49Z","receivedAt":"2008-06-11T23:51:49Z","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/commit.c b/commit.c\n> index bbf9c75..f9410d8 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, int cleanup)\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, cleanup);\n\nWhy is passing \"cleanup\" through to get_merge_bases() safe, even when you\nare running it more than once?\n\nget_merge_bases() uses PARENT1, PARENT2 and other flags internally, and\nthe information necessary to clean them efficiently (i.e. the set of\ncommits that could be smudged with these flags due to its operation) is\nknown only to it _after it finishes_.  You cannot even say \"previous run\ndid not clean things up, so please clean-up before starting this round\"\nupfront.  That is why we have clean-up loops at the _end_ of the function\nafter it finishes computing what it wants to return.\n\nThe only reason clean-up is optional to the function is because there are\none-shot callers such as \"git merge-base\" that do not want to pay penalty\nfor cleaning up (the only time you do not have to tell it to clean up is\nwhen you know the invocation is the _last_ invocation of the function, iow\nthere is no later invocation that will be harmed by leftover flags).\n"},{"id":"79606","messageId":"1213289998-14410-1-git-send-email-vmiklos@frugalware.org","threadId":"13902","inReplyTo":"7vprqna5t6.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-12T16:59:58Z","receivedAt":"2008-06-12T16:59:58Z","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>\n---\n\nOn Wed, Jun 11, 2008 at 04:51:49PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Why is passing \"cleanup\" through to get_merge_bases() safe, even when you\n> are running it more than once?\n>\n> get_merge_bases() uses PARENT1, PARENT2 and other flags internally, and\n> the information necessary to clean them efficiently (i.e. the set of\n> commits that could be smudged with these flags due to its operation) is\n> known only to it _after it finishes_.  You cannot even say \"previous run\n> did not clean things up, so please clean-up before starting this round\"\n> upfront.  That is why we have clean-up loops at the _end_ of the function\n> after it finishes computing what it wants to return.\n>\n> The only reason clean-up is optional to the function is because there are\n> one-shot callers such as \"git merge-base\" that do not want to pay penalty\n> for cleaning up (the only time you do not have to tell it to clean up is\n> when you know the invocation is the _last_ invocation of the function, iow\n> there is no later invocation that will be harmed by leftover flags).\n\nYes, you have reason, cleanup in get_octopus_merge_bases() should not be\noptional. Here is an updated patch that does not have this problem.\n\n(Also available in my working branch where I fixed up 9/10 and 10/10 to\nreflect this change.)\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.rc2.dirty\n"}]}