{"thread":{"id":"39668","subject":"[PATCH v4 00/19] Make git-pull a builtin","startedAt":"2015-06-18T10:53:53Z","lastAt":"2015-06-19T10:11:36Z","messageCount":22,"participants":["Paul Tan","Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":19},"messages":[{"id":"264116","messageId":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":null,"subject":"[PATCH v4 00/19] Make git-pull a builtin","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:53Z","receivedAt":"2015-06-18T10:53:53Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"This is a re-roll of [v3]. It squashes in Ramsay's patch \"fix some sparse\nwarnings\", and fixes the use-before-free reported by Duy. Thanks a lot for\ndealing with my mess :-).\n\nOther than that, there are no other changes as I'm working on the git-am side\nof things.\n\nPrevious versions:\n\n[v1] http://thread.gmane.org/gmane.comp.version-control.git/269258\n[v2] http://thread.gmane.org/gmane.comp.version-control.git/270639\n[v3] http://thread.gmane.org/gmane.comp.version-control.git/271614\n\ngit-pull is a commonly executed command to check for new changes in the\nupstream repository and, if there are, fetch and integrate them into the\ncurrent branch. Currently it is implemented by the shell script git-pull.sh.\nHowever, compared to C, shell scripts have certain deficiencies -- they need to\nspawn a lot of processes, introduce a lot of dependencies and cannot take\nadvantage of git's internal caches.\n\nThis series rewrites git-pull.sh into a C builtin, thus improving its\nperformance and portability. It is part of my GSoC project to rewrite git-pull\nand git-am into builtins[1].\n\n[1] https://gist.github.com/pyokagan/1b7b0d1f4dab6ba3cef1\n\n\nPaul Tan (19):\n  parse-options-cb: implement parse_opt_passthru()\n  parse-options-cb: implement parse_opt_passthru_argv()\n  argv-array: implement argv_array_pushv()\n  pull: implement skeletal builtin pull\n  pull: implement fetch + merge\n  pull: pass verbosity, --progress flags to fetch and merge\n  pull: pass git-merge's options to git-merge\n  pull: pass git-fetch's options to git-fetch\n  pull: error on no merge candidates\n  pull: support pull.ff config\n  pull: check if in unresolved merge state\n  pull: fast-forward working tree if head is updated\n  pull: implement pulling into an unborn branch\n  pull: set reflog message\n  pull: teach git pull about --rebase\n  pull: configure --rebase via branch.<name>.rebase or pull.rebase\n  pull --rebase: exit early when the working directory is dirty\n  pull --rebase: error on no merge candidate cases\n  pull: remove redirection to git-pull.sh\n\n Documentation/technical/api-argv-array.txt    |   3 +\n Documentation/technical/api-parse-options.txt |  13 +\n Makefile                                      |   2 +-\n advice.c                                      |   8 +\n advice.h                                      |   1 +\n argv-array.c                                  |   6 +\n argv-array.h                                  |   1 +\n builtin.h                                     |   1 +\n builtin/pull.c                                | 882 ++++++++++++++++++++++++++\n git-pull.sh => contrib/examples/git-pull.sh   |   0\n git.c                                         |   1 +\n parse-options-cb.c                            |  69 ++\n parse-options.h                               |   6 +\n 13 files changed, 992 insertions(+), 1 deletion(-)\n create mode 100644 builtin/pull.c\n rename git-pull.sh => contrib/examples/git-pull.sh (100%)\n\n-- \n2.1.4\n"},{"id":"264117","messageId":"1434624852-6869-2-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 01/19] parse-options-cb: implement parse_opt_passthru()","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:54Z","receivedAt":"2015-06-18T10:53:54Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Certain git commands, such as git-pull, are simply wrappers around other\ngit commands like git-fetch, git-merge and git-rebase. As such, these\nwrapper commands will typically need to \"pass through\" command-line\noptions of the commands they wrap.\n\nImplement the parse_opt_passthru() parse-options callback, which will\nreconstruct the command-line option into an char* string, such that it\ncan be passed to another git command.\n\nHelped-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Documentation/technical/api-parse-options.txt |  7 ++++\n parse-options-cb.c                            | 49 +++++++++++++++++++++++++++\n parse-options.h                               |  3 ++\n 3 files changed, 59 insertions(+)\n\ndiff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt\nindex 1f2db31..85d10ab 100644\n--- a/Documentation/technical/api-parse-options.txt\n+++ b/Documentation/technical/api-parse-options.txt\n@@ -212,6 +212,13 @@ There are some macros to easily define options:\n \tUse it to hide deprecated options that are still to be recognized\n \tand ignored silently.\n \n+`OPT_PASSTHRU(short, long, &char_var, arg_str, description, flags)`::\n+\tIntroduce an option that will be reconstructed into a char* string,\n+\twhich must be initialized to NULL. This is useful when you need to\n+\tpass the command-line option to another command. Any previous value\n+\twill be overwritten, so this should only be used for options where\n+\tthe last one specified on the command line wins.\n+\n \n The last element of the array must be `OPT_END()`.\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex be8c413..68bc593 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -134,3 +134,52 @@ int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset)\n {\n \treturn 0;\n }\n+\n+/**\n+ * Recreates the command-line option in the strbuf.\n+ */\n+static int recreate_opt(struct strbuf *sb, const struct option *opt,\n+\t\tconst char *arg, int unset)\n+{\n+\tstrbuf_reset(sb);\n+\n+\tif (opt->long_name) {\n+\t\tstrbuf_addstr(sb, unset ? \"--no-\" : \"--\");\n+\t\tstrbuf_addstr(sb, opt->long_name);\n+\t\tif (arg) {\n+\t\t\tstrbuf_addch(sb, '=');\n+\t\t\tstrbuf_addstr(sb, arg);\n+\t\t}\n+\t} else if (opt->short_name && !unset) {\n+\t\tstrbuf_addch(sb, '-');\n+\t\tstrbuf_addch(sb, opt->short_name);\n+\t\tif (arg)\n+\t\t\tstrbuf_addstr(sb, arg);\n+\t} else\n+\t\treturn -1;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * For an option opt, recreates the command-line option in opt->value which\n+ * must be an char* initialized to NULL. This is useful when we need to pass\n+ * the command-line option to another command. Since any previous value will be\n+ * overwritten, this callback should only be used for options where the last\n+ * one wins.\n+ */\n+int parse_opt_passthru(const struct option *opt, const char *arg, int unset)\n+{\n+\tstatic struct strbuf sb = STRBUF_INIT;\n+\tchar **opt_value = opt->value;\n+\n+\tif (recreate_opt(&sb, opt, arg, unset) < 0)\n+\t\treturn -1;\n+\n+\tif (*opt_value)\n+\t\tfree(*opt_value);\n+\n+\t*opt_value = strbuf_detach(&sb, NULL);\n+\n+\treturn 0;\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex c71e9da..5b0f886 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -224,6 +224,7 @@ extern int parse_opt_with_commit(const struct option *, const char *, int);\n extern int parse_opt_tertiary(const struct option *, const char *, int);\n extern int parse_opt_string_list(const struct option *, const char *, int);\n extern int parse_opt_noop_cb(const struct option *, const char *, int);\n+extern int parse_opt_passthru(const struct option *, const char *, int);\n \n #define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', \"verbose\", (var), (h))\n #define OPT__QUIET(var, h)    OPT_COUNTUP('q', \"quiet\",   (var), (h))\n@@ -242,5 +243,7 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);\n \tOPT_COLOR_FLAG(0, \"color\", (var), (h))\n #define OPT_COLUMN(s, l, v, h) \\\n \t{ OPTION_CALLBACK, (s), (l), (v), N_(\"style\"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n+#define OPT_PASSTHRU(s, l, v, a, h, f) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }\n \n #endif\n-- \n2.1.4\n"},{"id":"264118","messageId":"1434624852-6869-3-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 02/19] parse-options-cb: implement parse_opt_passthru_argv()","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:55Z","receivedAt":"2015-06-18T10:53:55Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Certain git commands, such as git-pull, are simply wrappers around other\ngit commands like git-fetch, git-merge and git-rebase. As such, these\nwrapper commands will typically need to \"pass through\" command-line\noptions of the commands they wrap.\n\nImplement the parse_opt_passthru_argv() parse-options callback, which\nwill reconstruct all the provided command-line options into an\nargv_array, such that it can be passed to another git command. This is\nuseful for passing command-line options that can be specified multiple\ntimes.\n\nHelped-by: Stefan Beller <sbeller@google.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Documentation/technical/api-parse-options.txt |  6 ++++++\n parse-options-cb.c                            | 20 ++++++++++++++++++++\n parse-options.h                               |  3 +++\n 3 files changed, 29 insertions(+)\n\ndiff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt\nindex 85d10ab..0b0ab01 100644\n--- a/Documentation/technical/api-parse-options.txt\n+++ b/Documentation/technical/api-parse-options.txt\n@@ -219,6 +219,12 @@ There are some macros to easily define options:\n \twill be overwritten, so this should only be used for options where\n \tthe last one specified on the command line wins.\n \n+`OPT_PASSTHRU_ARGV(short, long, &argv_array_var, arg_str, description, flags)`::\n+\tIntroduce an option where all instances of it on the command-line will\n+\tbe reconstructed into an argv_array. This is useful when you need to\n+\tpass the command-line option, which can be specified multiple times,\n+\tto another command.\n+\n \n The last element of the array must be `OPT_END()`.\n \ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 68bc593..5ab6ed6 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -4,6 +4,7 @@\n #include \"commit.h\"\n #include \"color.h\"\n #include \"string-list.h\"\n+#include \"argv-array.h\"\n \n /*----- some often used options -----*/\n \n@@ -183,3 +184,22 @@ int parse_opt_passthru(const struct option *opt, const char *arg, int unset)\n \n \treturn 0;\n }\n+\n+/**\n+ * For an option opt, recreate the command-line option, appending it to\n+ * opt->value which must be a argv_array. This is useful when we need to pass\n+ * the command-line option, which can be specified multiple times, to another\n+ * command.\n+ */\n+int parse_opt_passthru_argv(const struct option *opt, const char *arg, int unset)\n+{\n+\tstatic struct strbuf sb = STRBUF_INIT;\n+\tstruct argv_array *opt_value = opt->value;\n+\n+\tif (recreate_opt(&sb, opt, arg, unset) < 0)\n+\t\treturn -1;\n+\n+\targv_array_push(opt_value, sb.buf);\n+\n+\treturn 0;\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex 5b0f886..aba06688 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -225,6 +225,7 @@ extern int parse_opt_tertiary(const struct option *, const char *, int);\n extern int parse_opt_string_list(const struct option *, const char *, int);\n extern int parse_opt_noop_cb(const struct option *, const char *, int);\n extern int parse_opt_passthru(const struct option *, const char *, int);\n+extern int parse_opt_passthru_argv(const struct option *, const char *, int);\n \n #define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', \"verbose\", (var), (h))\n #define OPT__QUIET(var, h)    OPT_COUNTUP('q', \"quiet\",   (var), (h))\n@@ -245,5 +246,7 @@ extern int parse_opt_passthru(const struct option *, const char *, int);\n \t{ OPTION_CALLBACK, (s), (l), (v), N_(\"style\"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n #define OPT_PASSTHRU(s, l, v, a, h, f) \\\n \t{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru }\n+#define OPT_PASSTHRU_ARGV(s, l, v, a, h, f) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), (a), (h), (f), parse_opt_passthru_argv }\n \n #endif\n-- \n2.1.4\n"},{"id":"264119","messageId":"1434624852-6869-4-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 03/19] argv-array: implement argv_array_pushv()","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:56Z","receivedAt":"2015-06-18T10:53:56Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"When we have a null-terminated array, it would be useful to convert it\nor append it to an argv_array for further manipulation.\n\nImplement argv_array_pushv() which will push a null-terminated array of\nstrings on to an argv_array.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Documentation/technical/api-argv-array.txt | 3 +++\n argv-array.c                               | 6 ++++++\n argv-array.h                               | 1 +\n 3 files changed, 10 insertions(+)\n\ndiff --git a/Documentation/technical/api-argv-array.txt b/Documentation/technical/api-argv-array.txt\nindex 1a79781..8076172 100644\n--- a/Documentation/technical/api-argv-array.txt\n+++ b/Documentation/technical/api-argv-array.txt\n@@ -46,6 +46,9 @@ Functions\n \tFormat a string and push it onto the end of the array. This is a\n \tconvenience wrapper combining `strbuf_addf` and `argv_array_push`.\n \n+`argv_array_pushv`::\n+\tPush a null-terminated array of strings onto the end of the array.\n+\n `argv_array_pop`::\n \tRemove the final element from the array. If there are no\n \telements in the array, do nothing.\ndiff --git a/argv-array.c b/argv-array.c\nindex 256741d..eaed477 100644\n--- a/argv-array.c\n+++ b/argv-array.c\n@@ -49,6 +49,12 @@ void argv_array_pushl(struct argv_array *array, ...)\n \tva_end(ap);\n }\n \n+void argv_array_pushv(struct argv_array *array, const char **argv)\n+{\n+\tfor (; *argv; argv++)\n+\t\targv_array_push(array, *argv);\n+}\n+\n void argv_array_pop(struct argv_array *array)\n {\n \tif (!array->argc)\ndiff --git a/argv-array.h b/argv-array.h\nindex c65e6e8..a2fa0aa 100644\n--- a/argv-array.h\n+++ b/argv-array.h\n@@ -17,6 +17,7 @@ __attribute__((format (printf,2,3)))\n void argv_array_pushf(struct argv_array *, const char *fmt, ...);\n LAST_ARG_MUST_BE_NULL\n void argv_array_pushl(struct argv_array *, ...);\n+void argv_array_pushv(struct argv_array *, const char **);\n void argv_array_pop(struct argv_array *);\n void argv_array_clear(struct argv_array *);\n \n-- \n2.1.4\n"},{"id":"264121","messageId":"1434624852-6869-5-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 04/19] pull: implement skeletal builtin pull","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:57Z","receivedAt":"2015-06-18T10:53:57Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"For the purpose of rewriting git-pull.sh into a C builtin, implement a\nskeletal builtin/pull.c that redirects to $GIT_EXEC_PATH/git-pull.sh if\nthe environment variable _GIT_USE_BUILTIN_PULL is not defined. This\nallows us to fall back on the functional git-pull.sh when running the\ntest suite for tests that depend on a working git-pull implementation.\n\nThis redirection should be removed when all the features of git-pull.sh\nhave been re-implemented in builtin/pull.c.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Makefile       |  1 +\n builtin.h      |  1 +\n builtin/pull.c | 33 +++++++++++++++++++++++++++++++++\n git.c          |  1 +\n 4 files changed, 36 insertions(+)\n create mode 100644 builtin/pull.c\n\ndiff --git a/Makefile b/Makefile\nindex 149f1c7..17e1136 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -877,6 +877,7 @@ BUILTIN_OBJS += builtin/pack-refs.o\n BUILTIN_OBJS += builtin/patch-id.o\n BUILTIN_OBJS += builtin/prune-packed.o\n BUILTIN_OBJS += builtin/prune.o\n+BUILTIN_OBJS += builtin/pull.o\n BUILTIN_OBJS += builtin/push.o\n BUILTIN_OBJS += builtin/read-tree.o\n BUILTIN_OBJS += builtin/receive-pack.o\ndiff --git a/builtin.h b/builtin.h\nindex b87df70..ea3c834 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -98,6 +98,7 @@ extern int cmd_pack_redundant(int argc, const char **argv, const char *prefix);\n extern int cmd_patch_id(int argc, const char **argv, const char *prefix);\n extern int cmd_prune(int argc, const char **argv, const char *prefix);\n extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);\n+extern int cmd_pull(int argc, const char **argv, const char *prefix);\n extern int cmd_push(int argc, const char **argv, const char *prefix);\n extern int cmd_read_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/pull.c b/builtin/pull.c\nnew file mode 100644\nindex 0000000..cabeed4\n--- /dev/null\n+++ b/builtin/pull.c\n@@ -0,0 +1,33 @@\n+/*\n+ * Builtin \"git pull\"\n+ *\n+ * Based on git-pull.sh by Junio C Hamano\n+ *\n+ * Fetch one or more remote refs and merge it/them into the current HEAD.\n+ */\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"parse-options.h\"\n+#include \"exec_cmd.h\"\n+\n+static const char * const pull_usage[] = {\n+\tNULL\n+};\n+\n+static struct option pull_options[] = {\n+\tOPT_END()\n+};\n+\n+int cmd_pull(int argc, const char **argv, const char *prefix)\n+{\n+\tif (!getenv(\"_GIT_USE_BUILTIN_PULL\")) {\n+\t\tconst char *path = mkpath(\"%s/git-pull\", git_exec_path());\n+\n+\t\tif (sane_execvp(path, (char **)argv) < 0)\n+\t\t\tdie_errno(\"could not exec %s\", path);\n+\t}\n+\n+\targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n+\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 44374b1..e7a7713 100644\n--- a/git.c\n+++ b/git.c\n@@ -445,6 +445,7 @@ static struct cmd_struct commands[] = {\n \t{ \"pickaxe\", cmd_blame, RUN_SETUP },\n \t{ \"prune\", cmd_prune, RUN_SETUP },\n \t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n+\t{ \"pull\", cmd_pull, RUN_SETUP | NEED_WORK_TREE },\n \t{ \"push\", cmd_push, RUN_SETUP },\n \t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n \t{ \"receive-pack\", cmd_receive_pack },\n-- \n2.1.4\n"},{"id":"264120","messageId":"1434624852-6869-6-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 05/19] pull: implement fetch + merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:58Z","receivedAt":"2015-06-18T10:53:58Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Implement the fetch + merge functionality of git-pull, by first running\ngit-fetch with the repo and refspecs provided on the command line, then\nrunning git-merge on FETCH_HEAD to merge the fetched refs into the\ncurrent branch.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 61 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex cabeed4..9157536 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -9,8 +9,10 @@\n #include \"builtin.h\"\n #include \"parse-options.h\"\n #include \"exec_cmd.h\"\n+#include \"run-command.h\"\n \n static const char * const pull_usage[] = {\n+\tN_(\"git pull [options] [<repository> [<refspec>...]]\"),\n \tNULL\n };\n \n@@ -18,8 +20,61 @@ static struct option pull_options[] = {\n \tOPT_END()\n };\n \n+/**\n+ * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`\n+ * as a string and `refspecs` as a null-terminated array of strings. If `repo`\n+ * is not provided in argv, it is set to NULL.\n+ */\n+static void parse_repo_refspecs(int argc, const char **argv, const char **repo,\n+\t\tconst char ***refspecs)\n+{\n+\tif (argc > 0) {\n+\t\t*repo = *argv++;\n+\t\targc--;\n+\t} else\n+\t\t*repo = NULL;\n+\t*refspecs = argv;\n+}\n+\n+/**\n+ * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the\n+ * repository and refspecs to fetch, or NULL if they are not provided.\n+ */\n+static int run_fetch(const char *repo, const char **refspecs)\n+{\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n+\tint ret;\n+\n+\targv_array_pushl(&args, \"fetch\", \"--update-head-ok\", NULL);\n+\tif (repo) {\n+\t\targv_array_push(&args, repo);\n+\t\targv_array_pushv(&args, refspecs);\n+\t} else if (*refspecs)\n+\t\tdie(\"BUG: refspecs without repo?\");\n+\tret = run_command_v_opt(args.argv, RUN_GIT_CMD);\n+\targv_array_clear(&args);\n+\treturn ret;\n+}\n+\n+/**\n+ * Runs git-merge, returning its exit status.\n+ */\n+static int run_merge(void)\n+{\n+\tint ret;\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n+\n+\targv_array_pushl(&args, \"merge\", NULL);\n+\targv_array_push(&args, \"FETCH_HEAD\");\n+\tret = run_command_v_opt(args.argv, RUN_GIT_CMD);\n+\targv_array_clear(&args);\n+\treturn ret;\n+}\n+\n int cmd_pull(int argc, const char **argv, const char *prefix)\n {\n+\tconst char *repo, **refspecs;\n+\n \tif (!getenv(\"_GIT_USE_BUILTIN_PULL\")) {\n \t\tconst char *path = mkpath(\"%s/git-pull\", git_exec_path());\n \n@@ -29,5 +84,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \n-\treturn 0;\n+\tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n+\n+\tif (run_fetch(repo, refspecs))\n+\t\treturn 1;\n+\n+\treturn run_merge();\n }\n-- \n2.1.4\n"},{"id":"264122","messageId":"1434624852-6869-7-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 06/19] pull: pass verbosity, --progress flags to fetch and merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:53:59Z","receivedAt":"2015-06-18T10:53:59Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"7f87aff (Teach/Fix pull/fetch -q/-v options, 2008-11-15) taught git-pull\nto accept the verbosity -v and -q options and pass them to git-fetch and\ngit-merge.\n\nRe-implement support for the verbosity flags by adding it to the options\nlist and introducing argv_push_verbosity() to push the flags into the\nargv array used to execute git-fetch and git-merge.\n\n9839018 (fetch and pull: learn --progress, 2010-02-24) and bebd2fd\n(pull: propagate --progress to merge, 2011-02-20) taught git-pull to\naccept the --progress option and pass it to git-fetch and git-merge.\n\nUse OPT_PASSTHRU() implemented earlier to pass the \"--[no-]progress\"\ncommand line options to git-fetch and git-merge.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 36 ++++++++++++++++++++++++++++++++++++\n 1 file changed, 36 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 9157536..5d9f2b5 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -16,11 +16,35 @@ static const char * const pull_usage[] = {\n \tNULL\n };\n \n+/* Shared options */\n+static int opt_verbosity;\n+static char *opt_progress;\n+\n static struct option pull_options[] = {\n+\t/* Shared options */\n+\tOPT__VERBOSITY(&opt_verbosity),\n+\tOPT_PASSTHRU(0, \"progress\", &opt_progress, NULL,\n+\t\tN_(\"force progress reporting\"),\n+\t\tPARSE_OPT_NOARG),\n+\n \tOPT_END()\n };\n \n /**\n+ * Pushes \"-q\" or \"-v\" switches into arr to match the opt_verbosity level.\n+ */\n+static void argv_push_verbosity(struct argv_array *arr)\n+{\n+\tint verbosity;\n+\n+\tfor (verbosity = opt_verbosity; verbosity > 0; verbosity--)\n+\t\targv_array_push(arr, \"-v\");\n+\n+\tfor (verbosity = opt_verbosity; verbosity < 0; verbosity++)\n+\t\targv_array_push(arr, \"-q\");\n+}\n+\n+/**\n  * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`\n  * as a string and `refspecs` as a null-terminated array of strings. If `repo`\n  * is not provided in argv, it is set to NULL.\n@@ -46,6 +70,12 @@ static int run_fetch(const char *repo, const char **refspecs)\n \tint ret;\n \n \targv_array_pushl(&args, \"fetch\", \"--update-head-ok\", NULL);\n+\n+\t/* Shared options */\n+\targv_push_verbosity(&args);\n+\tif (opt_progress)\n+\t\targv_array_push(&args, opt_progress);\n+\n \tif (repo) {\n \t\targv_array_push(&args, repo);\n \t\targv_array_pushv(&args, refspecs);\n@@ -65,6 +95,12 @@ static int run_merge(void)\n \tstruct argv_array args = ARGV_ARRAY_INIT;\n \n \targv_array_pushl(&args, \"merge\", NULL);\n+\n+\t/* Shared options */\n+\targv_push_verbosity(&args);\n+\tif (opt_progress)\n+\t\targv_array_push(&args, opt_progress);\n+\n \targv_array_push(&args, \"FETCH_HEAD\");\n \tret = run_command_v_opt(args.argv, RUN_GIT_CMD);\n \targv_array_clear(&args);\n-- \n2.1.4\n"},{"id":"264123","messageId":"1434624852-6869-8-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 07/19] pull: pass git-merge's options to git-merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:00Z","receivedAt":"2015-06-18T10:54:00Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Specify git-merge's options in the option list, and pass any specified\noptions to git-merge.\n\nThese options are:\n\n* -n, --stat, --summary: since d8abe14 (merge, pull: introduce\n  '--(no-)stat' option, 2008-04-06)\n\n* --log: since efb779f (merge, pull: add '--(no-)log' command line\n  option, 2008-04-06)\n\n* --squash: since 7d0c688 (git-merge --squash, 2006-06-23)\n\n* --commit: since 5072a32 (Teach git-pull about --[no-]ff, --no-squash\n  and --commit, 2007-10-29)\n\n* --edit: since 8580830 (\"git pull\" doesn't know \"--edit\", 2012-02-11)\n\n* --ff, --ff-only: since 5072a32 (Teach git-pull about --[no-]ff,\n  --no-squash and --commit, 2007-10-29)\n\n* --verify-signatures: since efed002 (merge/pull: verify GPG signatures\n  of commits being merged, 2013-03-31)\n\n* -s, --strategy: since 60fb5b2 (Use git-merge in git-pull (second\n  try)., 2005-09-25)\n\n* -X, --strategy-option: since ee2c795 (Teach git-pull to pass\n  -X<option> to git-merge, 2009-11-25)\n\n* -S, --gpg-sign: since ea230d8 (pull: add the --gpg-sign option.,\n  2014-02-10)\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 75 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 5d9f2b5..0442da9 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -20,6 +20,18 @@ static const char * const pull_usage[] = {\n static int opt_verbosity;\n static char *opt_progress;\n \n+/* Options passed to git-merge */\n+static char *opt_diffstat;\n+static char *opt_log;\n+static char *opt_squash;\n+static char *opt_commit;\n+static char *opt_edit;\n+static char *opt_ff;\n+static char *opt_verify_signatures;\n+static struct argv_array opt_strategies = ARGV_ARRAY_INIT;\n+static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;\n+static char *opt_gpg_sign;\n+\n static struct option pull_options[] = {\n \t/* Shared options */\n \tOPT__VERBOSITY(&opt_verbosity),\n@@ -27,6 +39,49 @@ static struct option pull_options[] = {\n \t\tN_(\"force progress reporting\"),\n \t\tPARSE_OPT_NOARG),\n \n+\t/* Options passed to git-merge */\n+\tOPT_GROUP(N_(\"Options related to merging\")),\n+\tOPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,\n+\t\tN_(\"do not show a diffstat at the end of the merge\"),\n+\t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG),\n+\tOPT_PASSTHRU(0, \"stat\", &opt_diffstat, NULL,\n+\t\tN_(\"show a diffstat at the end of the merge\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"summary\", &opt_diffstat, NULL,\n+\t\tN_(\"(synonym to --stat)\"),\n+\t\tPARSE_OPT_NOARG | PARSE_OPT_HIDDEN),\n+\tOPT_PASSTHRU(0, \"log\", &opt_log, N_(\"n\"),\n+\t\tN_(\"add (at most <n>) entries from shortlog to merge commit message\"),\n+\t\tPARSE_OPT_OPTARG),\n+\tOPT_PASSTHRU(0, \"squash\", &opt_squash, NULL,\n+\t\tN_(\"create a single commit instead of doing a merge\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"commit\", &opt_commit, NULL,\n+\t\tN_(\"perform a commit if the merge succeeds (default)\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"edit\", &opt_edit, NULL,\n+\t\tN_(\"edit message before committing\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"ff\", &opt_ff, NULL,\n+\t\tN_(\"allow fast-forward\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"ff-only\", &opt_ff, NULL,\n+\t\tN_(\"abort if fast-forward is not possible\"),\n+\t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG),\n+\tOPT_PASSTHRU(0, \"verify-signatures\", &opt_verify_signatures, NULL,\n+\t\tN_(\"verify that the named commit has a valid GPG signature\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n+\t\tN_(\"merge strategy to use\"),\n+\t\t0),\n+\tOPT_PASSTHRU_ARGV('X', \"strategy-option\", &opt_strategy_opts,\n+\t\tN_(\"option=value\"),\n+\t\tN_(\"option for selected merge strategy\"),\n+\t\t0),\n+\tOPT_PASSTHRU('S', \"gpg-sign\", &opt_gpg_sign, N_(\"key-id\"),\n+\t\tN_(\"GPG sign commit\"),\n+\t\tPARSE_OPT_OPTARG),\n+\n \tOPT_END()\n };\n \n@@ -101,6 +156,26 @@ static int run_merge(void)\n \tif (opt_progress)\n \t\targv_array_push(&args, opt_progress);\n \n+\t/* Options passed to git-merge */\n+\tif (opt_diffstat)\n+\t\targv_array_push(&args, opt_diffstat);\n+\tif (opt_log)\n+\t\targv_array_push(&args, opt_log);\n+\tif (opt_squash)\n+\t\targv_array_push(&args, opt_squash);\n+\tif (opt_commit)\n+\t\targv_array_push(&args, opt_commit);\n+\tif (opt_edit)\n+\t\targv_array_push(&args, opt_edit);\n+\tif (opt_ff)\n+\t\targv_array_push(&args, opt_ff);\n+\tif (opt_verify_signatures)\n+\t\targv_array_push(&args, opt_verify_signatures);\n+\targv_array_pushv(&args, opt_strategies.argv);\n+\targv_array_pushv(&args, opt_strategy_opts.argv);\n+\tif (opt_gpg_sign)\n+\t\targv_array_push(&args, opt_gpg_sign);\n+\n \targv_array_push(&args, \"FETCH_HEAD\");\n \tret = run_command_v_opt(args.argv, RUN_GIT_CMD);\n \targv_array_clear(&args);\n-- \n2.1.4\n"},{"id":"264124","messageId":"1434624852-6869-9-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 08/19] pull: pass git-fetch's options to git-fetch","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:01Z","receivedAt":"2015-06-18T10:54:01Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since eb2a8d9 (pull: handle git-fetch's options as well, 2015-06-02),\ngit-pull knows about and handles git-fetch's options, passing them to\ngit-fetch. Re-implement this behavior.\n\nSince 29609e6 (pull: do nothing on --dry-run, 2010-05-25) git-pull\nsupported the --dry-run option, exiting after git-fetch if --dry-run is\nset. Re-implement this behavior.\n\nSigned-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 95 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 0442da9..f35649c 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -32,6 +32,21 @@ static struct argv_array opt_strategies = ARGV_ARRAY_INIT;\n static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;\n static char *opt_gpg_sign;\n \n+/* Options passed to git-fetch */\n+static char *opt_all;\n+static char *opt_append;\n+static char *opt_upload_pack;\n+static int opt_force;\n+static char *opt_tags;\n+static char *opt_prune;\n+static char *opt_recurse_submodules;\n+static int opt_dry_run;\n+static char *opt_keep;\n+static char *opt_depth;\n+static char *opt_unshallow;\n+static char *opt_update_shallow;\n+static char *opt_refmap;\n+\n static struct option pull_options[] = {\n \t/* Shared options */\n \tOPT__VERBOSITY(&opt_verbosity),\n@@ -82,6 +97,46 @@ static struct option pull_options[] = {\n \t\tN_(\"GPG sign commit\"),\n \t\tPARSE_OPT_OPTARG),\n \n+\t/* Options passed to git-fetch */\n+\tOPT_GROUP(N_(\"Options related to fetching\")),\n+\tOPT_PASSTHRU(0, \"all\", &opt_all, NULL,\n+\t\tN_(\"fetch from all remotes\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU('a', \"append\", &opt_append, NULL,\n+\t\tN_(\"append to .git/FETCH_HEAD instead of overwriting\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"upload-pack\", &opt_upload_pack, N_(\"path\"),\n+\t\tN_(\"path to upload pack on remote end\"),\n+\t\t0),\n+\tOPT__FORCE(&opt_force, N_(\"force overwrite of local branch\")),\n+\tOPT_PASSTHRU('t', \"tags\", &opt_tags, NULL,\n+\t\tN_(\"fetch all tags and associated objects\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU('p', \"prune\", &opt_prune, NULL,\n+\t\tN_(\"prune remote-tracking branches no longer on remote\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"recurse-submodules\", &opt_recurse_submodules,\n+\t\tN_(\"on-demand\"),\n+\t\tN_(\"control recursive fetching of submodules\"),\n+\t\tPARSE_OPT_OPTARG),\n+\tOPT_BOOL(0, \"dry-run\", &opt_dry_run,\n+\t\tN_(\"dry run\")),\n+\tOPT_PASSTHRU('k', \"keep\", &opt_keep, NULL,\n+\t\tN_(\"keep downloaded pack\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"depth\", &opt_depth, N_(\"depth\"),\n+\t\tN_(\"deepen history of shallow clone\"),\n+\t\t0),\n+\tOPT_PASSTHRU(0, \"unshallow\", &opt_unshallow, NULL,\n+\t\tN_(\"convert to a complete repository\"),\n+\t\tPARSE_OPT_NONEG | PARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"update-shallow\", &opt_update_shallow, NULL,\n+\t\tN_(\"accept refs that update .git/shallow\"),\n+\t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"refmap\", &opt_refmap, N_(\"refmap\"),\n+\t\tN_(\"specify fetch refmap\"),\n+\t\tPARSE_OPT_NONEG),\n+\n \tOPT_END()\n };\n \n@@ -100,6 +155,16 @@ static void argv_push_verbosity(struct argv_array *arr)\n }\n \n /**\n+ * Pushes \"-f\" switches into arr to match the opt_force level.\n+ */\n+static void argv_push_force(struct argv_array *arr)\n+{\n+\tint force = opt_force;\n+\twhile (force-- > 0)\n+\t\targv_array_push(arr, \"-f\");\n+}\n+\n+/**\n  * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`\n  * as a string and `refspecs` as a null-terminated array of strings. If `repo`\n  * is not provided in argv, it is set to NULL.\n@@ -131,6 +196,33 @@ static int run_fetch(const char *repo, const char **refspecs)\n \tif (opt_progress)\n \t\targv_array_push(&args, opt_progress);\n \n+\t/* Options passed to git-fetch */\n+\tif (opt_all)\n+\t\targv_array_push(&args, opt_all);\n+\tif (opt_append)\n+\t\targv_array_push(&args, opt_append);\n+\tif (opt_upload_pack)\n+\t\targv_array_push(&args, opt_upload_pack);\n+\targv_push_force(&args);\n+\tif (opt_tags)\n+\t\targv_array_push(&args, opt_tags);\n+\tif (opt_prune)\n+\t\targv_array_push(&args, opt_prune);\n+\tif (opt_recurse_submodules)\n+\t\targv_array_push(&args, opt_recurse_submodules);\n+\tif (opt_dry_run)\n+\t\targv_array_push(&args, \"--dry-run\");\n+\tif (opt_keep)\n+\t\targv_array_push(&args, opt_keep);\n+\tif (opt_depth)\n+\t\targv_array_push(&args, opt_depth);\n+\tif (opt_unshallow)\n+\t\targv_array_push(&args, opt_unshallow);\n+\tif (opt_update_shallow)\n+\t\targv_array_push(&args, opt_update_shallow);\n+\tif (opt_refmap)\n+\t\targv_array_push(&args, opt_refmap);\n+\n \tif (repo) {\n \t\targv_array_push(&args, repo);\n \t\targv_array_pushv(&args, refspecs);\n@@ -200,5 +292,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (run_fetch(repo, refspecs))\n \t\treturn 1;\n \n+\tif (opt_dry_run)\n+\t\treturn 0;\n+\n \treturn run_merge();\n }\n-- \n2.1.4\n"},{"id":"264127","messageId":"1434624852-6869-10-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 09/19] pull: error on no merge candidates","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:02Z","receivedAt":"2015-06-18T10:54:02Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Commit a8c9bef (pull: improve advice for unconfigured error case,\n2009-10-05) fully established the current advices given by git-pull for\nthe different cases where git-fetch will not have anything marked for\nmerge:\n\n1. We fetched from a specific remote, and a refspec was given, but it\n   ended up not fetching anything. This is usually because the user\n   provided a wildcard refspec which had no matches on the remote end.\n\n2. We fetched from a non-default remote, but didn't specify a branch to\n   merge. We can't use the configured one because it applies to the\n   default remote, and thus the user must specify the branches to merge.\n\n3. We fetched from the branch's or repo's default remote, but:\n\n   a. We are not on a branch, so there will never be a configured branch\n      to merge with.\n\n   b. We are on a branch, but there is no configured branch to merge\n      with.\n\n4. We fetched from the branch's or repo's default remote, but the\n   configured branch to merge didn't get fetched (either it doesn't\n   exist, or wasn't part of the configured fetch refspec)\n\nRe-implement the above behavior by implementing get_merge_heads() to\nparse the heads in FETCH_HEAD for merging, and implementing\ndie_no_merge_candidates(), which will be called when FETCH_HEAD has no\nheads for merging.\n\nHelped-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 113 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex f35649c..647bcb9 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -10,6 +10,8 @@\n #include \"parse-options.h\"\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n+#include \"sha1-array.h\"\n+#include \"remote.h\"\n \n static const char * const pull_usage[] = {\n \tN_(\"git pull [options] [<repository> [<refspec>...]]\"),\n@@ -165,6 +167,111 @@ static void argv_push_force(struct argv_array *arr)\n }\n \n /**\n+ * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge\n+ * into merge_heads.\n+ */\n+static void get_merge_heads(struct sha1_array *merge_heads)\n+{\n+\tconst char *filename = git_path(\"FETCH_HEAD\");\n+\tFILE *fp;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tunsigned char sha1[GIT_SHA1_RAWSZ];\n+\n+\tif (!(fp = fopen(filename, \"r\")))\n+\t\tdie_errno(_(\"could not open '%s' for reading\"), filename);\n+\twhile (strbuf_getline(&sb, fp, '\\n') != EOF) {\n+\t\tif (get_sha1_hex(sb.buf, sha1))\n+\t\t\tcontinue;  /* invalid line: does not start with SHA1 */\n+\t\tif (starts_with(sb.buf + GIT_SHA1_HEXSZ, \"\\tnot-for-merge\\t\"))\n+\t\t\tcontinue;  /* ref is not-for-merge */\n+\t\tsha1_array_append(merge_heads, sha1);\n+\t}\n+\tfclose(fp);\n+\tstrbuf_release(&sb);\n+}\n+\n+/**\n+ * Used by die_no_merge_candidates() as a for_each_remote() callback to\n+ * retrieve the name of the remote if the repository only has one remote.\n+ */\n+static int get_only_remote(struct remote *remote, void *cb_data)\n+{\n+\tconst char **remote_name = cb_data;\n+\n+\tif (*remote_name)\n+\t\treturn -1;\n+\n+\t*remote_name = remote->name;\n+\treturn 0;\n+}\n+\n+/**\n+ * Dies with the appropriate reason for why there are no merge candidates:\n+ *\n+ * 1. We fetched from a specific remote, and a refspec was given, but it ended\n+ *    up not fetching anything. This is usually because the user provided a\n+ *    wildcard refspec which had no matches on the remote end.\n+ *\n+ * 2. We fetched from a non-default remote, but didn't specify a branch to\n+ *    merge. We can't use the configured one because it applies to the default\n+ *    remote, thus the user must specify the branches to merge.\n+ *\n+ * 3. We fetched from the branch's or repo's default remote, but:\n+ *\n+ *    a. We are not on a branch, so there will never be a configured branch to\n+ *       merge with.\n+ *\n+ *    b. We are on a branch, but there is no configured branch to merge with.\n+ *\n+ * 4. We fetched from the branch's or repo's default remote, but the configured\n+ *    branch to merge didn't get fetched. (Either it doesn't exist, or wasn't\n+ *    part of the configured fetch refspec.)\n+ */\n+static void NORETURN die_no_merge_candidates(const char *repo, const char **refspecs)\n+{\n+\tstruct branch *curr_branch = branch_get(\"HEAD\");\n+\tconst char *remote = curr_branch ? curr_branch->remote_name : NULL;\n+\n+\tif (*refspecs) {\n+\t\tfprintf_ln(stderr, _(\"There are no candidates for merging among the refs that you just fetched.\"));\n+\t\tfprintf_ln(stderr, _(\"Generally this means that you provided a wildcard refspec which had no\\n\"\n+\t\t\t\t\t\"matches on the remote end.\"));\n+\t} else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {\n+\t\tfprintf_ln(stderr, _(\"You asked to pull from the remote '%s', but did not specify\\n\"\n+\t\t\t\"a branch. Because this is not the default configured remote\\n\"\n+\t\t\t\"for your current branch, you must specify a branch on the command line.\"),\n+\t\t\trepo);\n+\t} else if (!curr_branch) {\n+\t\tfprintf_ln(stderr, _(\"You are not currently on a branch.\"));\n+\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to merge with.\"));\n+\t\tfprintf_ln(stderr, _(\"See git-pull(1) for details.\"));\n+\t\tfprintf(stderr, \"\\n\");\n+\t\tfprintf_ln(stderr, \"    git pull <remote> <branch>\");\n+\t\tfprintf(stderr, \"\\n\");\n+\t} else if (!curr_branch->merge_nr) {\n+\t\tconst char *remote_name = NULL;\n+\n+\t\tif (for_each_remote(get_only_remote, &remote_name) || !remote_name)\n+\t\t\tremote_name = \"<remote>\";\n+\n+\t\tfprintf_ln(stderr, _(\"There is no tracking information for the current branch.\"));\n+\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to merge with.\"));\n+\t\tfprintf_ln(stderr, _(\"See git-pull(1) for details.\"));\n+\t\tfprintf(stderr, \"\\n\");\n+\t\tfprintf_ln(stderr, \"    git pull <remote> <branch>\");\n+\t\tfprintf(stderr, \"\\n\");\n+\t\tfprintf_ln(stderr, _(\"If you wish to set tracking information for this branch you can do so with:\\n\"\n+\t\t\t\t\"\\n\"\n+\t\t\t\t\"    git branch --set-upstream-to=%s/<branch> %s\\n\"),\n+\t\t\t\tremote_name, curr_branch->name);\n+\t} else\n+\t\tfprintf_ln(stderr, _(\"Your configuration specifies to merge with the ref '%s'\\n\"\n+\t\t\t\"from the remote, but no such ref was fetched.\"),\n+\t\t\t*curr_branch->merge_name);\n+\texit(1);\n+}\n+\n+/**\n  * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`\n  * as a string and `refspecs` as a null-terminated array of strings. If `repo`\n  * is not provided in argv, it is set to NULL.\n@@ -277,6 +384,7 @@ static int run_merge(void)\n int cmd_pull(int argc, const char **argv, const char *prefix)\n {\n \tconst char *repo, **refspecs;\n+\tstruct sha1_array merge_heads = SHA1_ARRAY_INIT;\n \n \tif (!getenv(\"_GIT_USE_BUILTIN_PULL\")) {\n \t\tconst char *path = mkpath(\"%s/git-pull\", git_exec_path());\n@@ -295,5 +403,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (opt_dry_run)\n \t\treturn 0;\n \n+\tget_merge_heads(&merge_heads);\n+\n+\tif (!merge_heads.nr)\n+\t\tdie_no_merge_candidates(repo, refspecs);\n+\n \treturn run_merge();\n }\n-- \n2.1.4\n"},{"id":"264128","messageId":"1434624852-6869-11-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 10/19] pull: support pull.ff config","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:03Z","receivedAt":"2015-06-18T10:54:03Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since b814da8 (pull: add pull.ff configuration, 2014-01-15), git-pull.sh\nwould lookup the configuration value of \"pull.ff\", and set the flag\n\"--ff\" if its value is \"true\", \"--no-ff\" if its value is \"false\" and\n\"--ff-only\" if its value is \"only\".\n\nRe-implement this behavior.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 29 +++++++++++++++++++++++++++++\n 1 file changed, 29 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 647bcb9..b61cff5 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -167,6 +167,32 @@ static void argv_push_force(struct argv_array *arr)\n }\n \n /**\n+ * If pull.ff is unset, returns NULL. If pull.ff is \"true\", returns \"--ff\". If\n+ * pull.ff is \"false\", returns \"--no-ff\". If pull.ff is \"only\", returns\n+ * \"--ff-only\". Otherwise, if pull.ff is set to an invalid value, die with an\n+ * error.\n+ */\n+static const char *config_get_ff(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"pull.ff\", &value))\n+\t\treturn NULL;\n+\n+\tswitch (git_config_maybe_bool(\"pull.ff\", value)) {\n+\tcase 0:\n+\t\treturn \"--no-ff\";\n+\tcase 1:\n+\t\treturn \"--ff\";\n+\t}\n+\n+\tif (!strcmp(value, \"only\"))\n+\t\treturn \"--ff-only\";\n+\n+\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n+}\n+\n+/**\n  * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge\n  * into merge_heads.\n  */\n@@ -397,6 +423,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n \n+\tif (!opt_ff)\n+\t\topt_ff = xstrdup_or_null(config_get_ff());\n+\n \tif (run_fetch(repo, refspecs))\n \t\treturn 1;\n \n-- \n2.1.4\n"},{"id":"264129","messageId":"1434624852-6869-12-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 11/19] pull: check if in unresolved merge state","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:04Z","receivedAt":"2015-06-18T10:54:04Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since d38a30d (Be more user-friendly when refusing to do something\nbecause of conflict., 2010-01-12), git-pull will error out with\nuser-friendly advices if the user is in the middle of a merge or has\nunmerged files.\n\nRe-implement this behavior. While the \"has unmerged files\" case can be\nhandled by die_resolve_conflict(), we introduce a new function\ndie_conclude_merge() for printing a different error message for when\nthere are no unmerged files but the merge has not been finished.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n advice.c       | 8 ++++++++\n advice.h       | 1 +\n builtin/pull.c | 9 +++++++++\n 3 files changed, 18 insertions(+)\n\ndiff --git a/advice.c b/advice.c\nindex 575bec2..4965686 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -96,6 +96,14 @@ void NORETURN die_resolve_conflict(const char *me)\n \tdie(\"Exiting because of an unresolved conflict.\");\n }\n \n+void NORETURN die_conclude_merge(void)\n+{\n+\terror(_(\"You have not concluded your merge (MERGE_HEAD exists).\"));\n+\tif (advice_resolve_conflict)\n+\t\tadvise(_(\"Please, commit your changes before you can merge.\"));\n+\tdie(_(\"Exiting because of unfinished merge.\"));\n+}\n+\n void detach_advice(const char *new_name)\n {\n \tconst char fmt[] =\ndiff --git a/advice.h b/advice.h\nindex 5ecc6c1..b341a55 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -24,6 +24,7 @@ __attribute__((format (printf, 1, 2)))\n void advise(const char *advice, ...);\n int error_resolve_conflict(const char *me);\n extern void NORETURN die_resolve_conflict(const char *me);\n+void NORETURN die_conclude_merge(void);\n void detach_advice(const char *new_name);\n \n #endif /* ADVICE_H */\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex b61cff5..1e688be 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -12,6 +12,7 @@\n #include \"run-command.h\"\n #include \"sha1-array.h\"\n #include \"remote.h\"\n+#include \"dir.h\"\n \n static const char * const pull_usage[] = {\n \tN_(\"git pull [options] [<repository> [<refspec>...]]\"),\n@@ -426,6 +427,14 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (!opt_ff)\n \t\topt_ff = xstrdup_or_null(config_get_ff());\n \n+\tgit_config(git_default_config, NULL);\n+\n+\tif (read_cache_unmerged())\n+\t\tdie_resolve_conflict(\"Pull\");\n+\n+\tif (file_exists(git_path(\"MERGE_HEAD\")))\n+\t\tdie_conclude_merge();\n+\n \tif (run_fetch(repo, refspecs))\n \t\treturn 1;\n \n-- \n2.1.4\n"},{"id":"264125","messageId":"1434624852-6869-13-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 12/19] pull: fast-forward working tree if head is updated","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:05Z","receivedAt":"2015-06-18T10:54:05Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since b10ac50 (Fix pulling into the same branch., 2005-08-25), git-pull,\nupon detecting that git-fetch updated the current head, will\nfast-forward the working tree to the updated head commit.\n\nRe-implement this behavior.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 30 ++++++++++++++++++++++++++++++\n 1 file changed, 30 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 1e688be..110e719 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -412,6 +412,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n {\n \tconst char *repo, **refspecs;\n \tstruct sha1_array merge_heads = SHA1_ARRAY_INIT;\n+\tunsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];\n \n \tif (!getenv(\"_GIT_USE_BUILTIN_PULL\")) {\n \t\tconst char *path = mkpath(\"%s/git-pull\", git_exec_path());\n@@ -435,12 +436,41 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (file_exists(git_path(\"MERGE_HEAD\")))\n \t\tdie_conclude_merge();\n \n+\tif (get_sha1(\"HEAD\", orig_head))\n+\t\thashclr(orig_head);\n+\n \tif (run_fetch(repo, refspecs))\n \t\treturn 1;\n \n \tif (opt_dry_run)\n \t\treturn 0;\n \n+\tif (get_sha1(\"HEAD\", curr_head))\n+\t\thashclr(curr_head);\n+\n+\tif (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) &&\n+\t\t\thashcmp(orig_head, curr_head)) {\n+\t\t/*\n+\t\t * The fetch involved updating the current branch.\n+\t\t *\n+\t\t * The working tree and the index file are still based on\n+\t\t * orig_head commit, but we are merging into curr_head.\n+\t\t * Update the working tree to match curr_head.\n+\t\t */\n+\n+\t\twarning(_(\"fetch updated the current branch head.\\n\"\n+\t\t\t\"fast-forwarding your working tree from\\n\"\n+\t\t\t\"commit %s.\"), sha1_to_hex(orig_head));\n+\n+\t\tif (checkout_fast_forward(orig_head, curr_head, 0))\n+\t\t\tdie(_(\"Cannot fast-forward your working tree.\\n\"\n+\t\t\t\t\"After making sure that you saved anything precious from\\n\"\n+\t\t\t\t\"$ git diff %s\\n\"\n+\t\t\t\t\"output, run\\n\"\n+\t\t\t\t\"$ git reset --hard\\n\"\n+\t\t\t\t\"to recover.\"), sha1_to_hex(orig_head));\n+\t}\n+\n \tget_merge_heads(&merge_heads);\n \n \tif (!merge_heads.nr)\n-- \n2.1.4\n"},{"id":"264126","messageId":"1434624852-6869-14-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 13/19] pull: implement pulling into an unborn branch","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:06Z","receivedAt":"2015-06-18T10:54:06Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"b4dc085 (pull: merge into unborn by fast-forwarding from empty\ntree, 2013-06-20) established git-pull's current behavior of pulling\ninto an unborn branch by fast-forwarding the work tree from an empty\ntree to the merge head, then setting HEAD to the merge head.\n\nRe-implement this behavior by introducing pull_into_void() which will be\ncalled instead of run_merge() if HEAD is invalid.\n\nHelped-by: Stephen Robin <stephen.robin@gmail.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 29 ++++++++++++++++++++++++++++-\n 1 file changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 110e719..492bb0e 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -13,6 +13,7 @@\n #include \"sha1-array.h\"\n #include \"remote.h\"\n #include \"dir.h\"\n+#include \"refs.h\"\n \n static const char * const pull_usage[] = {\n \tN_(\"git pull [options] [<repository> [<refspec>...]]\"),\n@@ -368,6 +369,27 @@ static int run_fetch(const char *repo, const char **refspecs)\n }\n \n /**\n+ * \"Pulls into void\" by branching off merge_head.\n+ */\n+static int pull_into_void(const unsigned char *merge_head,\n+\t\tconst unsigned char *curr_head)\n+{\n+\t/*\n+\t * Two-way merge: we treat the index as based on an empty tree,\n+\t * and try to fast-forward to HEAD. This ensures we will not lose\n+\t * index/worktree changes that the user already made on the unborn\n+\t * branch.\n+\t */\n+\tif (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0))\n+\t\treturn 1;\n+\n+\tif (update_ref(\"initial pull\", \"HEAD\", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+/**\n  * Runs git-merge, returning its exit status.\n  */\n static int run_merge(void)\n@@ -476,5 +498,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (!merge_heads.nr)\n \t\tdie_no_merge_candidates(repo, refspecs);\n \n-\treturn run_merge();\n+\tif (is_null_sha1(orig_head)) {\n+\t\tif (merge_heads.nr > 1)\n+\t\t\tdie(_(\"Cannot merge multiple branches into empty head.\"));\n+\t\treturn pull_into_void(*merge_heads.sha1, curr_head);\n+\t} else\n+\t\treturn run_merge();\n }\n-- \n2.1.4\n"},{"id":"264130","messageId":"1434624852-6869-15-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 14/19] pull: set reflog message","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:07Z","receivedAt":"2015-06-18T10:54:07Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"f947413 (Use GIT_REFLOG_ACTION environment variable instead.,\n2006-12-28) established git-pull's method for setting the reflog\nmessage, which is to set the environment variable GIT_REFLOG_ACTION to\nthe evaluation of \"pull${1+ $*}\" if it has not already been set.\n\nRe-implement this behavior.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 492bb0e..98caffe 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -169,6 +169,25 @@ static void argv_push_force(struct argv_array *arr)\n }\n \n /**\n+ * Sets the GIT_REFLOG_ACTION environment variable to the concatenation of argv\n+ */\n+static void set_reflog_message(int argc, const char **argv)\n+{\n+\tint i;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tif (i)\n+\t\t\tstrbuf_addch(&msg, ' ');\n+\t\tstrbuf_addstr(&msg, argv[i]);\n+\t}\n+\n+\tsetenv(\"GIT_REFLOG_ACTION\", msg.buf, 0);\n+\n+\tstrbuf_release(&msg);\n+}\n+\n+/**\n  * If pull.ff is unset, returns NULL. If pull.ff is \"true\", returns \"--ff\". If\n  * pull.ff is \"false\", returns \"--no-ff\". If pull.ff is \"only\", returns\n  * \"--ff-only\". Otherwise, if pull.ff is set to an invalid value, die with an\n@@ -443,6 +462,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \t\t\tdie_errno(\"could not exec %s\", path);\n \t}\n \n+\tif (!getenv(\"GIT_REFLOG_ACTION\"))\n+\t\tset_reflog_message(argc, argv);\n+\n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n-- \n2.1.4\n"},{"id":"264133","messageId":"1434624852-6869-16-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 15/19] pull: teach git pull about --rebase","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:08Z","receivedAt":"2015-06-18T10:54:08Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since cd67e4d (Teach 'git pull' about --rebase, 2007-11-28), if the\n--rebase option is set, git-rebase is run instead of git-merge.\n\nRe-implement this by introducing run_rebase(), which is called instead\nof run_merge() if opt_rebase is a true value.\n\nSince c85c792 (pull --rebase: be cleverer with rebased upstream\nbranches, 2008-01-26), git-pull handles the case where the upstream\nbranch was rebased since it was last fetched. The fork point (old remote\nref) of the branch from the upstream branch is calculated before fetch,\nand then rebased from onto the new remote head (merge_head) after fetch.\n\nRe-implement this by introducing get_merge_branch_2() and\nget_merge_branch_1() to find the upstream branch for the\nspecified/current branch, and get_rebase_fork_point() which will find\nthe fork point between the upstream branch and current branch.\n\nHowever, the above change created a problem where git-rebase cannot\ndetect commits that are already upstream, and thus may result in\nunnecessary conflicts. cf65426 (pull --rebase: Avoid spurious conflicts\nand reapplying unnecessary patches, 2010-08-12) fixes this by ignoring\nthe above old remote ref if it is contained within the merge base of the\nmerge head and the current branch.\n\nThis is re-implemented in run_rebase() where fork_point is not used if\nit is the merge base returned by get_octopus_merge_base().\n\nHelped-by: Stefan Beller <sbeller@google.com>\nHelped-by: Johannes Schindelin <johannes.schindelin@gmx.de>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 245 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 98caffe..33a7b98 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -15,6 +15,53 @@\n #include \"dir.h\"\n #include \"refs.h\"\n \n+enum rebase_type {\n+\tREBASE_INVALID = -1,\n+\tREBASE_FALSE = 0,\n+\tREBASE_TRUE,\n+\tREBASE_PRESERVE\n+};\n+\n+/**\n+ * Parses the value of --rebase. If value is a false value, returns\n+ * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is\n+ * \"preserve\", returns REBASE_PRESERVE. If value is a invalid value, dies with\n+ * a fatal error if fatal is true, otherwise returns REBASE_INVALID.\n+ */\n+static enum rebase_type parse_config_rebase(const char *key, const char *value,\n+\t\tint fatal)\n+{\n+\tint v = git_config_maybe_bool(\"pull.rebase\", value);\n+\n+\tif (!v)\n+\t\treturn REBASE_FALSE;\n+\telse if (v > 0)\n+\t\treturn REBASE_TRUE;\n+\telse if (!strcmp(value, \"preserve\"))\n+\t\treturn REBASE_PRESERVE;\n+\n+\tif (fatal)\n+\t\tdie(_(\"Invalid value for %s: %s\"), key, value);\n+\telse\n+\t\terror(_(\"Invalid value for %s: %s\"), key, value);\n+\n+\treturn REBASE_INVALID;\n+}\n+\n+/**\n+ * Callback for --rebase, which parses arg with parse_config_rebase().\n+ */\n+static int parse_opt_rebase(const struct option *opt, const char *arg, int unset)\n+{\n+\tenum rebase_type *value = opt->value;\n+\n+\tif (arg)\n+\t\t*value = parse_config_rebase(\"--rebase\", arg, 0);\n+\telse\n+\t\t*value = unset ? REBASE_FALSE : REBASE_TRUE;\n+\treturn *value == REBASE_INVALID ? -1 : 0;\n+}\n+\n static const char * const pull_usage[] = {\n \tN_(\"git pull [options] [<repository> [<refspec>...]]\"),\n \tNULL\n@@ -24,7 +71,8 @@ static const char * const pull_usage[] = {\n static int opt_verbosity;\n static char *opt_progress;\n \n-/* Options passed to git-merge */\n+/* Options passed to git-merge or git-rebase */\n+static enum rebase_type opt_rebase;\n static char *opt_diffstat;\n static char *opt_log;\n static char *opt_squash;\n@@ -58,8 +106,12 @@ static struct option pull_options[] = {\n \t\tN_(\"force progress reporting\"),\n \t\tPARSE_OPT_NOARG),\n \n-\t/* Options passed to git-merge */\n+\t/* Options passed to git-merge or git-rebase */\n \tOPT_GROUP(N_(\"Options related to merging\")),\n+\t{ OPTION_CALLBACK, 'r', \"rebase\", &opt_rebase,\n+\t  N_(\"false|true|preserve\"),\n+\t  N_(\"incorporate changes by rebasing rather than merging\"),\n+\t  PARSE_OPT_OPTARG, parse_opt_rebase },\n \tOPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,\n \t\tN_(\"do not show a diffstat at the end of the merge\"),\n \t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG),\n@@ -449,11 +501,194 @@ static int run_merge(void)\n \treturn ret;\n }\n \n+/**\n+ * Returns remote's upstream branch for the current branch. If remote is NULL,\n+ * the current branch's configured default remote is used. Returns NULL if\n+ * `remote` does not name a valid remote, HEAD does not point to a branch,\n+ * remote is not the branch's configured remote or the branch does not have any\n+ * configured upstream branch.\n+ */\n+static const char *get_upstream_branch(const char *remote)\n+{\n+\tstruct remote *rm;\n+\tstruct branch *curr_branch;\n+\tconst char *curr_branch_remote;\n+\n+\trm = remote_get(remote);\n+\tif (!rm)\n+\t\treturn NULL;\n+\n+\tcurr_branch = branch_get(\"HEAD\");\n+\tif (!curr_branch)\n+\t\treturn NULL;\n+\n+\tcurr_branch_remote = remote_for_branch(curr_branch, NULL);\n+\tassert(curr_branch_remote);\n+\n+\tif (strcmp(curr_branch_remote, rm->name))\n+\t\treturn NULL;\n+\n+\treturn branch_get_upstream(curr_branch, NULL);\n+}\n+\n+/**\n+ * Derives the remote tracking branch from the remote and refspec.\n+ *\n+ * FIXME: The current implementation assumes the default mapping of\n+ * refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>.\n+ */\n+static const char *get_tracking_branch(const char *remote, const char *refspec)\n+{\n+\tstruct refspec *spec;\n+\tconst char *spec_src;\n+\tconst char *merge_branch;\n+\n+\tspec = parse_fetch_refspec(1, &refspec);\n+\tspec_src = spec->src;\n+\tif (!*spec_src || !strcmp(spec_src, \"HEAD\"))\n+\t\tspec_src = \"HEAD\";\n+\telse if (skip_prefix(spec_src, \"heads/\", &spec_src))\n+\t\t;\n+\telse if (skip_prefix(spec_src, \"refs/heads/\", &spec_src))\n+\t\t;\n+\telse if (starts_with(spec_src, \"refs/\") ||\n+\t\tstarts_with(spec_src, \"tags/\") ||\n+\t\tstarts_with(spec_src, \"remotes/\"))\n+\t\tspec_src = \"\";\n+\n+\tif (*spec_src) {\n+\t\tif (!strcmp(remote, \".\"))\n+\t\t\tmerge_branch = mkpath(\"refs/heads/%s\", spec_src);\n+\t\telse\n+\t\t\tmerge_branch = mkpath(\"refs/remotes/%s/%s\", remote, spec_src);\n+\t} else\n+\t\tmerge_branch = NULL;\n+\n+\tfree_refspec(1, spec);\n+\treturn merge_branch;\n+}\n+\n+/**\n+ * Given the repo and refspecs, sets fork_point to the point at which the\n+ * current branch forked from its remote tracking branch. Returns 0 on success,\n+ * -1 on failure.\n+ */\n+static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,\n+\t\tconst char *refspec)\n+{\n+\tint ret;\n+\tstruct branch *curr_branch;\n+\tconst char *remote_branch;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tcurr_branch = branch_get(\"HEAD\");\n+\tif (!curr_branch)\n+\t\treturn -1;\n+\n+\tif (refspec)\n+\t\tremote_branch = get_tracking_branch(repo, refspec);\n+\telse\n+\t\tremote_branch = get_upstream_branch(repo);\n+\n+\tif (!remote_branch)\n+\t\treturn -1;\n+\n+\targv_array_pushl(&cp.args, \"merge-base\", \"--fork-point\",\n+\t\t\tremote_branch, curr_branch->name, NULL);\n+\tcp.no_stdin = 1;\n+\tcp.no_stderr = 1;\n+\tcp.git_cmd = 1;\n+\n+\tret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ);\n+\tif (ret)\n+\t\tgoto cleanup;\n+\n+\tret = get_sha1_hex(sb.buf, fork_point);\n+\tif (ret)\n+\t\tgoto cleanup;\n+\n+cleanup:\n+\tstrbuf_release(&sb);\n+\treturn ret ? -1 : 0;\n+}\n+\n+/**\n+ * Sets merge_base to the octopus merge base of curr_head, merge_head and\n+ * fork_point. Returns 0 if a merge base is found, 1 otherwise.\n+ */\n+static int get_octopus_merge_base(unsigned char *merge_base,\n+\t\tconst unsigned char *curr_head,\n+\t\tconst unsigned char *merge_head,\n+\t\tconst unsigned char *fork_point)\n+{\n+\tstruct commit_list *revs = NULL, *result;\n+\n+\tcommit_list_insert(lookup_commit_reference(curr_head), &revs);\n+\tcommit_list_insert(lookup_commit_reference(merge_head), &revs);\n+\tif (!is_null_sha1(fork_point))\n+\t\tcommit_list_insert(lookup_commit_reference(fork_point), &revs);\n+\n+\tresult = reduce_heads(get_octopus_merge_bases(revs));\n+\tfree_commit_list(revs);\n+\tif (!result)\n+\t\treturn 1;\n+\n+\thashcpy(merge_base, result->item->object.sha1);\n+\treturn 0;\n+}\n+\n+/**\n+ * Given the current HEAD SHA1, the merge head returned from git-fetch and the\n+ * fork point calculated by get_rebase_fork_point(), runs git-rebase with the\n+ * appropriate arguments and returns its exit status.\n+ */\n+static int run_rebase(const unsigned char *curr_head,\n+\t\tconst unsigned char *merge_head,\n+\t\tconst unsigned char *fork_point)\n+{\n+\tint ret;\n+\tunsigned char oct_merge_base[GIT_SHA1_RAWSZ];\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n+\n+\tif (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point))\n+\t\tif (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point))\n+\t\t\tfork_point = NULL;\n+\n+\targv_array_push(&args, \"rebase\");\n+\n+\t/* Shared options */\n+\targv_push_verbosity(&args);\n+\n+\t/* Options passed to git-rebase */\n+\tif (opt_rebase == REBASE_PRESERVE)\n+\t\targv_array_push(&args, \"--preserve-merges\");\n+\tif (opt_diffstat)\n+\t\targv_array_push(&args, opt_diffstat);\n+\targv_array_pushv(&args, opt_strategies.argv);\n+\targv_array_pushv(&args, opt_strategy_opts.argv);\n+\tif (opt_gpg_sign)\n+\t\targv_array_push(&args, opt_gpg_sign);\n+\n+\targv_array_push(&args, \"--onto\");\n+\targv_array_push(&args, sha1_to_hex(merge_head));\n+\n+\tif (fork_point && !is_null_sha1(fork_point))\n+\t\targv_array_push(&args, sha1_to_hex(fork_point));\n+\telse\n+\t\targv_array_push(&args, sha1_to_hex(merge_head));\n+\n+\tret = run_command_v_opt(args.argv, RUN_GIT_CMD);\n+\targv_array_clear(&args);\n+\treturn ret;\n+}\n+\n int cmd_pull(int argc, const char **argv, const char *prefix)\n {\n \tconst char *repo, **refspecs;\n \tstruct sha1_array merge_heads = SHA1_ARRAY_INIT;\n \tunsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];\n+\tunsigned char rebase_fork_point[GIT_SHA1_RAWSZ];\n \n \tif (!getenv(\"_GIT_USE_BUILTIN_PULL\")) {\n \t\tconst char *path = mkpath(\"%s/git-pull\", git_exec_path());\n@@ -483,6 +718,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_sha1(\"HEAD\", orig_head))\n \t\thashclr(orig_head);\n \n+\tif (opt_rebase)\n+\t\tif (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))\n+\t\t\thashclr(rebase_fork_point);\n+\n \tif (run_fetch(repo, refspecs))\n \t\treturn 1;\n \n@@ -524,6 +763,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \t\tif (merge_heads.nr > 1)\n \t\t\tdie(_(\"Cannot merge multiple branches into empty head.\"));\n \t\treturn pull_into_void(*merge_heads.sha1, curr_head);\n+\t} else if (opt_rebase) {\n+\t\tif (merge_heads.nr > 1)\n+\t\t\tdie(_(\"Cannot rebase onto multiple branches.\"));\n+\t\treturn run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);\n \t} else\n \t\treturn run_merge();\n }\n-- \n2.1.4\n"},{"id":"264134","messageId":"1434624852-6869-17-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 16/19] pull: configure --rebase via branch.<name>.rebase or pull.rebase","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:09Z","receivedAt":"2015-06-18T10:54:09Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since cd67e4d (Teach 'git pull' about --rebase, 2007-11-28),\nfetch+rebase could be set by default by defining the config variable\nbranch.<name>.rebase. This setting can be overriden on the command line\nby --rebase and --no-rebase.\n\nSince 6b37dff (pull: introduce a pull.rebase option to enable --rebase,\n2011-11-06), git-pull --rebase can also be configured via the\npull.rebase configuration option.\n\nRe-implement support for these two configuration settings by introducing\nconfig_get_rebase() which is called before parse_options() to set the\ndefault value of opt_rebase.\n\nHelped-by: Stefan Beller <sbeller@google.com>\nHelped-by: Duy Nguyen <pclouds@gmail.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n\nNotes:\n    v4\n    \n    * Fixed the use-after-free. Thanks Duy for catching it.\n\n builtin/pull.c | 35 ++++++++++++++++++++++++++++++++++-\n 1 file changed, 34 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 33a7b98..1c4fb44 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -72,7 +72,7 @@ static int opt_verbosity;\n static char *opt_progress;\n \n /* Options passed to git-merge or git-rebase */\n-static enum rebase_type opt_rebase;\n+static enum rebase_type opt_rebase = -1;\n static char *opt_diffstat;\n static char *opt_log;\n static char *opt_squash;\n@@ -266,6 +266,36 @@ static const char *config_get_ff(void)\n }\n \n /**\n+ * Returns the default configured value for --rebase. It first looks for the\n+ * value of \"branch.$curr_branch.rebase\", where $curr_branch is the current\n+ * branch, and if HEAD is detached or the configuration key does not exist,\n+ * looks for the value of \"pull.rebase\". If both configuration keys do not\n+ * exist, returns REBASE_FALSE.\n+ */\n+static enum rebase_type config_get_rebase(void)\n+{\n+\tstruct branch *curr_branch = branch_get(\"HEAD\");\n+\tconst char *value;\n+\n+\tif (curr_branch) {\n+\t\tchar *key = xstrfmt(\"branch.%s.rebase\", curr_branch->name);\n+\n+\t\tif (!git_config_get_value(key, &value)) {\n+\t\t\tenum rebase_type ret = parse_config_rebase(key, value, 1);\n+\t\t\tfree(key);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tfree(key);\n+\t}\n+\n+\tif (!git_config_get_value(\"pull.rebase\", &value))\n+\t\treturn parse_config_rebase(\"pull.rebase\", value, 1);\n+\n+\treturn REBASE_FALSE;\n+}\n+\n+/**\n  * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge\n  * into merge_heads.\n  */\n@@ -707,6 +737,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (!opt_ff)\n \t\topt_ff = xstrdup_or_null(config_get_ff());\n \n+\tif (opt_rebase < 0)\n+\t\topt_rebase = config_get_rebase();\n+\n \tgit_config(git_default_config, NULL);\n \n \tif (read_cache_unmerged())\n-- \n2.1.4\n"},{"id":"264132","messageId":"1434624852-6869-18-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 17/19] pull --rebase: exit early when the working directory is dirty","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:10Z","receivedAt":"2015-06-18T10:54:10Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Re-implement the behavior introduced by f9189cf (pull --rebase: exit\nearly when the working directory is dirty, 2008-05-21).\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 76 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 1c4fb44..eb2a28f 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -14,6 +14,8 @@\n #include \"remote.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"revision.h\"\n+#include \"lockfile.h\"\n \n enum rebase_type {\n \tREBASE_INVALID = -1,\n@@ -296,6 +298,73 @@ static enum rebase_type config_get_rebase(void)\n }\n \n /**\n+ * Returns 1 if there are unstaged changes, 0 otherwise.\n+ */\n+static int has_unstaged_changes(const char *prefix)\n+{\n+\tstruct rev_info rev_info;\n+\tint result;\n+\n+\tinit_revisions(&rev_info, prefix);\n+\tDIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);\n+\tDIFF_OPT_SET(&rev_info.diffopt, QUICK);\n+\tdiff_setup_done(&rev_info.diffopt);\n+\tresult = run_diff_files(&rev_info, 0);\n+\treturn diff_result_code(&rev_info.diffopt, result);\n+}\n+\n+/**\n+ * Returns 1 if there are uncommitted changes, 0 otherwise.\n+ */\n+static int has_uncommitted_changes(const char *prefix)\n+{\n+\tstruct rev_info rev_info;\n+\tint result;\n+\n+\tif (is_cache_unborn())\n+\t\treturn 0;\n+\n+\tinit_revisions(&rev_info, prefix);\n+\tDIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);\n+\tDIFF_OPT_SET(&rev_info.diffopt, QUICK);\n+\tadd_head_to_pending(&rev_info);\n+\tdiff_setup_done(&rev_info.diffopt);\n+\tresult = run_diff_index(&rev_info, 1);\n+\treturn diff_result_code(&rev_info.diffopt, result);\n+}\n+\n+/**\n+ * If the work tree has unstaged or uncommitted changes, dies with the\n+ * appropriate message.\n+ */\n+static void die_on_unclean_work_tree(const char *prefix)\n+{\n+\tstruct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));\n+\tint do_die = 0;\n+\n+\thold_locked_index(lock_file, 0);\n+\trefresh_cache(REFRESH_QUIET);\n+\tupdate_index_if_able(&the_index, lock_file);\n+\trollback_lock_file(lock_file);\n+\n+\tif (has_unstaged_changes(prefix)) {\n+\t\terror(_(\"Cannot pull with rebase: You have unstaged changes.\"));\n+\t\tdo_die = 1;\n+\t}\n+\n+\tif (has_uncommitted_changes(prefix)) {\n+\t\tif (do_die)\n+\t\t\terror(_(\"Additionally, your index contains uncommitted changes.\"));\n+\t\telse\n+\t\t\terror(_(\"Cannot pull with rebase: Your index contains uncommitted changes.\"));\n+\t\tdo_die = 1;\n+\t}\n+\n+\tif (do_die)\n+\t\texit(1);\n+}\n+\n+/**\n  * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge\n  * into merge_heads.\n  */\n@@ -751,9 +820,15 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_sha1(\"HEAD\", orig_head))\n \t\thashclr(orig_head);\n \n-\tif (opt_rebase)\n+\tif (opt_rebase) {\n+\t\tif (is_null_sha1(orig_head) && !is_cache_unborn())\n+\t\t\tdie(_(\"Updating an unborn branch with changes added to the index.\"));\n+\n+\t\tdie_on_unclean_work_tree(prefix);\n+\n \t\tif (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))\n \t\t\thashclr(rebase_fork_point);\n+\t}\n \n \tif (run_fetch(repo, refspecs))\n \t\treturn 1;\n-- \n2.1.4\n"},{"id":"264135","messageId":"1434624852-6869-19-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 18/19] pull --rebase: error on no merge candidate cases","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:11Z","receivedAt":"2015-06-18T10:54:11Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Tweak the error messages printed by die_no_merge_candidates() to take\ninto account that we may be \"rebasing against\" rather than \"merging\nwith\".\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c | 15 ++++++++++++---\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex eb2a28f..421a34d 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -431,7 +431,10 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n \tconst char *remote = curr_branch ? curr_branch->remote_name : NULL;\n \n \tif (*refspecs) {\n-\t\tfprintf_ln(stderr, _(\"There are no candidates for merging among the refs that you just fetched.\"));\n+\t\tif (opt_rebase)\n+\t\t\tfprintf_ln(stderr, _(\"There is no candidate for rebasing against among the refs that you just fetched.\"));\n+\t\telse\n+\t\t\tfprintf_ln(stderr, _(\"There are no candidates for merging among the refs that you just fetched.\"));\n \t\tfprintf_ln(stderr, _(\"Generally this means that you provided a wildcard refspec which had no\\n\"\n \t\t\t\t\t\"matches on the remote end.\"));\n \t} else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {\n@@ -441,7 +444,10 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n \t\t\trepo);\n \t} else if (!curr_branch) {\n \t\tfprintf_ln(stderr, _(\"You are not currently on a branch.\"));\n-\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to merge with.\"));\n+\t\tif (opt_rebase)\n+\t\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to rebase against.\"));\n+\t\telse\n+\t\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to merge with.\"));\n \t\tfprintf_ln(stderr, _(\"See git-pull(1) for details.\"));\n \t\tfprintf(stderr, \"\\n\");\n \t\tfprintf_ln(stderr, \"    git pull <remote> <branch>\");\n@@ -453,7 +459,10 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n \t\t\tremote_name = \"<remote>\";\n \n \t\tfprintf_ln(stderr, _(\"There is no tracking information for the current branch.\"));\n-\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to merge with.\"));\n+\t\tif (opt_rebase)\n+\t\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to rebase against.\"));\n+\t\telse\n+\t\t\tfprintf_ln(stderr, _(\"Please specify which branch you want to merge with.\"));\n \t\tfprintf_ln(stderr, _(\"See git-pull(1) for details.\"));\n \t\tfprintf(stderr, \"\\n\");\n \t\tfprintf_ln(stderr, \"    git pull <remote> <branch>\");\n-- \n2.1.4\n"},{"id":"264131","messageId":"1434624852-6869-20-git-send-email-pyokagan@gmail.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH v4 19/19] pull: remove redirection to git-pull.sh","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-18T10:54:12Z","receivedAt":"2015-06-18T10:54:12Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"At the beginning of the rewrite of git-pull.sh to C, we introduced a\nredirection to git-pull.sh if the environment variable\n_GIT_USE_BUILTIN_PULL was not defined in order to not break test scripts\nthat relied on a functional git-pull.\n\nNow that all of git-pull's functionality has been re-implemented in\nbuiltin/pull.c, remove this redirection, and retire the old git-pull.sh\ninto contrib/examples/.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Makefile                                    | 1 -\n builtin/pull.c                              | 7 -------\n git-pull.sh => contrib/examples/git-pull.sh | 0\n 3 files changed, 8 deletions(-)\n rename git-pull.sh => contrib/examples/git-pull.sh (100%)\n\ndiff --git a/Makefile b/Makefile\nindex 17e1136..93e4fa2 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -474,7 +474,6 @@ 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-mergetool.sh\n-SCRIPT_SH += git-pull.sh\n SCRIPT_SH += git-quiltimport.sh\n SCRIPT_SH += git-rebase.sh\n SCRIPT_SH += git-remote-testgit.sh\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 421a34d..722a83c 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -798,13 +798,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tunsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];\n \tunsigned char rebase_fork_point[GIT_SHA1_RAWSZ];\n \n-\tif (!getenv(\"_GIT_USE_BUILTIN_PULL\")) {\n-\t\tconst char *path = mkpath(\"%s/git-pull\", git_exec_path());\n-\n-\t\tif (sane_execvp(path, (char **)argv) < 0)\n-\t\t\tdie_errno(\"could not exec %s\", path);\n-\t}\n-\n \tif (!getenv(\"GIT_REFLOG_ACTION\"))\n \t\tset_reflog_message(argc, argv);\n \ndiff --git a/git-pull.sh b/contrib/examples/git-pull.sh\nsimilarity index 100%\nrename from git-pull.sh\nrename to contrib/examples/git-pull.sh\n-- \n2.1.4\n"},{"id":"264237","messageId":"xmqqfv5oepvh.fsf@gitster.dls.corp.google.com","threadId":"39668","inReplyTo":"1434624852-6869-1-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH v4 00/19] Make git-pull a builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-06-18T20:13:54Z","receivedAt":"2015-06-18T20:13:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul Tan <pyokagan@gmail.com> writes:\n\n> This is a re-roll of [v3]. It squashes in Ramsay's patch \"fix some sparse\n> warnings\", and fixes the use-before-free reported by Duy. Thanks a lot for\n> dealing with my mess :-).\n>\n> Other than that, there are no other changes as I'm working on the git-am side\n> of things.\n\nI didn't look carefully, but does that mean 04/19 has the \"what if\nyou start from a subdirectory and are still using the scripted one?\"\nissue we discussed recently for \"am\"?\n"},{"id":"264293","messageId":"CACRoPnSP4sxE7z4pTYe2TZVCHAie0Sad_tS3zW=znksjusXN1g@mail.gmail.com","threadId":"39668","inReplyTo":"xmqqfv5oepvh.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v4 00/19] Make git-pull a builtin","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-19T10:11:36Z","receivedAt":"2015-06-19T10:11:36Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Fri, Jun 19, 2015 at 4:13 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> I didn't look carefully, but does that mean 04/19 has the \"what if\n> you start from a subdirectory and are still using the scripted one?\"\n> issue we discussed recently for \"am\"?\n\nIt does, but git-pull.sh does not care about the original working directory, no?\n\nRegards,\nPaul\n"}]}