{"thread":{"id":"14048","subject":"[PATCH 04/11] Add new test to ensure git-merge handles pull.twohead and pull.octopus","startedAt":"2008-06-19T23:22:25Z","lastAt":"2008-06-21T09:45:55Z","messageCount":23,"participants":["Miklos Vajna","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"80404","messageId":"cover.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":null,"subject":"[PATCH 00/11] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:25Z","receivedAt":"2008-06-19T23:22:25Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"Hi,\n\nChanges since the previous series I sent out to the list:\n\n- get_octopus_merge_bases() now always does a cleanup\n\n- added a new test to make sure git-merge handles more than 25 refs\n\nAs usual, comments are welcome. :-)\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  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  Add new test to ensure git-merge handles more than 25 refs.\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                                      |   51 ++\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 t/t7602-merge-octopus-many.sh                 |   52 ++\n 19 files changed, 1529 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 create mode 100755 t/t7602-merge-octopus-many.sh\n"},{"id":"80414","messageId":"b9fc7f1cc6c36ba197ed2c0394a6b89ed33c4b96.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"cover.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 01/11] Move split_cmdline() to alias.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:26Z","receivedAt":"2008-06-19T23:22:26Z","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 81b7e17..26b2cad 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -827,5 +827,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":"80413","messageId":"45f73cd0355a5375dcd8a123dd0facf5cad99061.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"b9fc7f1cc6c36ba197ed2c0394a6b89ed33c4b96.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 02/11] Move commit_list_count() to commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:27Z","receivedAt":"2008-06-19T23:22:27Z","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\n"},{"id":"80407","messageId":"f41fbf4630e6341ae89eacad52c465cecc605e42.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"45f73cd0355a5375dcd8a123dd0facf5cad99061.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 03/11] Move parse-options's skip_prefix() to git-compat-util.h","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:28Z","receivedAt":"2008-06-19T23:22:28Z","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 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 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\n"},{"id":"80403","messageId":"702cfa09fdf6203e1199415af21de787c4a19ead.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"f41fbf4630e6341ae89eacad52c465cecc605e42.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 04/11] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:29Z","receivedAt":"2008-06-19T23:22:29Z","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\n"},{"id":"80405","messageId":"adba33a239d99b30fcccedc0638b060daec1016e.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"702cfa09fdf6203e1199415af21de787c4a19ead.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 05/11] parseopt: add a new PARSE_OPT_ARGV0_IS_AN_OPTION option","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:30Z","receivedAt":"2008-06-19T23:22:30Z","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\n"},{"id":"80406","messageId":"6323597c45d409498ce231b063737bb36c52ef9f.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"adba33a239d99b30fcccedc0638b060daec1016e.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 06/11] Move read_cache_unmerged() to read-cache.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:31Z","receivedAt":"2008-06-19T23:22:31Z","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 26b2cad..02fb7cb 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\n"},{"id":"80411","messageId":"172740d7c998269dd0c454a1365a429acdf7cae2.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"6323597c45d409498ce231b063737bb36c52ef9f.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 07/11] git-fmt-merge-msg: make it usable from other builtins","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:32Z","receivedAt":"2008-06-19T23:22:32Z","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\n"},{"id":"80410","messageId":"c83288c0995a7b4463b66e2f45d1a14839f0901c.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"172740d7c998269dd0c454a1365a429acdf7cae2.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 08/11] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:33Z","receivedAt":"2008-06-19T23:22:33Z","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 |   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":"80409","messageId":"0d95a099ad6e47c60894983ab7bfd94a3aeddce7.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"c83288c0995a7b4463b66e2f45d1a14839f0901c.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 09/11] Introduce filter_independent() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:34Z","receivedAt":"2008-06-19T23:22:34Z","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 6052ca3..0dbf4b7 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -705,3 +705,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);\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 dcec7fb..0aef7e4 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\n"},{"id":"80412","messageId":"a750360c92c30e05c686c454c0e5df2223b08f99.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"0d95a099ad6e47c60894983ab7bfd94a3aeddce7.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 10/11] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:35Z","receivedAt":"2008-06-19T23:22:35Z","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 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..cc6b88b\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);\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 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":"80408","messageId":"ac678179d9dcee24fbf5606f8650f2dd21549e40.1213917600.git.vmiklos@frugalware.org","threadId":"14048","inReplyTo":"a750360c92c30e05c686c454c0e5df2223b08f99.1213917600.git.vmiklos@frugalware.org","subject":"[PATCH 11/11] Add new test to ensure git-merge handles more than 25 refs.","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-19T23:22:36Z","receivedAt":"2008-06-19T23:22:36Z","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 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":"80433","messageId":"7vabhgq02p.fsf@gitster.siamese.dyndns.org","threadId":"14048","inReplyTo":"0d95a099ad6e47c60894983ab7bfd94a3aeddce7.1213917600.git.vmiklos@frugalware.org","subject":"Re: [PATCH 09/11] Introduce filter_independent() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-20T03:03:58Z","receivedAt":"2008-06-20T03:03:58Z","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> This is similar to git-show-branch --independent: It filters out commits\n> which are reachable from any other item from the input list.\n>\n> Signed-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>\n> diff --git a/commit.c b/commit.c\n> index 6052ca3..0dbf4b7 100644\n> --- a/commit.c\n> +++ b/commit.c\n> @@ -705,3 +705,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);\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\nHmm.  How well was this function tested?\n\nBecause RESULT is an implementation detail of merge_bases(), I do not\nthink we would want to expose it outside of it.\n\nMore worryingly, the flag is supposed to be cleaned from the objects after\nget_merge_bases() returns.  I am not sure what you'll learn by looking at\nthe flag here.\n"},{"id":"80432","messageId":"7v4p7oq029.fsf@gitster.siamese.dyndns.org","threadId":"14048","inReplyTo":"cover.1213917600.git.vmiklos@frugalware.org","subject":"Re: [PATCH 00/11] Build in merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-20T03:04:14Z","receivedAt":"2008-06-20T03:04:14Z","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> Changes since the previous series I sent out to the list:\n>\n> - get_octopus_merge_bases() now always does a cleanup\n>\n> - added a new test to make sure git-merge handles more than 25 refs\n>\n> As usual, comments are welcome. :-)\n\nThanks.\n\nI take that last line of comment to mean \"this is still RFC and not for\ninclusion yet\" ;-) I think the series is fine up to eighth patch (get\noctopus merge base).\n\nI'll queue the whole thing (still strictly as \"test merge\" basis) to 'pu';\nI've fixed 3 \"old-style C function definition\" issues you have in the\ntenth patch (build in merge), and I have some other issues with the ninth\none (filter independent).\n"},{"id":"80464","messageId":"alpine.LSU.1.00.0806201350000.32725@wbgn129.biozentrum.uni-wuerzburg.de","threadId":"14048","inReplyTo":"7vabhgq02p.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 09/11] Introduce filter_independent() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-20T11:53:06Z","receivedAt":"2008-06-20T11:53:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 19 Jun 2008, Junio C Hamano wrote:\n\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n> \n> > This is similar to git-show-branch --independent: It filters out commits\n> > which are reachable from any other item from the input list.\n> >\n> > Signed-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> >\n> > diff --git a/commit.c b/commit.c\n> > index 6052ca3..0dbf4b7 100644\n> > --- a/commit.c\n> > +++ b/commit.c\n> > @@ -705,3 +705,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);\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> \n> Hmm.  How well was this function tested?\n> \n> Because RESULT is an implementation detail of merge_bases(), I do not\n> think we would want to expose it outside of it.\n\nWell, filter_independent() lives in commit.c, too, so it is in the same \nscope as merge_branches().\n\n> More worryingly, the flag is supposed to be cleaned from the objects \n> after get_merge_bases() returns.  I am not sure what you'll learn by \n> looking at the flag here.\n\nThat is a valid point, methinks.  Probably the octopus_merge_base and the \ncleanup have to be decoupled here.  The cleanup should be nothing more \nthan calling clear_commit_marks() for every head.\n\nA test case would be desirable, but probably pretty complicated.  Hmm.  I \nlike tests when they are simple, because they are easy to verify by a \nhuman.  Miklos, any chance for such a test case?\n\nCiao,\nDscho\n"},{"id":"80466","messageId":"20080620120643.GX29404@genesis.frugalware.org","threadId":"14048","inReplyTo":"alpine.LSU.1.00.0806201350000.32725@wbgn129.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 09/11] Introduce filter_independent() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-20T12:06:43Z","receivedAt":"2008-06-20T12:06:43Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Fri, Jun 20, 2008 at 01:53:06PM +0200, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > More worryingly, the flag is supposed to be cleaned from the objects \n> > after get_merge_bases() returns.  I am not sure what you'll learn by \n> > looking at the flag here.\n> \n> That is a valid point, methinks.  Probably the octopus_merge_base and the \n> cleanup have to be decoupled here.  The cleanup should be nothing more \n> than calling clear_commit_marks() for every head.\n\nActually get_octopus_merge_bases() _needs_ to clean up the flags as it\ncalls get_merge_bases() multiple times and get_merge_bases() expects\nempty flags.\n\n> A test case would be desirable, but probably pretty complicated.  Hmm.  I \n> like tests when they are simple, because they are easy to verify by a \n> human.  Miklos, any chance for such a test case?\n\nSure, I'll write one.\n"},{"id":"80469","messageId":"alpine.DEB.1.00.0806201336470.6439@racer","threadId":"14048","inReplyTo":"20080620120643.GX29404@genesis.frugalware.org","subject":"Re: [PATCH 09/11] Introduce filter_independent() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-20T12:37:23Z","receivedAt":"2008-06-20T12:37:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 20 Jun 2008, Miklos Vajna wrote:\n\n> On Fri, Jun 20, 2008 at 01:53:06PM +0200, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > > More worryingly, the flag is supposed to be cleaned from the objects \n> > > after get_merge_bases() returns.  I am not sure what you'll learn by \n> > > looking at the flag here.\n> > \n> > That is a valid point, methinks.  Probably the octopus_merge_base and \n> > the cleanup have to be decoupled here.  The cleanup should be nothing \n> > more than calling clear_commit_marks() for every head.\n> \n> Actually get_octopus_merge_bases() _needs_ to clean up the flags as it \n> calls get_merge_bases() multiple times and get_merge_bases() expects \n> empty flags.\n\nOh darn, you are right.\n\n> > A test case would be desirable, but probably pretty complicated.  \n> > Hmm.  I like tests when they are simple, because they are easy to \n> > verify by a human.  Miklos, any chance for such a test case?\n> \n> Sure, I'll write one.\n\nThanks,\nDscho\n"},{"id":"80484","messageId":"20080620132506.GZ29404@genesis.frugalware.org","threadId":"14048","inReplyTo":"alpine.LSU.1.00.0806201350000.32725@wbgn129.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH 09/11] Introduce filter_independent() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-20T13:25:06Z","receivedAt":"2008-06-20T13:25:06Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Fri, Jun 20, 2008 at 01:53:06PM +0200, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> A test case would be desirable, but probably pretty complicated.  Hmm.  I \n> like tests when they are simple, because they are easy to verify by a \n> human.  Miklos, any chance for such a test case?\n\nNot that complicated:\n\nhttp://repo.or.cz/w/git/vmiklos.git?a=commit;h=c19b568714c7141e9f96ebea3990f7dae615223b\n\nAt the moment it passes on master and fails on mv/merge-in-c.\n"},{"id":"80522","messageId":"1214007784-4801-1-git-send-email-vmiklos@frugalware.org","threadId":"14048","inReplyTo":"7vabhgq02p.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Introduce filter_independent() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-21T00:23:04Z","receivedAt":"2008-06-21T00:23:04Z","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\nOn Thu, Jun 19, 2008 at 08:03:58PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > +   for (i = heads; i; i = i->next)\n> > +           if (!(i->item->object.flags & RESULT))\n> > +                   pptr = &commit_list_insert(i->item, pptr)->next;\n>\n> Hmm.  How well was this function tested?\n\nAs Dscho pointed out in an other mail there was no testcase for it, I\nwrote one and it pointed out what you said: it was buggy.\n\n> Because RESULT is an implementation detail of merge_bases(), I do not\n> think we would want to expose it outside of it.\n\nThe new version does not have this problem, either.\n\n> More worryingly, the flag is supposed to be cleaned from the objects\n> after get_merge_bases() returns.  I am not sure what you'll learn by\n> looking at the flag here.\n\nYes, you are right. In fact the idea to use get_octopus_merge_bases()\nwas wrong, since it does not do what we need.\n\nHere is an example:\n\n\tA - B\n\t  \\ C\n\t  \\ D\n\t  \\ E - F\n\nIn this case get_octopus_merge_bases() will return only A, which is what\nit should do, but we need E in filter_indepenent() as well.\n\nBelow is what I pushed to my working branch.\n\nThis version now passes the test that was failed for the previous one.\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 6052ca3..17e18d7 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -705,3 +705,35 @@ 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 *b, *i, *j, *k, *bases = NULL, *ret = NULL;\n+\tstruct commit_list **pptr = &ret;\n+\n+\tcommit_list_insert(lookup_commit(head), &heads);\n+\n+\tfor (i = heads; i; i = i->next) {\n+\t\tfor (j = heads; j; j = j->next) {\n+\t\t\tif (i == j)\n+\t\t\t\tcontinue;\n+\t\t\tb = get_merge_bases(i->item, j->item, 1);\n+\t\t\tfor (k = b; k; k = k->next)\n+\t\t\t\tcommit_list_insert(k->item, &bases);\n+\t\t}\n+\t}\n+\n+\tfor (i = heads; i; i = i->next) {\n+\t\tint found = 0;\n+\t\tfor (b = bases; b; b = b->next) {\n+\t\t\tif (!hashcmp(i->item->object.sha1, b->item->object.sha1)) {\n+\t\t\t\tfound = 1;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (!found)\n+\t\t\tpptr = &commit_list_insert(i->item, pptr)->next;\n+\t}\n+\treturn ret;\n+}\ndiff --git a/commit.h b/commit.h\nindex dcec7fb..0aef7e4 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\n"},{"id":"80523","messageId":"20080621003243.GF29404@genesis.frugalware.org","threadId":"14048","inReplyTo":"7v4p7oq029.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 00/11] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-21T00:32:43Z","receivedAt":"2008-06-21T00:32:43Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Thu, Jun 19, 2008 at 08:04:14PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > As usual, comments are welcome. :-)\n> \n> Thanks.\n> \n> I take that last line of comment to mean \"this is still RFC and not for\n> inclusion yet\" ;-) I think the series is fine up to eighth patch (get\n> octopus merge base).\n\nIn fact only the version that had \"WIP\" in the subject was sent not for\ninclusion. ;-)\n\n> I'll queue the whole thing (still strictly as \"test merge\" basis) to 'pu';\n> I've fixed 3 \"old-style C function definition\" issues you have in the\n> tenth patch (build in merge), and I have some other issues with the ninth\n> one (filter independent).\n\nThanks. I'm using the kernel's scripts/checkpatch.pl, but it does not\nhave such a check, and I missed it.\n\nCurrently I have 12 patches in my branch; if there will be no objections\nto the new \"filter independent\" one, then I will resend the series\nexcluding the first 8 to avoid unnecessary traffic, if that is OK to\nyou.\n"},{"id":"80536","messageId":"7vr6arazp9.fsf@gitster.siamese.dyndns.org","threadId":"14048","inReplyTo":"1214007784-4801-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Introduce filter_independent() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-21T09:45:38Z","receivedAt":"2008-06-21T09:45:38Z","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> +struct commit_list *filter_independent(unsigned char *head,\n> +\tstruct commit_list *heads)\n> +{\n> +\tstruct commit_list *b, *i, *j, *k, *bases = NULL, *ret = NULL;\n> +\tstruct commit_list **pptr = &ret;\n> +\n> +\tcommit_list_insert(lookup_commit(head), &heads);\n\nIsn't the special casing of head making this function less easier to reuse\nin other contexts?  \"show-branch --independent\" is about getting N commits\nand removing commits from that set that can be reachable from another\ncommit, so there is no need nor reason to treat one \"head\" in any special\nway.\n\n> +\tfor (i = heads; i; i = i->next) {\n> +\t\tfor (j = heads; j; j = j->next) {\n> +\t\t\tif (i == j)\n> +\t\t\t\tcontinue;\n> +\t\t\tb = get_merge_bases(i->item, j->item, 1);\n> +\t\t\tfor (k = b; k; k = k->next)\n> +\t\t\t\tcommit_list_insert(k->item, &bases);\n> +\t\t}\n> +\t}\n\nYou run (N-1)*N merge-base computation to get all pairwise merge-bases\nhere.  As merge-base(A,B) == merge-base(B,A), this is computing the same\nthing twice.\n\nIsn't your \"b\" leaking?\n\n> +\tfor (i = heads; i; i = i->next) {\n> +\t\tint found = 0;\n> +\t\tfor (b = bases; b; b = b->next) {\n> +\t\t\tif (!hashcmp(i->item->object.sha1, b->item->object.sha1)) {\n> +\t\t\t\tfound = 1;\n\nThen you see if the given heads exactly match one of the merge bases you\nfound earlier.  But does this have to be in a separate pass?\n\nIsn't your \"bases\" list leaking?\n\nEven though you may be able to reduce more than 25 heads, you run N^2\nmerge base traversals, which means 625 merge base traversals for 25 heads;\nshow-branch engine can do the same thing with a single traversal.\n\nCan't we do better than O(N^2)?\n\nLet's step back a bit and think.  You have N commits (stop thinking about\n\"my head and N other heads\" like your function signature suggests).  For\neach one, you would want to see if it is reachable from any of the other\n(N-1) commits, and if so, you would exclude it from the resulting set.\nAnd you do that for all N commits and you are done.  You can relatively\neasily do this with an O(N) traversals.\n\nNow, if you have one commit and other (N-1) commits, is there a way to\nefficiently figure out if that one commit is reachable from any of the\nother (N-1) commits?\n\nIf there were a merge of these other (N-1) commits, and if you compute a\nmerge base between that merge commit and the one commit you are looking\nat, what would you get?  Yes, you will get your commit back if and only if\nit is reachable from some of these (N-1) commits.\n\nIf you recall the merge-base-many patch we discussed earlier, that is\nexactly what it computes, isn't it?\n"},{"id":"80537","messageId":"7vlk0zazox.fsf@gitster.siamese.dyndns.org","threadId":"14048","inReplyTo":"1214007784-4801-1-git-send-email-vmiklos@frugalware.org","subject":"[PATCH 1/2] Introduce get_merge_bases_many()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-21T09:45:50Z","receivedAt":"2008-06-21T09:45:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This 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 e2d8624..4ee234d 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -525,26 +525,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@@ -592,21 +600,26 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n \treturn result;\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@@ -624,12 +637,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@@ -651,6 +665,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.6.gd3e97\n"},{"id":"80538","messageId":"7vfxr7azos.fsf@gitster.siamese.dyndns.org","threadId":"14048","inReplyTo":"1214007784-4801-1-git-send-email-vmiklos@frugalware.org","subject":"[PATCH 2/2] Introduce reduce_heads()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-21T09:45:55Z","receivedAt":"2008-06-21T09:45:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"The 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 * The important part of this patch is the addition to commit.c to show\n   how I would write your filter_independent().  The new command is not\n   essential, but is included as a demonstration.\n\n Makefile               |    1 +\n builtin-reduce-heads.c |   44 ++++++++++++++++++++++++++++++++++++++++++++\n builtin.h              |    1 +\n commit.c               |   45 +++++++++++++++++++++++++++++++++++++++++++++\n commit.h               |    2 ++\n git.c                  |    1 +\n 6 files changed, 94 insertions(+), 0 deletions(-)\n create mode 100644 builtin-reduce-heads.c\n\ndiff --git a/Makefile b/Makefile\nindex b003e3e..d660756 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -522,6 +522,7 @@ BUILTIN_OBJS += builtin-pack-refs.o\n BUILTIN_OBJS += builtin-prune-packed.o\n BUILTIN_OBJS += builtin-prune.o\n BUILTIN_OBJS += builtin-push.o\n+BUILTIN_OBJS += builtin-reduce-heads.o\n BUILTIN_OBJS += builtin-read-tree.o\n BUILTIN_OBJS += builtin-reflog.o\n BUILTIN_OBJS += builtin-remote.o\ndiff --git a/builtin-reduce-heads.c b/builtin-reduce-heads.c\nnew file mode 100644\nindex 0000000..ff6178d\n--- /dev/null\n+++ b/builtin-reduce-heads.c\n@@ -0,0 +1,44 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"strbuf.h\"\n+\n+static void show_commit_list(struct commit_list *list)\n+{\n+\twhile (list) {\n+\t\tstruct strbuf it;\n+\t\tstruct commit *commit = list->item;\n+\n+\t\tlist = list->next;\n+\t\tstrbuf_init(&it, 128);\n+\t\tparse_commit(commit);\n+\t\tpretty_print_commit(CMIT_FMT_ONELINE, commit, &it,\n+\t\t\t\t    0, NULL, NULL, 0, 0);\n+\t\tputs(it.buf);\n+\t\tstrbuf_release(&it);\n+\t}\n+}\n+\n+int cmd_reduce_heads(int ac, const char **av, const char *prefix)\n+{\n+\tstruct commit_list *list = NULL, **tail = &list;\n+\tint i;\n+\n+\tfor (i = 1; i < ac; i++) {\n+\t\tstruct commit *commit;\n+\t\tunsigned char sha1[20];\n+\n+\t\tif (get_sha1(av[i], sha1))\n+\t\t\tdie(\"'%s' is not a valid ref.\", av[i]);\n+\t\tcommit = lookup_commit_reference(sha1);\n+\t\tif (!commit)\n+\t\t\tdie(\"cannot find commit %s\", av[i]);\n+\t\ttail = &commit_list_insert(commit, tail)->next;\n+\t}\n+\n+\tshow_commit_list(list);\n+\tputchar('\\n');\n+\n+\tlist = reduce_heads(list);\n+\tshow_commit_list(list);\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex b460b2d..b6fb163 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -72,6 +72,7 @@ extern int cmd_read_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_reflog(int argc, const char **argv, const char *prefix);\n extern int cmd_remote(int argc, const char **argv, const char *prefix);\n extern int cmd_config(int argc, const char **argv, const char *prefix);\n+extern int cmd_reduce_heads(int argc, const char **argv, const char *prefix);\n extern int cmd_rerere(int argc, const char **argv, const char *prefix);\n extern int cmd_reset(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\ndiff --git a/commit.c b/commit.c\nindex 4ee234d..0fc4acb 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -690,3 +690,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 2d94d41..ec95102 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -138,4 +138,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 */\ndiff --git a/git.c b/git.c\nindex 59f0fcc..23b690d 100644\n--- a/git.c\n+++ b/git.c\n@@ -338,6 +338,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n \t\t{ \"push\", cmd_push, RUN_SETUP },\n \t\t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n+\t\t{ \"reduce-heads\", cmd_reduce_heads, RUN_SETUP },\n \t\t{ \"reflog\", cmd_reflog, RUN_SETUP },\n \t\t{ \"remote\", cmd_remote, RUN_SETUP },\n \t\t{ \"repo-config\", cmd_config },\n-- \n1.5.6.6.gd3e97\n"}]}