{"thread":{"id":"13817","subject":"[PATCH 00/10] Build in merge","startedAt":"2008-06-05T20:44:26Z","lastAt":"2008-06-10T02:43:51Z","messageCount":45,"participants":["Miklos Vajna","Junio C Hamano","Johannes Schindelin","Andreas Ericsson"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"78808","messageId":"cover.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":null,"subject":"[PATCH 00/10] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:26Z","receivedAt":"2008-06-05T20:44:26Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This series is a rewrite of git-merge in C.\n\nI already sent a WIP version of this series, it already worked at that\ntime, but I had numerous internal TODOs in the code.\n\nI also tried to use the internal API wherever it was possible to avoid\nthe expensive run_command() calls.\n\nAt the moment I am not aware of any bugs in ugly parts in the code, so I\nwould appreciate if I could get some comments (positive or negative) on\nit.\n\nAnd yes, I'm aware that it's already late for 1.5.6, I'm sending it out\nso that I can work on issues pointed out by others in the meantime.\n\nThanks.\n\nMiklos Vajna (10):\n  Move split_cmdline() to alias.c\n  Move commit_list_count() to commit.c\n  Move builtin-remote'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 useable from other builtins\n  Introduce commit_list_append() in commit.c\n  Introduce get_octopus_merge_bases() in commit.c\n  Build in merge\n\n Makefile                                      |    2 +-\n alias.c                                       |   54 ++\n builtin-fmt-merge-msg.c                       |  157 ++--\n builtin-merge-recursive.c                     |    8 -\n builtin-merge.c                               | 1147 +++++++++++++++++++++++++\n builtin-read-tree.c                           |   24 -\n builtin-remote.c                              |    6 -\n builtin.h                                     |    4 +\n cache.h                                       |    3 +\n commit.c                                      |   59 ++\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                               |   19 +-\n parse-options.h                               |    1 +\n read-cache.c                                  |   24 +\n t/t7601-merge-pull-config.sh                  |   57 ++\n 18 files changed, 1460 insertions(+), 168 deletions(-)\n create mode 100644 builtin-merge.c\n rename git-merge.sh => contrib/examples/git-merge.sh (100%)\n create mode 100755 t/t7601-merge-pull-config.sh\n"},{"id":"78813","messageId":"0a2c2130f9fd87e98192ab0fe0d23e16c902997c.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"cover.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 01/10] Move split_cmdline() to alias.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:27Z","receivedAt":"2008-06-05T20:44:27Z","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 092a997..e342ad3 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -831,5 +831,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 272bf03..9935069 100644\n--- a/git.c\n+++ b/git.c\n@@ -87,59 +87,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.rc0.dirty\n"},{"id":"78818","messageId":"9867fa302ce1c28f4bd8534a70bda19786c75971.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"0a2c2130f9fd87e98192ab0fe0d23e16c902997c.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 02/10] Move commit_list_count() to commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:28Z","receivedAt":"2008-06-05T20:44:28Z","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 362c290..0afd4a4 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 94d5b3d..b45ec9b 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -331,6 +331,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.rc0.dirty\n"},{"id":"78814","messageId":"5aca216074b88d68f97b8223ebf6272dfe6bddeb.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"9867fa302ce1c28f4bd8534a70bda19786c75971.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 03/10] Move builtin-remote's skip_prefix() to git-compat-util.h","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:29Z","receivedAt":"2008-06-05T20:44:29Z","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 builtin-remote's one to git-compat-util.h and\nlet parse-options use it as well.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n builtin-remote.c  |    6 ------\n git-compat-util.h |    6 ++++++\n parse-options.c   |   14 ++++----------\n 3 files changed, 10 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex c49f00f..9c25018 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;\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 01c4045..af515d4 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 *name, const char *prefix)\n+{\n+\treturn !name ? \"\" :\n+\t\tprefixcmp(name, prefix) ?  name : name + strlen(prefix);\n+}\n+\n #ifdef NO_MMAP\n \n #ifndef PROT_READ\ndiff --git a/parse-options.c b/parse-options.c\nindex acf3fe3..aa164d0 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@@ -161,7 +155,7 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,\n \n \t\trest = skip_prefix(arg, options->long_name);\n \t\tif (options->type == OPTION_ARGUMENT) {\n-\t\t\tif (!rest)\n+\t\t\tif (!strcmp(rest, arg))\n \t\t\t\tcontinue;\n \t\t\tif (*rest == '=')\n \t\t\t\treturn opterror(options, \"takes no value\", flags);\n@@ -170,7 +164,7 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,\n \t\t\tp->out[p->cpidx++] = arg - 2;\n \t\t\treturn 0;\n \t\t}\n-\t\tif (!rest) {\n+\t\tif (!strcmp(rest, arg)) {\n \t\t\t/* abbreviated? */\n \t\t\tif (!strncmp(options->long_name, arg, arg_end - arg)) {\n is_abbreviated:\n@@ -201,9 +195,9 @@ is_abbreviated:\n \t\t\tflags |= OPT_UNSET;\n \t\t\trest = skip_prefix(arg + 3, options->long_name);\n \t\t\t/* abbreviated and negated? */\n-\t\t\tif (!rest && !prefixcmp(options->long_name, arg + 3))\n+\t\t\tif (!strcmp(rest, arg + 3) && !prefixcmp(options->long_name, arg + 3))\n \t\t\t\tgoto is_abbreviated;\n-\t\t\tif (!rest)\n+\t\t\tif (!strcmp(rest, arg + 3))\n \t\t\t\tcontinue;\n \t\t}\n \t\tif (*rest) {\n-- \n1.5.6.rc0.dirty\n"},{"id":"78810","messageId":"3168647573b1325f47ab16f9ee3cae5abaaee473.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"5aca216074b88d68f97b8223ebf6272dfe6bddeb.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 04/10] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:30Z","receivedAt":"2008-06-05T20:44:30Z","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 |   57 ++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 57 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..cc595ac\n--- /dev/null\n+++ b/t/t7601-merge-pull-config.sh\n@@ -0,0 +1,57 @@\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+\tgit merge c2 &&\n+\ttest -e c1.c &&\n+\ttest -e 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 -e c1.c &&\n+\t! test -e 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+\t! git merge c2 c3\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+'\n+\n+test_done\n-- \n1.5.6.rc0.dirty\n"},{"id":"78815","messageId":"01dd116d05eedba51578935e39f679a8747380d6.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"3168647573b1325f47ab16f9ee3cae5abaaee473.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 05/10] parseopt: add a new PARSE_OPT_ARGV0_IS_AN_OPTION option","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:31Z","receivedAt":"2008-06-05T20:44:31Z","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 aa164d0..4d4f302 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.rc0.dirty\n"},{"id":"78809","messageId":"514d4184569ab033cad97be9afbd88c767bfb484.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"01dd116d05eedba51578935e39f679a8747380d6.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 06/10] Move read_cache_unmerged() to read-cache.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:32Z","receivedAt":"2008-06-05T20:44:32Z","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        |   24 ++++++++++++++++++++++++\n 3 files changed, 26 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 e342ad3..df0ac56 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..197160c 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1394,3 +1394,27 @@ int write_index(const struct index_state *istate, int newfd)\n \t}\n \treturn ce_flush(&c, newfd);\n }\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.rc0.dirty\n"},{"id":"78811","messageId":"2e4b20178405cf993ce9e0f1ffe4ac402a96fd03.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"514d4184569ab033cad97be9afbd88c767bfb484.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 07/10] git-fmt-merge-msg: make it useable from other builtins","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:33Z","receivedAt":"2008-06-05T20:44:33Z","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..5ec7195 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_addf(out, \"%s\", 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 8bda111..9b928e2 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@@ -10,6 +11,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.rc0.dirty\n"},{"id":"78817","messageId":"cbafb7e632b176658fe84e1eb9926f0a8d3c96b4.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"2e4b20178405cf993ce9e0f1ffe4ac402a96fd03.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 08/10] Introduce commit_list_append() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:34Z","receivedAt":"2008-06-05T20:44:34Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This is like commit_list_insert() but it appends the new commit to the\nend of the list, rather than insert to the start of it.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n commit.c |   19 +++++++++++++++++++\n commit.h |    1 +\n 2 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex b45ec9b..6ba5acb 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -331,6 +331,25 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *\n \treturn new_list;\n }\n \n+struct commit_list *commit_list_append(struct commit *item,\n+\tstruct commit_list **list_p)\n+{\n+\tstruct commit_list *i, *prev = NULL, *list = *list_p;\n+\tstruct commit_list *new_list = xmalloc(sizeof(struct commit_list));\n+\n+\tnew_list->item = item;\n+\tnew_list->next = NULL;\n+\n+\tif (!list)\n+\t\t*list_p = new_list;\n+\telse {\n+\t\tfor (i = list; i; i = i->next)\n+\t\t\tprev = i;\n+\t\tprev->next = new_list;\n+\t}\n+\treturn list;\n+}\n+\n unsigned commit_list_count(const struct commit_list *l)\n {\n \tunsigned c = 0;\ndiff --git a/commit.h b/commit.h\nindex 7f8c5ee..5d9ac43 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+struct commit_list *commit_list_append(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-- \n1.5.6.rc0.dirty\n"},{"id":"78816","messageId":"e13c0c2e9c8b0ebef84ac062bf05c05f729b3b20.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"cbafb7e632b176658fe84e1eb9926f0a8d3c96b4.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:35Z","receivedAt":"2008-06-05T20:44:35Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"This is like get_merge_bases() but it works for multiple heads, like\nshow-branch --merge-base.\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n commit.c |   32 ++++++++++++++++++++++++++++++++\n commit.h |    1 +\n 2 files changed, 33 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 6ba5acb..c09b305 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -625,6 +625,38 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n \treturn result;\n }\n \n+struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n+{\n+\tstruct commit_list *i, *j, *k, *ret = NULL;\n+\n+\tfor (i = in; i; i = i->next) {\n+\t\tif (!ret)\n+\t\t\tcommit_list_append(i->item, &ret);\n+\t\telse {\n+\t\t\tstruct commit_list *new = NULL, *end = NULL;\n+\n+\t\t\tfor (j = ret; j; j = j->next) {\n+\t\t\t\tstruct commit_list *bases;\n+\t\t\t\tbases = get_merge_bases(i->item, j->item, cleanup);\n+\t\t\t\t/*\n+\t\t\t\t * Now we just append bases to new, but\n+\t\t\t\t * calling commit_list_append() for each\n+\t\t\t\t * item would be expensive, so do it by\n+\t\t\t\t * hand.\n+\t\t\t\t */\n+\t\t\t\tif (!new)\n+\t\t\t\t\tnew = bases;\n+\t\t\t\telse\n+\t\t\t\t\tend->next = bases;\n+\t\t\t\tfor (k = bases; k; k = k->next)\n+\t\t\t\t\tend = k;\n+\t\t\t}\n+\t\t\tret = new;\n+\t\t}\n+\t}\n+\treturn ret;\n+}\n+\n struct commit_list *get_merge_bases(struct commit *one,\n \t\t\t\t\tstruct commit *two, int cleanup)\n {\ndiff --git a/commit.h b/commit.h\nindex 5d9ac43..201782d 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -122,6 +122,7 @@ int read_graft_file(const char *graft_file);\n struct commit_graft *lookup_commit_graft(const unsigned char *sha1);\n \n extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);\n+extern struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup);\n \n extern int register_shallow(const unsigned char *sha1);\n extern int unregister_shallow(const unsigned char *sha1);\n-- \n1.5.6.rc0.dirty\n"},{"id":"78812","messageId":"23e2073d4792cc0b54e7a7e4f3167c85a716e40c.1212698317.git.vmiklos@frugalware.org","threadId":"13817","inReplyTo":"e13c0c2e9c8b0ebef84ac062bf05c05f729b3b20.1212698317.git.vmiklos@frugalware.org","subject":"[PATCH 10/10] Build in merge","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-05T20:44:36Z","receivedAt":"2008-06-05T20:44:36Z","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                               | 1147 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-merge.sh => contrib/examples/git-merge.sh |    0 \n git.c                                         |    1 +\n 5 files changed, 1150 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 cce5a6e..748a2ae 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..9ebd990\n--- /dev/null\n+++ b/builtin-merge.c\n@@ -0,0 +1,1147 @@\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+static int commit_list_has_commit(struct commit *commit,\n+\tstruct commit_list *list)\n+{\n+\tstruct commit_list *i;\n+\n+\tfor (i = list; i; i = i->next) {\n+\t\tif (!hashcmp(commit->object.sha1, i->item->object.sha1))\n+\t\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+static struct commit_list *filter_independent(unsigned char *head,\n+\tstruct commit_list *heads)\n+{\n+\tstruct commit_list *i, *bases, *ret = NULL;\n+\n+\tcommit_list_insert(lookup_commit(head), &heads);\n+\n+\tbases = get_octopus_merge_bases(heads, 1);\n+\n+\tfor (i = heads; i; i = i->next)\n+\t\tif (!commit_list_has_commit(i->item, bases))\n+\t\t\tcommit_list_append(i->item, &ret);\n+\treturn ret;\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+\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\tbranch = skip_prefix(branch, \"refs/heads/\");\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\tcommit_list_append(lookup_commit(o->sha1), &remoteheads);\n+\n+\t\tstrbuf_addf(&buf, \"GITHEAD_%s\", sha1_to_hex(o->sha1));\n+\t\tsetenv(buf.buf, argv[i], 1);\n+\t\tstrbuf_reset(&buf);\n+\t}\n+\n+\tif (!use_strategies.nr) {\n+\t\tif (!remoteheads->next)\n+\t\t\tadd_strategies(pull_twohead, DEFAULT_TWOHEAD);\n+\t\telse\n+\t\t\tadd_strategies(pull_octopus, DEFAULT_OCTOPUS);\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tif ((unsigned int)use_strategies.items[i].util &\n+\t\t\tNO_FAST_FORWARD)\n+\t\t\tallow_fast_forward = 0;\n+\t\tif ((unsigned int)use_strategies.items[i].util & NO_TRIVIAL)\n+\t\t\tallow_trivial = 0;\n+\t}\n+\n+\tif (!remoteheads->next)\n+\t\tcommon = get_merge_bases(lookup_commit(head),\n+\t\t\t\tremoteheads->item, 1);\n+\telse {\n+\t\tstruct commit_list *list = remoteheads;\n+\t\tcommit_list_insert(lookup_commit(head), &list);\n+\t\tcommon = get_octopus_merge_bases(list, 1);\n+\t}\n+\n+\tupdate_ref(\"updating ORIG_HEAD\", \"ORIG_HEAD\", head, NULL, 0,\n+\t\tDIE_ON_ERR);\n+\n+\tif (!common)\n+\t\t; /* No common ancestors found. We need a real merge. */\n+\telse if (!remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1,\n+\t\tremoteheads->item->object.sha1)) {\n+\t\t/*\n+\t\t * If head can reach all the merge then we are up to\n+\t\t * date.\n+\t\t */\n+\t\tfinish_up_to_date(\"Already up-to-date.\");\n+\t\treturn 0;\n+\t} else if (allow_fast_forward && !remoteheads->next &&\n+\t\t!hashcmp(common->item->object.sha1, head)) {\n+\t\t/* Again the most common case of merging one remote. */\n+\t\tstruct strbuf msg;\n+\t\tstruct object *o;\n+\n+\t\tprintf(\"Updating %s..%s\\n\",\n+\t\t\tfind_unique_abbrev(head, DEFAULT_ABBREV),\n+\t\t\tfind_unique_abbrev(remoteheads->item->object.sha1,\n+\t\t\tDEFAULT_ABBREV));\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tstrbuf_init(&msg, 0);\n+\t\tstrbuf_addstr(&msg, \"Fast forward\");\n+\t\tif (have_message)\n+\t\t\tstrbuf_addstr(&msg,\n+\t\t\t\t\" (no commit created; -m option ignored)\");\n+\t\to = peel_to_type(sha1_to_hex(remoteheads->item->object.sha1),\n+\t\t\t0, NULL, OBJ_COMMIT);\n+\t\tif (!o)\n+\t\t\treturn 0;\n+\n+\t\tif (merge_one_remote(head, remoteheads->item->object.sha1))\n+\t\t\treturn 0;\n+\n+\t\tfinish(o->sha1, msg.buf);\n+\t\tdropsave();\n+\t\treturn 0;\n+\t} else if (!remoteheads->next && common->next)\n+\t\t;\n+\t\t/*\n+\t\t * We are not doing octopus and not fast forward.  Need\n+\t\t * a real merge.\n+\t\t */\n+\telse if (!remoteheads->next && option_commit) {\n+\t\t/*\n+\t\t * We are not doing octopus and not fast forward.  Need\n+\t\t * a real merge.\n+\t\t */\n+\t\trefresh_cache(REFRESH_QUIET);\n+\t\tif (allow_trivial) {\n+\t\t\t/* See if it is really trivial. */\n+\t\t\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\t\t\tprintf(\"Trying really trivial in-index merge...\\n\");\n+\t\t\tif (!read_tree_trivial(common->item->object.sha1,\n+\t\t\t\t\thead, remoteheads->item->object.sha1)) {\n+\t\t\t\tunsigned char result_tree[20],\n+\t\t\t\t\tresult_commit[20];\n+\t\t\t\tstruct commit_list parent;\n+\n+\t\t\t\twrite_tree_trivial(result_tree);\n+\t\t\t\tprintf(\"Wonderful.\\n\");\n+\t\t\t\tparent.item = remoteheads->item;\n+\t\t\t\tparent.next = NULL;\n+\t\t\t\tcommit_tree_trivial(merge_msg.buf,\n+\t\t\t\t\tresult_tree, &parent,\n+\t\t\t\t\tresult_commit);\n+\t\t\t\tfinish(result_commit, \"In-index merge\");\n+\t\t\t\tdropsave();\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tprintf(\"Nope.\\n\");\n+\t\t}\n+\t} else {\n+\t\t/*\n+\t\t * An octopus.  If we can reach all the remote we are up\n+\t\t * to date.\n+\t\t */\n+\t\tint up_to_date = 1;\n+\t\tstruct commit_list *j;\n+\n+\t\tfor (j = remoteheads; j; j = j->next) {\n+\t\t\tstruct commit_list *common_one;\n+\n+\t\t\tcommon_one = get_merge_bases(lookup_commit(head),\n+\t\t\t\tj->item, 1);\n+\t\t\tif (hashcmp(common_one->item->object.sha1,\n+\t\t\t\tj->item->object.sha1)) {\n+\t\t\t\tup_to_date = 0;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (up_to_date) {\n+\t\t\tfinish_up_to_date(\"Already up-to-date. Yeeah!\");\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\n+\t/* We are going to make a new commit. */\n+\tgit_committer_info(IDENT_ERROR_ON_NO_NAME);\n+\n+\t/*\n+\t * At this point, we need a real merge.  No matter what strategy\n+\t * we use, it would operate on the index, possibly affecting the\n+\t * working tree, and when resolved cleanly, have the desired\n+\t * tree in the index -- this means that the index must be in\n+\t * sync with the head commit.  The strategies are responsible\n+\t * to ensure this.\n+\t */\n+\tif (use_strategies.nr != 1) {\n+\t\t/*\n+\t\t * Stash away the local changes so that we can try more\n+\t\t * than one.\n+\t\t */\n+\t\tsave_state();\n+\t\tsingle_strategy = 0;\n+\t} else {\n+\t\tunlink(git_path(\"MERGE_STASH\"));\n+\t\tsingle_strategy = 1;\n+\t}\n+\n+\tfor (i = 0; i < use_strategies.nr; i++) {\n+\t\tint ret;\n+\t\tif (i) {\n+\t\t\tprintf(\"Rewinding the tree to pristine...\\n\");\n+\t\t\trestore_state();\n+\t\t}\n+\t\tif (!single_strategy)\n+\t\t\tprintf(\"Trying merge strategy %s...\\n\",\n+\t\t\t\tuse_strategies.items[i].path);\n+\t\t/*\n+\t\t * Remember which strategy left the state in the working\n+\t\t * tree.\n+\t\t */\n+\t\twt_strategy = &use_strategies.items[i];\n+\n+\t\tret = try_merge_strategy(use_strategies.items[i].path,\n+\t\t\tcommon, &head_arg);\n+\t\tif (!option_commit && !ret) {\n+\t\t\tmerge_was_ok = 1;\n+\t\t\tret = 1;\n+\t\t}\n+\n+\t\tif (ret) {\n+\t\t\t/*\n+\t\t\t * The backend exits with 1 when conflicts are\n+\t\t\t * left to be resolved, with 2 when it does not\n+\t\t\t * handle the given merge at all.\n+\t\t\t */\n+\t\t\tif (ret == 1) {\n+\t\t\t\tint cnt = 0;\n+\t\t\t\tstruct rev_info rev;\n+\n+\t\t\t\tif (read_cache() < 0)\n+\t\t\t\t\tdie(\"failed to read the cache\");\n+\n+\t\t\t\t/* Check how many files differ. */\n+\t\t\t\tinit_revisions(&rev, \"\");\n+\t\t\t\tsetup_revisions(0, NULL, &rev, NULL);\n+\t\t\t\trev.diffopt.output_format |=\n+\t\t\t\t\tDIFF_FORMAT_CALLBACK;\n+\t\t\t\trev.diffopt.format_callback = count_diff_files;\n+\t\t\t\trev.diffopt.format_callback_data = &cnt;\n+\t\t\t\trun_diff_files(&rev, 0);\n+\n+\t\t\t\t/*\n+\t\t\t\t * Check how many unmerged entries are\n+\t\t\t\t * there.\n+\t\t\t\t */\n+\t\t\t\tcnt += count_unmerged_entries();\n+\n+\t\t\t\tif (best_cnt <= 0 || cnt <= best_cnt) {\n+\t\t\t\t\tbest_strategy =\n+\t\t\t\t\t\t&use_strategies.items[i];\n+\t\t\t\t\tbest_cnt = cnt;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\t/* Automerge succeeded. */\n+\t\twrite_tree_trivial(result_tree);\n+\t\tautomerge_was_ok = 1;\n+\t\tbreak;\n+\t}\n+\n+\t/*\n+\t * If we have a resulting tree, that means the strategy module\n+\t * auto resolved the merge cleanly.\n+\t */\n+\tif (automerge_was_ok) {\n+\t\tstruct commit_list *parents = NULL, *j;\n+\t\tunsigned char result_commit[20];\n+\n+\t\tfree_commit_list(common);\n+\t\tif (allow_fast_forward)\n+\t\t\tparents = filter_independent(head, remoteheads);\n+\t\telse {\n+\t\t\tcommit_list_append(lookup_commit(head), &parents);\n+\t\t\tfor (j = remoteheads; j; j = j->next)\n+\t\t\t\tcommit_list_append(j->item, &parents);\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 9b928e2..098eaa2 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -59,6 +59,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 9935069..7f69c3b 100644\n--- a/git.c\n+++ b/git.c\n@@ -268,6 +268,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.rc0.dirty\n"},{"id":"78829","messageId":"7vtzg7zew0.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"5aca216074b88d68f97b8223ebf6272dfe6bddeb.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 03/10] Move builtin-remote's skip_prefix() to git-compat-util.h","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-05T22:38:23Z","receivedAt":"2008-06-05T22:38:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Miklos Vajna <vmiklos@frugalware.org> writes:\n\n> diff --git a/builtin-remote.c b/builtin-remote.c\n> index c49f00f..9c25018 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> diff --git a/git-compat-util.h b/git-compat-util.h\n> index 01c4045..af515d4 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 *name, const char *prefix)\n> +{\n> +\treturn !name ? \"\" :\n> +\t\tprefixcmp(name, prefix) ?  name : name + strlen(prefix);\n> +}\n> +\n\nSomehow you seemed to have picked the one whose semantics is defined much\nless clearly.  For one thing, it takes more effort (and unnatural way to\ncheck) for the caller to detect the case where prefix did not match than\nthe one that returns NULL.  Worse, I think this one is less efficient,\ndoing strlen(prefix) twice.\n\n> diff --git a/parse-options.c b/parse-options.c\n> index acf3fe3..aa164d0 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> @@ -161,7 +155,7 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,\n>  \n>  \t\trest = skip_prefix(arg, options->long_name);\n>  \t\tif (options->type == OPTION_ARGUMENT) {\n> -\t\t\tif (!rest)\n> +\t\t\tif (!strcmp(rest, arg))\n\nUgh.\n\nAt least please have the courtesy of not making it more expensive than the\noriginal unnecessarily.  Isn't (rest == arg) enough here?\n\nStill, I think the one from builtin-remote.c you used is a much worse\nimplementation of the same thing between the two.  It was Ok while it was\na local scope hack only for builtin-remote.c's use, but a special calling\nconvention like \"if name is NULL return empty string\" should not be\npromoted to public utility library status without defending why it is\nalways a good idea to do so.\n"},{"id":"78832","messageId":"7vmylzzdyo.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"3168647573b1325f47ab16f9ee3cae5abaaee473.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 04/10] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-05T22:58:23Z","receivedAt":"2008-06-05T22:58:23Z","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> Test if the given strategies are used and test the case when multiple\n> strategies are configured using a space separated list.\n>\n> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n> ---\n>  t/t7601-merge-pull-config.sh |   57 ++++++++++++++++++++++++++++++++++++++++++\n>  1 files changed, 57 insertions(+), 0 deletions(-)\n>  create mode 100755 t/t7601-merge-pull-config.sh\n>\n> diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\n> new file mode 100755\n> index 0000000..cc595ac\n> --- /dev/null\n> +++ b/t/t7601-merge-pull-config.sh\n> @@ -0,0 +1,57 @@\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\ntest that c0 and c1 do and c2 and c3 do not exist here, as it is cheap,\nand otherwise you may end up chasing wild-goose when somebody breaks\ngit-reset.  No need to do so in later tests in this script, but it is a\ncheap protection for yourself from others' mistakes ;-).\n\n> +\tgit merge c2 &&\n> +\ttest -e c1.c &&\n> +\ttest -e c2.c\n> +'\n\nNobody runs V7 that lacked \"test -e\" to run these test scripts, but you\nexpect them to be regular files at this point of the test, so the correct\nway to spell these is with \"test -f\".\n\nIn general, you are better off training yourself to think if you can use\n\"test -f\" before blindly using \"test -e\".\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 -e c1.c &&\n> +\t! test -e 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> +\t! git merge c2 c3\n\nIs it because it should dump core, or is it because the command should\ndecline to work, gracefully failing with an error message and non-zero\nexit status?  Use \"test_must_fail\" to check for the latter.\n\nDon't you want to check how it fails and in what shape the command leaves\nthe work tree?  I am assuming that recursive sees more than one \"remote\"\nhead and declines to work without touching work tree nor the index, so if\nthat is what you expect, you should check for that.  Otherwise, a\nregression that loses local changes will go unnoticed.\n\n> +'\n> +\n> +test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '\n> +\tgit reset --hard c1 &&\n> +\tgit config pull.octopus \"recursive octopus\" &&\n> +\tgit merge c2 c3\n\nLikewise, don't you want to check the result of the merge?  Not just\n\"merge exited with 0\", but you would want to see that the HEAD has\nadvanced, it has the expected parents, there is no unexpected local\nchanges (because you did not have any when you started the merge), and it\nhas the expected tree contents.\n\n> +'\n> +\n> +test_done\n> -- \n> 1.5.6.rc0.dirty\n"},{"id":"78834","messageId":"7vhcc7zdm5.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"514d4184569ab033cad97be9afbd88c767bfb484.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 06/10] Move read_cache_unmerged() to read-cache.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-05T23:05:54Z","receivedAt":"2008-06-05T23:05:54Z","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> builtin-read-tree has a read_cache_unmerged() which is useful for other\n> builtins, for example builtin-merge uses it as well. Move it to\n> read-cache.c to avoid code duplication.\n\nLooks good, but as a public interface probably it needs a few lines of\ncomment in front of the function's definition to describe what it is used\nfor.  Perhaps like...\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"},{"id":"78838","messageId":"7vd4mvzdbz.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"2e4b20178405cf993ce9e0f1ffe4ac402a96fd03.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 07/10] git-fmt-merge-msg: make it useable from other builtins","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-05T23:12:00Z","receivedAt":"2008-06-05T23:12:00Z","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> Move all functionality (except config and option parsing) from\n> cmd_fmt_merge_msg() to fmt_merge_msg(), so that other builtins can use\n> it without a child process.\n>\n> All functions have been changed to use strbufs, and now only\n> cmd_fmt_merge_msg() reads directly from a file / writes anything to\n> stdout.\n>\n> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n\nThe word in the title should be \"usable\" (s/sea/sa/).\n\nOther than that, looks like a quite straightforward conversion.\n"},{"id":"78839","messageId":"7v8wxjzd41.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"cbafb7e632b176658fe84e1eb9926f0a8d3c96b4.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 08/10] Introduce commit_list_append() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-05T23:16:46Z","receivedAt":"2008-06-05T23:16:46Z","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 like commit_list_insert() but it appends the new commit to the\n> end of the list, rather than insert to the start of it.\n>\n> Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>\n> ---\n>  commit.c |   19 +++++++++++++++++++\n>  commit.h |    1 +\n>  2 files changed, 20 insertions(+), 0 deletions(-)\n>\n> diff --git a/commit.c b/commit.c\n> index b45ec9b..6ba5acb 100644\n> --- a/commit.c\n> +++ b/commit.c\n> @@ -331,6 +331,25 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list *\n>  \treturn new_list;\n>  }\n>  \n> +struct commit_list *commit_list_append(struct commit *item,\n> +\tstruct commit_list **list_p)\n> +{\n> +\tstruct commit_list *i, *prev = NULL, *list = *list_p;\n> +\tstruct commit_list *new_list = xmalloc(sizeof(struct commit_list));\n> +\n> +\tnew_list->item = item;\n> +\tnew_list->next = NULL;\n> +\n> +\tif (!list)\n> +\t\t*list_p = new_list;\n> +\telse {\n> +\t\tfor (i = list; i; i = i->next)\n> +\t\t\tprev = i;\n> +\t\tprev->next = new_list;\n> +\t}\n> +\treturn list;\n> +}\n\nDo you have a caller of this function that keeps a pointer to commit_list\nthat needs to be appended at the tail or inserted at the beginning\ndepending on the phase of the moon, or does the caller always append to\nthat list?\n"},{"id":"78858","messageId":"7vk5h3xlt9.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"e13c0c2e9c8b0ebef84ac062bf05c05f729b3b20.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-06T03:51:46Z","receivedAt":"2008-06-06T03:51:46Z","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 like get_merge_bases() but it works for multiple heads, like\n> show-branch --merge-base.\n\nIn what sense is this \"like show-branch --merge-base\"?\n\nThe only similarlity I can spot is that it can take more than two heads,\nbut what it computes and the way it computes it seem to be different.  It\ncertainly looks much less efficient as it does not walk the ancestry chain\nin one-go like show-branch does.\n\nI haven't looked at [10/10] so I do not know yet how you intend to use\nthis value, but I am not sure if it makes sense to use the computed value\nas the merge base when running an octopus.\n\nWhen you want to merge A, B and C, because internally we always do\npairwise, you would first compute:\n\n\tT(A,B) == merge_3way(mb(A,B), A, B)\n\nand then you would want to pick some merge base to merge that result with\nC:\n\n\tT(A,B,C) == merge_3way(???, T(A,B), C)\n\nI do not think using mb(mb(A,B),C) as ??? is necessary nor optimal.\n\n          o---o---A\n         /         .\n    ----1---2---B...T(A,B)\n             \\       .\n              o---C...T(A,B,C)\n\n\"1\" above is mb(A,B) (i.e. the merge base between A and B) that you would\nuse when you merge A and B to compute an internal merge result T(A,B).\nAnd \"1\" also is mb(mb(A,B),C).\n\nBut if you are doing 3-way merge to arrive at T(A,B,C) by merging T(A,B)\nand C, don't you want to be using \"2\" as the merge base, not \"1\"?\n\nThe big comment on $MRC at the end of git-merge-octopus is about this\nissue.  In earlier implementation, we used to use \"1\" to create T(A,B) and\nthen also used it to update the variable $MRC, which is used to compute\nthe merge base to use when the tentative tree T(A,B) is merged with the\nother remote in the next round.  It was a mistake.\n\nIdeally we should pick a better base between mb(A,C) and mb(B,C) (and if\nwe used mb(B,C) that's \"2\" and is a much better base than \"1\" when merging\nT(A,B) and C).  Using mb(mb(A,B),C) means we are guaranteed to pick the\nworst base for that last merge.\n"},{"id":"78868","messageId":"7vmylzw1l4.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"7vk5h3xlt9.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-06T05:53:59Z","receivedAt":"2008-06-06T05:53:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> When you want to merge A, B and C, because internally we always do\n> pairwise, you would first compute:\n>\n> \tT(A,B) == merge_3way(mb(A,B), A, B)\n>\n> and then you would want to pick some merge base to merge that result with\n> C:\n>\n> \tT(A,B,C) == merge_3way(???, T(A,B), C)\n>\n> I do not think using mb(mb(A,B),C) as ??? is necessary nor optimal.\n>\n>           o---o---A\n>          /         .\n>     ----1---2---B...T(A,B)\n>              \\       .\n>               o---C...T(A,B,C)\n>\n> \"1\" above is mb(A,B) (i.e. the merge base between A and B) that you would\n> use when you merge A and B to compute an internal merge result T(A,B).\n> And \"1\" also is mb(mb(A,B),C).\n>\n> But if you are doing 3-way merge to arrive at T(A,B,C) by merging T(A,B)\n> and C, don't you want to be using \"2\" as the merge base, not \"1\"?\n>\n> The big comment on $MRC at the end of git-merge-octopus is about this\n> issue.  In earlier implementation, we used to use \"1\" to create T(A,B) and\n> then also used it to update the variable $MRC, which is used to compute\n> the merge base to use when the tentative tree T(A,B) is merged with the\n> other remote in the next round.  It was a mistake.\n>\n> Ideally we should pick a better base between mb(A,C) and mb(B,C) (and if\n> we used mb(B,C) that's \"2\" and is a much better base than \"1\" when merging\n> T(A,B) and C).  Using mb(mb(A,B),C) means we are guaranteed to pick the\n> worst base for that last merge.\n\nSomething like this might be enough to get us started.  Instead of finding\nout the merge bases between two commits, \"one\" and \"two\", this would find\nmerge base between \"one\" and a commit that would be a merge across all the\n\"two\"s.  To apply it to the above example, you would give \"C\" as \"one\",\nand \"A\" and \"B\" as \"twos\" to it, to compute \"2\".\n\n commit.c |   32 ++++++++++++++++++++++----------\n 1 files changed, 22 insertions(+), 10 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 94d5b3d..816eed1 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -531,26 +531,33 @@ 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/* 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@@ -598,6 +605,11 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n \treturn result;\n }\n \n+static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n+{\n+\treturn merge_bases_many(one, 1, &two);\n+}\n+\n struct commit_list *get_merge_bases(struct commit *one,\n \t\t\t\t\tstruct commit *two, int cleanup)\n {\n"},{"id":"78896","messageId":"alpine.DEB.1.00.0806061316350.1783@racer","threadId":"13817","inReplyTo":"7vmylzw1l4.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-06T12:28:09Z","receivedAt":"2008-06-06T12:28:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 5 Jun 2008, Junio C Hamano wrote:\n\n> diff --git a/commit.c b/commit.c\n> index 94d5b3d..816eed1 100644\n> --- a/commit.c\n> +++ b/commit.c\n> @@ -531,26 +531,33 @@ 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\nClever, but for performance reasons I think it might be better to use a \ncommit list here.  This can be a local commit_list in the helper that \ncalls merge_bases_many() for the two-head case.\n\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/* 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\nDistracting curly brackets.\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\nAh, now that is clever: I thought we would get into a lot of problems \nbecause we would need a bit for every initial commit.  But what you are \nbasically doing is reusing PARENT2 for all the merge bases that have been \nfound for the heads up to, but not including, the current one.\n\nMaybe this should be documented.\n\nBut this is not yet enough, right?  You still have to have a loop\n\nmerge_bases <- calculate merge bases from the first two heads\n\nforeach head starting from the 3rd one\n\tmerge_bases <- calculate merge_bases_many(this_head, merge_bases)\n\nNo?\n\nAnd these merge_bases are commit_lists, therefore, as mentioned above, the \nsignature should not take an array, but the commit_lists.\n\nCiao,\nDscho\n"},{"id":"78898","messageId":"alpine.DEB.1.00.0806061305490.1783@racer","threadId":"13817","inReplyTo":"7vk5h3xlt9.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-06T12:30:34Z","receivedAt":"2008-06-06T12:30:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 5 Jun 2008, Junio C Hamano wrote:\n\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n> \n> > This is like get_merge_bases() but it works for multiple heads, like\n> > show-branch --merge-base.\n> \n> In what sense is this \"like show-branch --merge-base\"?\n> \n> The only similarlity I can spot is that it can take more than two heads, \n> but what it computes and the way it computes it seem to be different.  \n> It certainly looks much less efficient as it does not walk the ancestry \n> chain in one-go like show-branch does.\n\nAh, you are right.  I thought that in the typical case (where the initial \ncommits are independent), it will never traverse any commit twice.  I did \nnot really understand your example quickly enough, so I made up my own:\n\nA - B - C - D - E\n  \\           X\n    F - G - H   I\n      \\   X   \\\n        J   \\   K\n             \\\n              - L \n\nNow, let's get the merge bases with Miklos' algorithm for E, I, K and L.\n\nFirst it will find the merge base for E and I, which is D.  Then it \ncalculates the merge base between that merge base and the third head, K, \nwhich leads us all the way back to A.\n\nNow, calculating the merge base between that merge base and L will \ntraverse the commits F, G and H _again_.\n\nCiao,\nDscho\n"},{"id":"78901","messageId":"alpine.DEB.1.00.0806061331560.1783@racer","threadId":"13817","inReplyTo":"alpine.DEB.1.00.0806061316350.1783@racer","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-06T12:36:14Z","receivedAt":"2008-06-06T12:36:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 6 Jun 2008, Johannes Schindelin wrote:\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> Ah, now that is clever: I thought we would get into a lot of problems \n> because we would need a bit for every initial commit.  But what you are \n> basically doing is reusing PARENT2 for all the merge bases that have \n> been found for the heads up to, but not including, the current one.\n\nI spoke too soon.  This is not going to work easily: after doing this for \ntwo heads, the PARENT1 flag must be reset to PARENT2 before traversing for \na third head.\n\nOr am I missing something obvious?\n\nCiao,\nDscho\n"},{"id":"78921","messageId":"7vy75ivamo.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"alpine.DEB.1.00.0806061316350.1783@racer","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-06T15:36:15Z","receivedAt":"2008-06-06T15:36:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Clever, but for performance reasons I think it might be better to use a \n> commit list here.\n\nWhy?  We do not iterate this that many times, and you do not gain much\nfrom extra allocations of commit_list structure you would need to do.\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> Ah, now that is clever: I thought we would get into a lot of problems \n> because we would need a bit for every initial commit.\n\nThat's not coming from being _clever_, but from solving a different\nproblem than what you thought we are solving.  This is _not_ computing the\n\"base of all of them\" as \"git-show-branch --merge-base\" computes.\n\nInstead, this is \"compute '2' given C and A+B\", without actually having to\nhave a commit T(A,B), for a merge to create T(A,B,C).\n\n>           o---o---A\n>          /         .\n>     ----1---2---B...T(A,B)\n>              \\       .\n>               o---C...T(A,B,C)\n\nInstead of fabricating a commit T(A,B) and drop it as \"two\", you\ndrop \"A\" and \"B\" both marked as PARENT2 into the list and have the logic\ntraverse as before.\n\nThe purpose of this exercise is not doing the merge base computation for\nan octopus in one round.  In fact, you do _not_ want to compute it\nupfront.  Re-read the original message the above picture comes from to\nremind you of why (and the loop with a big comment at the bottom of\ngit-merge-octopus).  This is for a better computation of $MRC in each\nround there.\n.\n"},{"id":"78986","messageId":"1212795724-24749-1-git-send-email-vmiklos@frugalware.org","threadId":"13817","inReplyTo":"7vtzg7zew0.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Move parse-options's skip_prefix() to git-compat-util.h","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-06T23:42:04Z","receivedAt":"2008-06-06T23:42:04Z","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\nOn Thu, Jun 05, 2008 at 03:38:23PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Somehow you seemed to have picked the one whose semantics is defined\n> much\n> less clearly.  For one thing, it takes more effort (and unnatural way\n> to\n> check) for the caller to detect the case where prefix did not match\n> than\n> the one that returns NULL.  Worse, I think this one is less efficient,\n> doing strlen(prefix) twice.\n\nHere is what I pushed to my working branch.\n\n builtin-remote.c  |   39 ++++++++++++++++++++++++++-------------\n git-compat-util.h |    6 ++++++\n parse-options.c   |    6 ------\n 3 files changed, 32 insertions(+), 19 deletions(-)\n\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex c49f00f..2bf0593 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -29,12 +29,6 @@ static inline int postfixcmp(const char *string, const char *postfix)\n \treturn strcmp(string + len1 - len2, postfix);\n }\n \n-static inline const char *skip_prefix(const char *name, const char *prefix)\n-{\n-\treturn !name ? \"\" :\n-\t\tprefixcmp(name, prefix) ?  name : name + strlen(prefix);\n-}\n-\n static int opt_parse_track(const struct option *opt, const char *arg, int not)\n {\n \tstruct path_list *list = opt->value;\n@@ -182,12 +176,18 @@ static int config_read_branches(const char *key, const char *value, void *cb)\n \t\t\tinfo->remote = xstrdup(value);\n \t\t} else {\n \t\t\tchar *space = strchr(value, ' ');\n-\t\t\tvalue = skip_prefix(value, \"refs/heads/\");\n+\t\t\tconst char *ptr = skip_prefix(value, \"refs/heads/\");\n+\t\t\tif (ptr)\n+\t\t\t\tvalue = ptr;\n \t\t\twhile (space) {\n \t\t\t\tchar *merge;\n \t\t\t\tmerge = xstrndup(value, space - value);\n \t\t\t\tpath_list_append(merge, &info->merge);\n-\t\t\t\tvalue = skip_prefix(space + 1, \"refs/heads/\");\n+\t\t\t\tptr = skip_prefix(space + 1, \"refs/heads/\");\n+\t\t\t\tif (ptr)\n+\t\t\t\t\tvalue = ptr;\n+\t\t\t\telse\n+\t\t\t\t\tvalue = space + 1;\n \t\t\t\tspace = strchr(value, ' ');\n \t\t\t}\n \t\t\tpath_list_append(xstrdup(value), &info->merge);\n@@ -219,7 +219,12 @@ static int handle_one_branch(const char *refname,\n \trefspec.dst = (char *)refname;\n \tif (!remote_find_tracking(states->remote, &refspec)) {\n \t\tstruct path_list_item *item;\n-\t\tconst char *name = skip_prefix(refspec.src, \"refs/heads/\");\n+\t\tconst char *name, *ptr;\n+\t\tptr = skip_prefix(refspec.src, \"refs/heads/\");\n+\t\tif (ptr)\n+\t\t\tname = ptr;\n+\t\telse\n+\t\t\tname = refspec.src;\n \t\t/* symbolic refs pointing nowhere were handled already */\n \t\tif ((flags & REF_ISSYMREF) ||\n \t\t\t\tunsorted_path_list_has_path(&states->tracked,\n@@ -248,6 +253,7 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)\n \t\tstruct path_list *target = &states->tracked;\n \t\tunsigned char sha1[20];\n \t\tvoid *util = NULL;\n+\t\tconst char *ptr;\n \n \t\tif (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1))\n \t\t\ttarget = &states->new;\n@@ -256,8 +262,10 @@ static int get_ref_states(const struct ref *ref, struct ref_states *states)\n \t\t\tif (hashcmp(sha1, ref->new_sha1))\n \t\t\t\tutil = &states;\n \t\t}\n-\t\tpath_list_append(skip_prefix(ref->name, \"refs/heads/\"),\n-\t\t\t\ttarget)->util = util;\n+\t\tptr = skip_prefix(ref->name, \"refs/heads/\");\n+\t\tif (!ptr)\n+\t\t\tptr = ref->name;\n+\t\tpath_list_append(ptr, target)->util = util;\n \t}\n \tfree_refs(fetch_map);\n \n@@ -504,10 +512,15 @@ static int show_or_prune(int argc, const char **argv, int prune)\n \t\t\t\t\t\"es\" : \"\");\n \t\t\tfor (i = 0; i < states.remote->push_refspec_nr; i++) {\n \t\t\t\tstruct refspec *spec = states.remote->push + i;\n+\t\t\t\tconst char *p = \"\", *q = \"\";\n+\t\t\t\tif (spec->src)\n+\t\t\t\t\tp = skip_prefix(spec->src, \"refs/heads/\");\n+\t\t\t\tif (spec->dst)\n+\t\t\t\t\tq = skip_prefix(spec->dst, \"refs/heads/\");\n \t\t\t\tprintf(\" %s%s%s%s\", spec->force ? \"+\" : \"\",\n-\t\t\t\t\tskip_prefix(spec->src, \"refs/heads/\"),\n+\t\t\t\t\tp ? p : spec->src,\n \t\t\t\t\tspec->dst ? \":\" : \"\",\n-\t\t\t\t\tskip_prefix(spec->dst, \"refs/heads/\"));\n+\t\t\t\t\tq ? q : spec->dst);\n \t\t\t}\n \t\t\tprintf(\"\\n\");\n \t\t}\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 01c4045..67a0b81 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.rc0.dirty\n"},{"id":"78987","messageId":"20080606235239.GY29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7v8wxjzd41.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 08/10] Introduce commit_list_append() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-06T23:52:39Z","receivedAt":"2008-06-06T23:52:39Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Thu, Jun 05, 2008 at 04:16:46PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Do you have a caller of this function that keeps a pointer to commit_list\n> that needs to be appended at the tail or inserted at the beginning\n> depending on the phase of the moon, or does the caller always append to\n> that list?\n\nThe later. I use it for appending a new parent for the merge commit and\nafter parsing a new remote head. In both cases I always append a list.\n"},{"id":"78993","messageId":"7viqwmqexg.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"20080606235239.GY29404@genesis.frugalware.org","subject":"Re: [PATCH 08/10] Introduce commit_list_append() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-07T00:14:35Z","receivedAt":"2008-06-07T00:14:35Z","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> On Thu, Jun 05, 2008 at 04:16:46PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n>> Do you have a caller of this function that keeps a pointer to commit_list\n>> that needs to be appended at the tail or inserted at the beginning\n>> depending on the phase of the moon, or does the caller always append to\n>> that list?\n>\n> The later. I use it for appending a new parent for the merge commit and\n> after parsing a new remote head. In both cases I always append a list.\n\nIf that is the case, you might want to check how parse_commit_buffer() in\ncommit.c builds \"parents\" list.  It is a standard pattern to append to the\nlist using commit_list_insert() and that is the reason why it returns a\npointer.\n"},{"id":"79000","messageId":"1212799649-3822-1-git-send-email-vmiklos@frugalware.org","threadId":"13817","inReplyTo":"7vmylzzdyo.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Add new test to ensure git-merge handles pull.twohead and pull.octopus","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-07T00:47:29Z","receivedAt":"2008-06-07T00:47: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\nOn Thu, Jun 05, 2008 at 03:58:23PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > +test_expect_success 'merge c1 with c2' '\n> > +   git reset --hard c1 &&\n>\n> test that c0 and c1 do and c2 and c3 do not exist here, as it is\n> cheap,\n> and otherwise you may end up chasing wild-goose when somebody breaks\n> git-reset.  No need to do so in later tests in this script, but it is\n> a\n> cheap protection for yourself from others' mistakes ;-).\n\nDone.\n\n> > +   git merge c2 &&\n> > +   test -e c1.c &&\n> > +   test -e c2.c\n> > +'\n>\n> Nobody runs V7 that lacked \"test -e\" to run these test scripts, but\n> you\n> expect them to be regular files at this point of the test, so the\n> correct\n> way to spell these is with \"test -f\".\n>\n> In general, you are better off training yourself to think if you can\n> use\n> \"test -f\" before blindly using \"test -e\".\n\nSure, corrected.\n\n> > +test_expect_success 'merge c1 with c2 and c3 (recursive in\n> > pull.octopus)' '\n> > +   git reset --hard c1 &&\n> > +   git config pull.octopus \"recursive\" &&\n> > +   ! git merge c2 c3\n>\n> Is it because it should dump core, or is it because the command should\n> decline to work, gracefully failing with an error message and non-zero\n> exit status?  Use \"test_must_fail\" to check for the latter.\n\nObviously the later, corrected.\n\n> Don't you want to check how it fails and in what shape the command\n> leaves\n> the work tree?  I am assuming that recursive sees more than one\n> \"remote\"\n> head and declines to work without touching work tree nor the index, so\n> if\n> that is what you expect, you should check for that.  Otherwise, a\n> regression that loses local changes will go unnoticed.\n\nHm yes. I added checks to ensure nothing happened.\n\n> > +test_expect_success 'merge c1 with c2 and c3 (recursive and octopus\n> > in pull.octopus)' '\n> > +   git reset --hard c1 &&\n> > +   git config pull.octopus \"recursive octopus\" &&\n> > +   git merge c2 c3\n>\n> Likewise, don't you want to check the result of the merge?  Not just\n> \"merge exited with 0\", but you would want to see that the HEAD has\n> advanced, it has the expected parents, there is no unexpected local\n> changes (because you did not have any when you started the merge), and\n> it\n> has the expected tree contents.\n\nCorrected.\n\nI'm sending the version I just pushed to my working branch.\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.rc0.dirty\n"},{"id":"79002","messageId":"1212800445-4365-1-git-send-email-vmiklos@frugalware.org","threadId":"13817","inReplyTo":"7vhcc7zdm5.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Move read_cache_unmerged() to read-cache.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-07T01:00:45Z","receivedAt":"2008-06-07T01:00:45Z","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\nOn Thu, Jun 05, 2008 at 04:05:54PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Looks good, but as a public interface probably it needs a few lines of\n> comment in front of the function's definition to describe what it is\n> used\n> for.  Perhaps like...\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\nAdded. I'm sending the patch I just pushed to my working branch.\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 e342ad3..df0ac56 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.rc0.dirty\n"},{"id":"79003","messageId":"20080607010459.GA29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7vd4mvzdbz.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 07/10] git-fmt-merge-msg: make it useable from other builtins","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-07T01:04:59Z","receivedAt":"2008-06-07T01:04:59Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Thu, Jun 05, 2008 at 04:12:00PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> The word in the title should be \"usable\" (s/sea/sa/).\n> \n> Other than that, looks like a quite straightforward conversion.\n\nOK, I fixed up the patch title in git://repo.or.cz/git/vmiklos.git.\n"},{"id":"79006","messageId":"20080607020326.GB29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7viqwmqexg.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 08/10] Introduce commit_list_append() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-07T02:03:26Z","receivedAt":"2008-06-07T02:03:26Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Fri, Jun 06, 2008 at 05:14:35PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> If that is the case, you might want to check how parse_commit_buffer() in\n> commit.c builds \"parents\" list.  It is a standard pattern to append to the\n> list using commit_list_insert() and that is the reason why it returns a\n> pointer.\n\nAh, great, then this patch is not necessary. I removed it from my\nworking branch.\n\nThanks.\n"},{"id":"79008","messageId":"20080607023033.GC29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7vk5h3xlt9.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 09/10] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-07T02:30:33Z","receivedAt":"2008-06-07T02:30:33Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Thu, Jun 05, 2008 at 08:51:46PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> In what sense is this \"like show-branch --merge-base\"?\n> \n> The only similarlity I can spot is that it can take more than two heads,\n> but what it computes and the way it computes it seem to be different.  It\n> certainly looks much less efficient as it does not walk the ancestry chain\n> in one-go like show-branch does.\n\nExactly. The original idea was to avoid the current limitation:\nshow-branch uses object flags for each branch, which is not something\nthat works if you have a lot of ones.\n"},{"id":"79070","messageId":"1212874720-22324-1-git-send-email-vmiklos@frugalware.org","threadId":"13817","inReplyTo":"7vmylzw1l4.fsf@gitster.siamese.dyndns.org","subject":"[PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-07T21:38:40Z","receivedAt":"2008-06-07T21:38:40Z","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.\nInternally it uses merge_bases_many() but it has the ability to clear\ncommit marks like get_merge_bases().\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Thu, Jun 05, 2008 at 10:53:59PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Something like this might be enough to get us started.  Instead of\n> finding\n> out the merge bases between two commits, \"one\" and \"two\", this would\n> find\n> merge base between \"one\" and a commit that would be a merge across all\n> the\n> \"two\"s.  To apply it to the above example, you would give \"C\" as\n> \"one\",\n> and \"A\" and \"B\" as \"twos\" to it, to compute \"2\".\n\nHere is what I pushed to my working branch, on top of your patch.\n\nBTW: How should I credit your patch? Add a Signed-off-by? If I make you\nthe author I don't know how send-email handles the situation.\n\nThanks.\n\n commit.c |   17 +++++++++++++++++\n commit.h |    1 +\n 2 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 1dba717..685a144 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -618,6 +618,23 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n \treturn merge_bases_many(one, 1, &two);\n }\n \n+struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n+{\n+\tstruct commit *head = in->item;\n+\tint n = 0;\n+\tstruct commit **commits = xmalloc(n*sizeof(struct commit *));\n+\tstruct commit_list *ret, *i;\n+\n+\tfor(i = in; i; i = i->next, n++)\n+\t\tcommits[n] = i->item;\n+\tret = merge_bases_many(head, n, commits);\n+\tfree(commits);\n+\tif (cleanup)\n+\t\tfor(i = in; i; i = i->next)\n+\t\t\tclear_commit_marks(i->item, all_flags);\n+\treturn ret;\n+}\n+\n struct commit_list *get_merge_bases(struct commit *one,\n \t\t\t\t\tstruct commit *two, int cleanup)\n {\ndiff --git a/commit.h b/commit.h\nindex 7f8c5ee..ca858ed 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -121,6 +121,7 @@ int read_graft_file(const char *graft_file);\n struct commit_graft *lookup_commit_graft(const unsigned char *sha1);\n \n extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);\n+extern struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup);\n \n extern int register_shallow(const unsigned char *sha1);\n extern int unregister_shallow(const unsigned char *sha1);\n-- \n1.5.6.rc0.dirty\n"},{"id":"79214","messageId":"484CF0B4.6000102@op5.se","threadId":"13817","inReplyTo":"2e4b20178405cf993ce9e0f1ffe4ac402a96fd03.1212698317.git.vmiklos@frugalware.org","subject":"Re: [PATCH 07/10] git-fmt-merge-msg: make it useable from other builtins","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2008-06-09T08:58:28Z","receivedAt":"2008-06-09T08:58:28Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Miklos Vajna wrote:\n> Move all functionality (except config and option parsing) from\n> cmd_fmt_merge_msg() to fmt_merge_msg(), so that other builtins can use\n> it without a child process.\n> \n> All functions have been changed to use strbufs, and now only\n> cmd_fmt_merge_msg() reads directly from a file / writes anything to\n> stdout.\n> \n\nI have a very nearly identical patch in an ancient offshoot branch\nsince my attempt to make git-pull a builtin (which was stupid as\nit doesn't do anything CPU-intensive apart from merging).\n\nI likes it. For bonus points you could replace some strbuf_addf()\ncalls with strbuf_addstr() ones, but I don't think anyone really\ncares.\n\nSo, fwiw,\nLiked-by: Andreas Ericsson <ae@op5.se>\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"79224","messageId":"alpine.DEB.1.00.0806091458190.1783@racer","threadId":"13817","inReplyTo":"1212874720-22324-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-09T14:02:07Z","receivedAt":"2008-06-09T14:02:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 7 Jun 2008, Miklos Vajna wrote:\n\n> diff --git a/commit.c b/commit.c\n> index 1dba717..685a144 100644\n> --- a/commit.c\n> +++ b/commit.c\n> @@ -618,6 +618,23 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n>  \treturn merge_bases_many(one, 1, &two);\n>  }\n>  \n> +struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n> +{\n> +\tstruct commit *head = in->item;\n> +\tint n = 0;\n> +\tstruct commit **commits = xmalloc(n*sizeof(struct commit *));\n\nHere, n will be 0 and therefore commits will be xmalloc(0), right?\n\n> +\tstruct commit_list *ret, *i;\n> +\n> +\tfor(i = in; i; i = i->next, n++)\n> +\t\tcommits[n] = i->item;\n\nAnd here, commits will never be realloc()ed.\n\n> +\tret = merge_bases_many(head, n, commits);\n\nIf merge_bases_many took a commit_list (yes, as I suggested to Junio), \nthis transformation would not be necessary.\n\nIIRC nothing in merge_bases_many() needed a commit array.\n\nOh, and whose responsibility is it to free \"in\"?  Caller or callee?  \n(Because it is a non-const parameter, I would have expected the callee, \nbut I think it makes more sense if the caller can do whatever she wants \nwith the heads after calling octopus_merge_bases()).\n\nCiao,\nDscho\n"},{"id":"79277","messageId":"1213051426-11530-1-git-send-email-vmiklos@frugalware.org","threadId":"13817","inReplyTo":"alpine.DEB.1.00.0806091458190.1783@racer","subject":"[PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-09T22:43:46Z","receivedAt":"2008-06-09T22:43:46Z","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.\nInternally it uses merge_bases_many() but it has the ability to clear\ncommit marks like get_merge_bases().\n\nSigned-off-by: Miklos Vajna <vmiklos@frugalware.org>\n---\n\nOn Mon, Jun 09, 2008 at 03:02:07PM +0100, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > +   int n = 0;\n> > +   struct commit **commits = xmalloc(n*sizeof(struct commit *));\n>\n> Here, n will be 0 and therefore commits will be xmalloc(0), right?\n>\n> > +   struct commit_list *ret, *i;\n> > +\n> > +   for(i = in; i; i = i->next, n++)\n> > +           commits[n] = i->item;\n>\n> And here, commits will never be realloc()ed.\n\nOuch, yes.\n\n> > +   ret = merge_bases_many(head, n, commits);\n>\n> If merge_bases_many took a commit_list (yes, as I suggested to Junio),\n> this transformation would not be necessary.\n>\n> IIRC nothing in merge_bases_many() needed a commit array.\n\nExactly. I modified merge_bases_many() to use a commit list.\n\n> Oh, and whose responsibility is it to free \"in\"?  Caller or callee?\n> (Because it is a non-const parameter, I would have expected the\n> callee,\n> but I think it makes more sense if the caller can do whatever she\n> wants\n> with the heads after calling octopus_merge_bases()).\n\nThe previous. I think using const would be a mistake since we do modify\nthe flags, but I still would prefer free the list in the caller, since\nthe list is still usable. That's exactly why the cleanup flag is there:\nin case the non-empty objects flags would cause any problem later in the\ncaller.\n\n commit.c |   11 +++++++++++\n commit.h |    1 +\n 2 files changed, 12 insertions(+), 0 deletions(-)\n\ndiff --git a/commit.c b/commit.c\nindex 033c1e8..f3ca099 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -615,6 +615,17 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)\n \treturn merge_bases_many(one, list);\n }\n \n+struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n+{\n+\tstruct commit_list *ret, *i;\n+\n+\tret = merge_bases_many(in->item, in->next);\n+\tif (cleanup)\n+\t\tfor(i = in; i; i = i->next)\n+\t\t\tclear_commit_marks(i->item, all_flags);\n+\treturn ret;\n+}\n+\n struct commit_list *get_merge_bases(struct commit *one,\n \t\t\t\t\tstruct commit *two, int cleanup)\n {\ndiff --git a/commit.h b/commit.h\nindex 7f8c5ee..ca858ed 100644\n--- a/commit.h\n+++ b/commit.h\n@@ -121,6 +121,7 @@ int read_graft_file(const char *graft_file);\n struct commit_graft *lookup_commit_graft(const unsigned char *sha1);\n \n extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);\n+extern struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup);\n \n extern int register_shallow(const unsigned char *sha1);\n extern int unregister_shallow(const unsigned char *sha1);\n-- \n1.5.6.rc2.dirty\n"},{"id":"79278","messageId":"1213051998-12000-1-git-send-email-vmiklos@frugalware.org","threadId":"13817","inReplyTo":"484CF0B4.6000102@op5.se","subject":"[PATCH] git-fmt-merge-msg: make it usable from other builtins","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-09T22:53:18Z","receivedAt":"2008-06-09T22:53:18Z","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\nOn Mon, Jun 09, 2008 at 10:58:28AM +0200, Andreas Ericsson <ae@op5.se> wrote:\n> I likes it. For bonus points you could replace some strbuf_addf()\n> calls with strbuf_addstr() ones, but I don't think anyone really\n> cares.\n\nHopefully this one has no unnecessary strbuf_addf() calls.\n\n> Liked-by: Andreas Ericsson <ae@op5.se>\n\n:-)\n\n builtin-fmt-merge-msg.c |  157 +++++++++++++++++++++++++++--------------------\n builtin.h               |    3 +\n 2 files changed, 94 insertions(+), 66 deletions(-)\n\ndiff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c\nindex b892621..91f08e9 100644\n--- a/builtin-fmt-merge-msg.c\n+++ b/builtin-fmt-merge-msg.c\n@@ -159,23 +159,24 @@ static int handle_line(char *line)\n }\n \n static void print_joined(const char *singular, const char *plural,\n-\t\tstruct list *list)\n+\t\tstruct list *list, struct strbuf *out)\n {\n \tif (list->nr == 0)\n \t\treturn;\n \tif (list->nr == 1) {\n-\t\tprintf(\"%s%s\", singular, list->list[0]);\n+\t\tstrbuf_addf(out, \"%s%s\", singular, list->list[0]);\n \t} else {\n \t\tint i;\n-\t\tprintf(\"%s\", plural);\n+\t\tstrbuf_addstr(out, plural);\n \t\tfor (i = 0; i < list->nr - 1; i++)\n-\t\t\tprintf(\"%s%s\", i > 0 ? \", \" : \"\", list->list[i]);\n-\t\tprintf(\" and %s\", list->list[list->nr - 1]);\n+\t\t\tstrbuf_addf(out, \"%s%s\", i > 0 ? \", \" : \"\", list->list[i]);\n+\t\tstrbuf_addf(out, \" and %s\", list->list[list->nr - 1]);\n \t}\n }\n \n static void shortlog(const char *name, unsigned char *sha1,\n-\t\tstruct commit *head, struct rev_info *rev, int limit)\n+\t\tstruct commit *head, struct rev_info *rev, int limit,\n+\t\tstruct strbuf *out)\n {\n \tint i, count = 0;\n \tstruct commit *commit;\n@@ -232,15 +233,15 @@ static void shortlog(const char *name, unsigned char *sha1,\n \t}\n \n \tif (count > limit)\n-\t\tprintf(\"\\n* %s: (%d commits)\\n\", name, count);\n+\t\tstrbuf_addf(out, \"\\n* %s: (%d commits)\\n\", name, count);\n \telse\n-\t\tprintf(\"\\n* %s:\\n\", name);\n+\t\tstrbuf_addf(out, \"\\n* %s:\\n\", name);\n \n \tfor (i = 0; i < subjects.nr; i++)\n \t\tif (i >= limit)\n-\t\t\tprintf(\"  ...\\n\");\n+\t\t\tstrbuf_addf(out, \"  ...\\n\");\n \t\telse\n-\t\t\tprintf(\"  %s\\n\", subjects.list[i]);\n+\t\t\tstrbuf_addf(out, \"  %s\\n\", subjects.list[i]);\n \n \tclear_commit_marks((struct commit *)branch, flags);\n \tclear_commit_marks(head, flags);\n@@ -251,43 +252,13 @@ static void shortlog(const char *name, unsigned char *sha1,\n \tfree_list(&subjects);\n }\n \n-int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)\n-{\n-\tint limit = 20, i = 0;\n+int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {\n+\tint limit = 20, i = 0, pos = 0;\n \tchar line[1024];\n-\tFILE *in = stdin;\n-\tconst char *sep = \"\";\n+\tchar *p = line, *sep = \"\";\n \tunsigned char head_sha1[20];\n \tconst char *current_branch;\n \n-\tgit_config(fmt_merge_msg_config, NULL);\n-\n-\twhile (argc > 1) {\n-\t\tif (!strcmp(argv[1], \"--log\") || !strcmp(argv[1], \"--summary\"))\n-\t\t\tmerge_summary = 1;\n-\t\telse if (!strcmp(argv[1], \"--no-log\")\n-\t\t\t\t|| !strcmp(argv[1], \"--no-summary\"))\n-\t\t\tmerge_summary = 0;\n-\t\telse if (!strcmp(argv[1], \"-F\") || !strcmp(argv[1], \"--file\")) {\n-\t\t\tif (argc < 3)\n-\t\t\t\tdie (\"Which file?\");\n-\t\t\tif (!strcmp(argv[2], \"-\"))\n-\t\t\t\tin = stdin;\n-\t\t\telse {\n-\t\t\t\tfclose(in);\n-\t\t\t\tin = fopen(argv[2], \"r\");\n-\t\t\t\tif (!in)\n-\t\t\t\t\tdie(\"cannot open %s\", argv[2]);\n-\t\t\t}\n-\t\t\targc--; argv++;\n-\t\t} else\n-\t\t\tbreak;\n-\t\targc--; argv++;\n-\t}\n-\n-\tif (argc > 1)\n-\t\tusage(fmt_merge_msg_usage);\n-\n \t/* get current branch */\n \tcurrent_branch = resolve_ref(\"HEAD\", head_sha1, 1, NULL);\n \tif (!current_branch)\n@@ -295,75 +266,129 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)\n \tif (!prefixcmp(current_branch, \"refs/heads/\"))\n \t\tcurrent_branch += 11;\n \n-\twhile (fgets(line, sizeof(line), in)) {\n+\t/* get a line */\n+\tfor (;;) {\n+\t\tint len;\n+\t\tchar *newline;\n+\n+\t\tif (pos >= in->len)\n+\t\t\tbreak;\n+\t\tp = in->buf + pos;\n+\t\tnewline = strchr(p, '\\n');\n+\t\tlen = newline ? newline - p : strlen(p);\n+\t\tpos += len + !!newline;\n \t\ti++;\n-\t\tif (line[0] == 0)\n-\t\t\tcontinue;\n-\t\tif (handle_line(line))\n-\t\t\tdie (\"Error in line %d: %s\", i, line);\n+\t\tp[len] = 0;\n+\t\tif (handle_line(p))\n+\t\t\tdie (\"Error in line %d: %.*s\", i, len, p);\n \t}\n \n-\tprintf(\"Merge \");\n+\tstrbuf_addstr(out, \"Merge \");\n \tfor (i = 0; i < srcs.nr; i++) {\n \t\tstruct src_data *src_data = srcs.payload[i];\n \t\tconst char *subsep = \"\";\n \n-\t\tprintf(sep);\n+\t\tstrbuf_addstr(out, sep);\n \t\tsep = \"; \";\n \n \t\tif (src_data->head_status == 1) {\n-\t\t\tprintf(srcs.list[i]);\n+\t\t\tstrbuf_addstr(out, srcs.list[i]);\n \t\t\tcontinue;\n \t\t}\n \t\tif (src_data->head_status == 3) {\n \t\t\tsubsep = \", \";\n-\t\t\tprintf(\"HEAD\");\n+\t\t\tstrbuf_addstr(out, \"HEAD\");\n \t\t}\n \t\tif (src_data->branch.nr) {\n-\t\t\tprintf(subsep);\n+\t\t\tstrbuf_addstr(out, subsep);\n \t\t\tsubsep = \", \";\n-\t\t\tprint_joined(\"branch \", \"branches \", &src_data->branch);\n+\t\t\tprint_joined(\"branch \", \"branches \", &src_data->branch,\n+\t\t\t\t\tout);\n \t\t}\n \t\tif (src_data->r_branch.nr) {\n-\t\t\tprintf(subsep);\n+\t\t\tstrbuf_addstr(out, subsep);\n \t\t\tsubsep = \", \";\n \t\t\tprint_joined(\"remote branch \", \"remote branches \",\n-\t\t\t\t\t&src_data->r_branch);\n+\t\t\t\t\t&src_data->r_branch, out);\n \t\t}\n \t\tif (src_data->tag.nr) {\n-\t\t\tprintf(subsep);\n+\t\t\tstrbuf_addstr(out, subsep);\n \t\t\tsubsep = \", \";\n-\t\t\tprint_joined(\"tag \", \"tags \", &src_data->tag);\n+\t\t\tprint_joined(\"tag \", \"tags \", &src_data->tag, out);\n \t\t}\n \t\tif (src_data->generic.nr) {\n-\t\t\tprintf(subsep);\n-\t\t\tprint_joined(\"commit \", \"commits \", &src_data->generic);\n+\t\t\tstrbuf_addstr(out, subsep);\n+\t\t\tprint_joined(\"commit \", \"commits \", &src_data->generic,\n+\t\t\t\t\tout);\n \t\t}\n \t\tif (strcmp(\".\", srcs.list[i]))\n-\t\t\tprintf(\" of %s\", srcs.list[i]);\n+\t\t\tstrbuf_addf(out, \" of %s\", srcs.list[i]);\n \t}\n \n \tif (!strcmp(\"master\", current_branch))\n-\t\tputchar('\\n');\n+\t\tstrbuf_addch(out, '\\n');\n \telse\n-\t\tprintf(\" into %s\\n\", current_branch);\n+\t\tstrbuf_addf(out, \" into %s\\n\", current_branch);\n \n \tif (merge_summary) {\n \t\tstruct commit *head;\n \t\tstruct rev_info rev;\n \n \t\thead = lookup_commit(head_sha1);\n-\t\tinit_revisions(&rev, prefix);\n+\t\tinit_revisions(&rev, NULL);\n \t\trev.commit_format = CMIT_FMT_ONELINE;\n \t\trev.ignore_merges = 1;\n \t\trev.limited = 1;\n \n \t\tfor (i = 0; i < origins.nr; i++)\n \t\t\tshortlog(origins.list[i], origins.payload[i],\n-\t\t\t\t\thead, &rev, limit);\n+\t\t\t\t\thead, &rev, limit, out);\n \t}\n+\treturn 0;\n+}\n+\n+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)\n+{\n+\tFILE *in = stdin;\n+\tstruct strbuf input, output;\n+\tint ret;\n+\n+\tgit_config(fmt_merge_msg_config, NULL);\n+\n+\twhile (argc > 1) {\n+\t\tif (!strcmp(argv[1], \"--log\") || !strcmp(argv[1], \"--summary\"))\n+\t\t\tmerge_summary = 1;\n+\t\telse if (!strcmp(argv[1], \"--no-log\")\n+\t\t\t\t|| !strcmp(argv[1], \"--no-summary\"))\n+\t\t\tmerge_summary = 0;\n+\t\telse if (!strcmp(argv[1], \"-F\") || !strcmp(argv[1], \"--file\")) {\n+\t\t\tif (argc < 3)\n+\t\t\t\tdie (\"Which file?\");\n+\t\t\tif (!strcmp(argv[2], \"-\"))\n+\t\t\t\tin = stdin;\n+\t\t\telse {\n+\t\t\t\tfclose(in);\n+\t\t\t\tin = fopen(argv[2], \"r\");\n+\t\t\t\tif (!in)\n+\t\t\t\t\tdie(\"cannot open %s\", argv[2]);\n+\t\t\t}\n+\t\t\targc--; argv++;\n+\t\t} else\n+\t\t\tbreak;\n+\t\targc--; argv++;\n+\t}\n+\n+\tif (argc > 1)\n+\t\tusage(fmt_merge_msg_usage);\n \n-\t/* No cleanup yet; is standalone anyway */\n+\tstrbuf_init(&input, 0);\n+\tif (strbuf_read(&input, fileno(in), 0) < 0)\n+\t\tdie(\"could not read input file %s\", strerror(errno));\n+\tstrbuf_init(&output, 0);\n \n+\tret = fmt_merge_msg(merge_summary, &input, &output);\n+\tif (ret)\n+\t\treturn ret;\n+\tprintf(\"%s\", output.buf);\n \treturn 0;\n }\ndiff --git a/builtin.h b/builtin.h\nindex b460b2d..2b01fea 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -2,6 +2,7 @@\n #define BUILTIN_H\n \n #include \"git-compat-util.h\"\n+#include \"strbuf.h\"\n \n extern const char git_version_string[];\n extern const char git_usage_string[];\n@@ -11,6 +12,8 @@ extern void list_common_cmds_help(void);\n extern void help_unknown_cmd(const char *cmd);\n extern void prune_packed_objects(int);\n extern int read_line_with_nul(char *buf, int size, FILE *file);\n+extern int fmt_merge_msg(int merge_summary, struct strbuf *in,\n+\tstruct strbuf *out);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n-- \n1.5.6.rc2.dirty\n"},{"id":"79287","messageId":"7vd4mqfcb0.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"1213051426-11530-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-09T22:55:47Z","receivedAt":"2008-06-09T22:55:47Z","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> Exactly. I modified merge_bases_many() to use a commit list.\n\nI just looked at your f755fb6; from the point of view of merge_bases()\nwhich is far more often used, I think this is not an improvement but\nactively wrong thing to do.  Most of the time callers compute merge base\nof two, and the codepath should be optimized for that case.\n"},{"id":"79289","messageId":"7v8wxefbtn.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"1213051426-11530-1-git-send-email-vmiklos@frugalware.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-09T23:06:12Z","receivedAt":"2008-06-09T23:06:12Z","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 *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n> +{\n> +\tstruct commit_list *ret, *i;\n> +\n> +\tret = merge_bases_many(in->item, in->next);\n> +\tif (cleanup)\n> +\t\tfor(i = in; i; i = i->next)\n> +\t\t\tclear_commit_marks(i->item, all_flags);\n> +\treturn ret;\n> +}\n\nI suspect either me or you are confused, but how is this exactly used?\n\nThe code for merge_bases_many(), at least the one I showed you a few days\nago, is not a replacement for \"show-branch --merge-base\", and I do not\nthink you would want to use it as such in the rewrite of git-merge, if you\nare trying to replace this part of git-merge.sh:\n\n        case \"$#\" in\n        1)\n                common=$(git merge-base --all $head \"$@\")\n                ;;\n        *)\n                common=$(git show-branch --merge-base $head \"$@\")\n                ;;\n        esac\n\nThe purpose of merge-base-many code was to improve this line in the\ngit-merge-octopus.sh:\n\n\tcommon=$(git merge-base --all $MRC $SHA1) ||\n\t\tdie \"Unable to find common commit with $SHA1\"\n\nInstead of keeping a single MRC, we can compute the merge-base-many\nbetween the SHA1 (i.e. the one we are looking at right now -- it is fed as\n\"one\") and all the previous SHA1's we have already looked at (they become\n\"two's\"), like this:\n\n\tcommon($git merge-base-many $SHA1 $SHA1_SO_FAR)\n\nand you would have at the end of the loop:\n\n\tSHA1_SO_FAR=\"$SHA1_SO_FAR$SHA1 \"\n\nor something.\n"},{"id":"79290","messageId":"alpine.DEB.1.00.0806100006140.1783@racer","threadId":"13817","inReplyTo":"7vd4mqfcb0.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-09T23:08:01Z","receivedAt":"2008-06-09T23:08:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 9 Jun 2008, Junio C Hamano wrote:\n\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n> \n> > Exactly. I modified merge_bases_many() to use a commit list.\n> \n> I just looked at your f755fb6; from the point of view of merge_bases() \n> which is far more often used, I think this is not an improvement but \n> actively wrong thing to do.  Most of the time callers compute merge base \n> of two, and the codepath should be optimized for that case.\n\nBut how much work is\n\n\tstruct commit_list list = { two, NULL };\n\n\tmerge_bases_many(one, &list);\n\n?  merge_bases_many() does not modify the list IIRC, and it looks \nconceptually clean while being pretty much in the same performance \nballpark.\n\nCiao,\nDscho\n"},{"id":"79291","messageId":"7vy75edwlx.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"alpine.DEB.1.00.0806100006140.1783@racer","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-09T23:20:10Z","receivedAt":"2008-06-09T23:20:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Mon, 9 Jun 2008, Junio C Hamano wrote:\n>\n>> Miklos Vajna <vmiklos@frugalware.org> writes:\n>> \n>> > Exactly. I modified merge_bases_many() to use a commit list.\n>> \n>> I just looked at your f755fb6; from the point of view of merge_bases() \n>> which is far more often used, I think this is not an improvement but \n>> actively wrong thing to do.  Most of the time callers compute merge base \n>> of two, and the codepath should be optimized for that case.\n>\n> But how much work is\n>\n> \tstruct commit_list list = { two, NULL };\n>\n> \tmerge_bases_many(one, &list);\n\nBut (1) that is not what f755fb6 does; it does commit_list_insert() that\nhas an allocation, and (2) how much work is to count, allocate and pass\narray for slow and less frequent codepath which octopus-merge-base is?\n"},{"id":"79293","messageId":"20080609232524.GV29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7v8wxefbtn.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-09T23:25:24Z","receivedAt":"2008-06-09T23:25:24Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Mon, Jun 09, 2008 at 04:06:12PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n> \n> > +struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n> > +{\n> > +\tstruct commit_list *ret, *i;\n> > +\n> > +\tret = merge_bases_many(in->item, in->next);\n> > +\tif (cleanup)\n> > +\t\tfor(i = in; i; i = i->next)\n> > +\t\t\tclear_commit_marks(i->item, all_flags);\n> > +\treturn ret;\n> > +}\n> \n> I suspect either me or you are confused, but how is this exactly used?\n> \n> The code for merge_bases_many(), at least the one I showed you a few days\n> ago, is not a replacement for \"show-branch --merge-base\", and I do not\n> think you would want to use it as such in the rewrite of git-merge, if you\n> are trying to replace this part of git-merge.sh:\n> \n>         case \"$#\" in\n>         1)\n>                 common=$(git merge-base --all $head \"$@\")\n>                 ;;\n>         *)\n>                 common=$(git show-branch --merge-base $head \"$@\")\n>                 ;;\n>         esac\n\nThen I think I'm the one who is confused. My original\nget_octopus_merge_bases() - in commit e13c0c2 - was exactly to replace\nthis part.\n\nSo, may I assume that the original get_octopus_merge_bases() is OK for\nthis purpose?\n\n> The purpose of merge-base-many code was to improve this line in the\n> git-merge-octopus.sh:\n> \n> \tcommon=$(git merge-base --all $MRC $SHA1) ||\n> \t\tdie \"Unable to find common commit with $SHA1\"\n> \n> Instead of keeping a single MRC, we can compute the merge-base-many\n> between the SHA1 (i.e. the one we are looking at right now -- it is fed as\n> \"one\") and all the previous SHA1's we have already looked at (they become\n> \"two's\"), like this:\n> \n> \tcommon($git merge-base-many $SHA1 $SHA1_SO_FAR)\n> \n> and you would have at the end of the loop:\n> \n> \tSHA1_SO_FAR=\"$SHA1_SO_FAR$SHA1 \"\n> \n> or something.\n\nActually I do not want to touch git-merge-octopus.sh before\nbuiltin-merge is not ready. (I try to \"do one thing and do it\nwell\".)\n\nThanks.\n"},{"id":"79294","messageId":"alpine.DEB.1.00.0806100024310.1783@racer","threadId":"13817","inReplyTo":"7v8wxefbtn.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-09T23:31:23Z","receivedAt":"2008-06-09T23:31:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 9 Jun 2008, Junio C Hamano wrote:\n\n> Miklos Vajna <vmiklos@frugalware.org> writes:\n> \n> > +struct commit_list *get_octopus_merge_bases(struct commit_list *in, int cleanup)\n> > +{\n> > +\tstruct commit_list *ret, *i;\n> > +\n> > +\tret = merge_bases_many(in->item, in->next);\n> > +\tif (cleanup)\n> > +\t\tfor(i = in; i; i = i->next)\n> > +\t\t\tclear_commit_marks(i->item, all_flags);\n> > +\treturn ret;\n> > +}\n> \n> I suspect either me or you are confused, but how is this exactly used?\n> \n> The code for merge_bases_many(), at least the one I showed you a few days\n> ago, is not a replacement for \"show-branch --merge-base\", and I do not\n> think you would want to use it as such in the rewrite of git-merge, if you\n> are trying to replace this part of git-merge.sh:\n> \n>         case \"$#\" in\n>         1)\n>                 common=$(git merge-base --all $head \"$@\")\n>                 ;;\n>         *)\n>                 common=$(git show-branch --merge-base $head \"$@\")\n>                 ;;\n>         esac\n> \n> The purpose of merge-base-many code was to improve this line in the\n> git-merge-octopus.sh:\n> \n> \tcommon=$(git merge-base --all $MRC $SHA1) ||\n> \t\tdie \"Unable to find common commit with $SHA1\"\n> \n> Instead of keeping a single MRC, we can compute the merge-base-many\n> between the SHA1 (i.e. the one we are looking at right now -- it is fed as\n> \"one\") and all the previous SHA1's we have already looked at (they become\n> \"two's\"), like this:\n> \n> \tcommon($git merge-base-many $SHA1 $SHA1_SO_FAR)\n> \n> and you would have at the end of the loop:\n> \n> \tSHA1_SO_FAR=\"$SHA1_SO_FAR$SHA1 \"\n> \n> or something.\n\nAh!  Even I thought that merge_bases_many() was meant for the show-branch \n--merge-base case.\n\nHowever, your remark about optimizing for the two-head case got me \nthinking: should we not rather use the simple algorithm Miklos proposed \nfor octopus_merge_bases(), even if it is suboptimal?\n\nOctopus is such a rare case that it is more important to have a robust, \nworking code, than a fast one, right?  Especially since Octopus will be \nexercized much less often, and therefore has a higher chance of hiding a \nbug.\n\nCiao,\nDscho\n"},{"id":"79295","messageId":"20080609233509.GW29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7vy75edwlx.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-09T23:35:09Z","receivedAt":"2008-06-09T23:35:09Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Mon, Jun 09, 2008 at 04:20:10PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> > \tstruct commit_list list = { two, NULL };\n> >\n> > \tmerge_bases_many(one, &list);\n> \n> But (1) that is not what f755fb6 does; it does commit_list_insert() that\n> has an allocation, and (2) how much work is to count, allocate and pass\n> array for slow and less frequent codepath which octopus-merge-base is?\n\nIf (1) is the only problem, I can do it the way Dscho suggested, but I\ndon't exactly see why using a commit_list* can slow merge_bases() down:\n\nfor (i = 0; i < n; i++) {\n        ...\n}\n\nis probably as fast as\n\nfor (i = list; i; i = i->next) {\n        ...\n}\n\nin case n = 0 / list->next is NULL.\n"},{"id":"79297","messageId":"7vlk1edvn1.fsf@gitster.siamese.dyndns.org","threadId":"13817","inReplyTo":"alpine.DEB.1.00.0806100024310.1783@racer","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-06-09T23:41:06Z","receivedAt":"2008-06-09T23:41:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> However, your remark about optimizing for the two-head case got me \n> thinking: should we not rather use the simple algorithm Miklos proposed \n> for octopus_merge_bases(), even if it is suboptimal?\n\nActually a quick glance at git-merge, a rather large case...esac after\nthat \"show-branch --merge-base\" tells me that we do not really use the\noutput from that operation and instead we check if we are fast-forward\nfrom all the other heads by iterating over them.  merge-octupos would\naccept it as the base but never looks at it.\n\nSo I think with a proper refactoring of the codepath you probably would\nnot even have to do the blanket \"merge-base across all heads\" at all.\n"},{"id":"79301","messageId":"20080610000340.GY29404@genesis.frugalware.org","threadId":"13817","inReplyTo":"7vlk1edvn1.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Miklos Vajna","fromEmail":"vmiklos@frugalware.org","sentAt":"2008-06-10T00:03:40Z","receivedAt":"2008-06-10T00:03:40Z","isPatch":true,"sender":{"key":"vmiklos@frugalware.org","avatar":"https://gravatar.com/avatar/401c1cbbb3a5d13e650c691a2c71d6fd0b80df1a01bc74d9f1972675dd58f2bd?d=mp&s=160"},"body":"On Mon, Jun 09, 2008 at 04:41:06PM -0700, Junio C Hamano <gitster@pobox.com> wrote:\n> Actually a quick glance at git-merge, a rather large case...esac after\n> that \"show-branch --merge-base\" tells me that we do not really use the\n> output from that operation and instead we check if we are fast-forward\n> from all the other heads by iterating over them.  merge-octupos would\n> accept it as the base but never looks at it.\n\nI may be wrong but I think it would be still nice to pass a valid base\nto the backend, even if _currently_ the only octopus implementation\nignores it.\n\nActually on \"valid\" I mean what my original get_octopus_merge_bases()\nreturned. :-)\n"},{"id":"79312","messageId":"alpine.DEB.1.00.0806100341430.1783@racer","threadId":"13817","inReplyTo":"20080610000340.GY29404@genesis.frugalware.org","subject":"Re: [PATCH] Introduce get_octopus_merge_bases() in commit.c","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-10T02:43:51Z","receivedAt":"2008-06-10T02:43:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 10 Jun 2008, Miklos Vajna wrote:\n\n> On Mon, Jun 09, 2008 at 04:41:06PM -0700, Junio C Hamano \n> <gitster@pobox.com> wrote:\n>\n> > Actually a quick glance at git-merge, a rather large case...esac after \n> > that \"show-branch --merge-base\" tells me that we do not really use the \n> > output from that operation and instead we check if we are fast-forward \n> > from all the other heads by iterating over them.  merge-octupos would \n> > accept it as the base but never looks at it.\n> \n> I may be wrong but I think it would be still nice to pass a valid base \n> to the backend, even if _currently_ the only octopus implementation \n> ignores it.\n> \n> Actually on \"valid\" I mean what my original get_octopus_merge_bases() \n> returned. :-)\n\nIMO that is one of the advantages of making git-merge a builtin: you need \nnot work around the lack of performance of shell scripts, but you can \nactually compute what you need to compute.\n\nIOW I agree that we could give the proper list of merge bases to whatever \noctopus backend we use.  And since your original implementation is \ncorrect, if not super-optimal, I would like to stick to it.\n\nCiao,\nDscho\n"}]}