{"thread":{"id":"39357","subject":"[PATCH 00/14] Make git-pull a builtin","startedAt":"2015-05-18T15:05:57Z","lastAt":"2015-06-02T11:26:06Z","messageCount":43,"participants":["Paul Tan","Johannes Schindelin","Junio C Hamano","Stefan Beller"],"isPatch":true,"patchVersion":1,"patchTotal":14},"messages":[{"id":"261452","messageId":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":null,"subject":"[PATCH 00/14] Make git-pull a builtin","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:05:57Z","receivedAt":"2015-05-18T15:05:57Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"This series directly depends on the changes made in jc/merge and\npt/pull-tests. The commit messages assume that the behavioral changes\nproposed in pt/pull-log-n, pt/pull-tags-error-diag, pt/pull-ff-vs-merge-ff,\nand [1] have been introduced.\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:\n\n1. Usually have to rely on spawning a lot of processes, which can be slow on\n   systems that do not implement copy-on-write semantics.\n\n2. They introduce a lot of dependencies, especially on non-POSIX systems.\n\n3. They cannot take advantage of git's C API and 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[2].\n\n[1] http://thread.gmane.org/gmane.comp.version-control.git/269249\n[2] https://gist.github.com/pyokagan/1b7b0d1f4dab6ba3cef1\n\nPaul Tan (14):\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\n Makefile                     |   2 +-\n advice.c                     |   8 +\n advice.h                     |   1 +\n builtin.h                    |   1 +\n builtin/pull.c               | 919 +++++++++++++++++++++++++++++++++++++++++++\n contrib/examples/git-pull.sh | 339 ++++++++++++++++\n git-pull.sh                  | 339 ----------------\n git.c                        |   1 +\n 8 files changed, 1270 insertions(+), 340 deletions(-)\n create mode 100644 builtin/pull.c\n create mode 100755 contrib/examples/git-pull.sh\n delete mode 100755 git-pull.sh\n\n-- \n2.1.4\n"},{"id":"261454","messageId":"1431961571-20370-2-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 01/14] pull: implement fetch + merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:05:58Z","receivedAt":"2015-05-18T15:05:58Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Start off the rewrite of git-pull.sh to C by implementing a simple\ngit-pull builtin that:\n\n1. Parses the command line arguments into [<repo> [<refspecs>...]]\n\n2. Runs git-fetch, passing the repo and refspecs to it.\n\n3. Runs git-merge FETCH_HEAD.\n\nDue to missing features, several tests are disabled:\n\n* Passing flags to git fetch, rebase, merge:\n\n  * t5521: verbosity flags -v, -s\n\n* Passing flags to git-merge, git-rebase:\n\n  * t5150, t5572: Pass --ff, --no-ff, --ff-only to git-merge\n\n  * t5520, t5521, t7406: Support --rebase, branch.*.rebase and\n    pull.rebase config\n\n  * t6029, t4013: -s/--strategy\n\n  * t6037: -X/--strategy-option\n\n  * t5524: --log\n\n* Passing flags to git-fetch:\n\n  * t5521: --dry-run\n\n  * t5500: --depth\n\n  * t5521: --force\n\n  * t5521: --all\n\n  * t7403: --no-recurse-submodules\n\n* t7601: pull.ff config sets --ff, --no-ff, --ff-only\n\n* t5520: branch.*.rebase or pull.rebase sets --rebase, --rebase=preserve\n\n* t5520: Pulling into void\n\n* t5520: appropriate user-friendly error messages on no merge candidates\n  case\n\n* t5520: appropriate user-friendly error messages if index has\n  unresolved entries.\n\n* t5520: fast-forward working tree if branch head is updated by\n  git-fetch\n\n* t5520: set reflog action to \"pull args...\"\n\nThese features will be re-implemented in succeeding patches.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n Makefile                     |   2 +-\n builtin.h                    |   1 +\n builtin/pull.c               |  83 +++++++++++\n contrib/examples/git-pull.sh | 339 +++++++++++++++++++++++++++++++++++++++++++\n git-pull.sh                  | 339 -------------------------------------------\n git.c                        |   1 +\n t/t4013-diff-various.sh      |   3 +\n t/t5150-request-pull.sh      |   2 +-\n t/t5500-fetch-pack.sh        |  10 +-\n t/t5520-pull.sh              |  68 ++++-----\n t/t5521-pull-options.sh      |  20 +--\n t/t5524-pull-msg.sh          |   2 +-\n t/t5572-pull-submodule.sh    |   4 +\n t/t6029-merge-subtree.sh     |   6 +-\n t/t6037-merge-ours-theirs.sh |   2 +-\n t/t7403-submodule-sync.sh    |   8 +-\n t/t7406-submodule-update.sh  |   2 +\n t/t7601-merge-pull-config.sh |   4 +-\n 18 files changed, 495 insertions(+), 401 deletions(-)\n create mode 100644 builtin/pull.c\n create mode 100755 contrib/examples/git-pull.sh\n delete mode 100755 git-pull.sh\n\ndiff --git a/Makefile b/Makefile\nindex 36655d5..51087dc 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\n@@ -876,6 +875,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..0b771b9\n--- /dev/null\n+++ b/builtin/pull.c\n@@ -0,0 +1,83 @@\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 \"argv-array.h\"\n+#include \"run-command.h\"\n+\n+static const char * const pull_usage[] = {\n+\tN_(\"git pull [options] [<repo> [<refspec>...]]\"),\n+\tNULL\n+};\n+\n+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, const 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+\twhile (*refspecs)\n+\t\targv_array_push(&args, *refspecs++);\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+\targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 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+}\ndiff --git a/contrib/examples/git-pull.sh b/contrib/examples/git-pull.sh\nnew file mode 100755\nindex 0000000..5ff4545\n--- /dev/null\n+++ b/contrib/examples/git-pull.sh\n@@ -0,0 +1,339 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+# Fetch one or more remote refs and merge it/them into the current HEAD.\n+\n+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff|--ff-only] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'\n+LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'\n+SUBDIRECTORY_OK=Yes\n+OPTIONS_SPEC=\n+. git-sh-setup\n+. git-sh-i18n\n+set_reflog_action \"pull${1+ $*}\"\n+require_work_tree_exists\n+cd_to_toplevel\n+\n+\n+die_conflict () {\n+    git diff-index --cached --name-status -r --ignore-submodules HEAD --\n+    if [ $(git config --bool --get advice.resolveConflict || echo true) = \"true\" ]; then\n+\tdie \"$(gettext \"Pull is not possible because you have unmerged files.\n+Please, fix them up in the work tree, and then use 'git add/rm <file>'\n+as appropriate to mark resolution and make a commit.\")\"\n+    else\n+\tdie \"$(gettext \"Pull is not possible because you have unmerged files.\")\"\n+    fi\n+}\n+\n+die_merge () {\n+    if [ $(git config --bool --get advice.resolveConflict || echo true) = \"true\" ]; then\n+\tdie \"$(gettext \"You have not concluded your merge (MERGE_HEAD exists).\n+Please, commit your changes before you can merge.\")\"\n+    else\n+\tdie \"$(gettext \"You have not concluded your merge (MERGE_HEAD exists).\")\"\n+    fi\n+}\n+\n+test -z \"$(git ls-files -u)\" || die_conflict\n+test -f \"$GIT_DIR/MERGE_HEAD\" && die_merge\n+\n+bool_or_string_config () {\n+\tgit config --bool \"$1\" 2>/dev/null || git config \"$1\"\n+}\n+\n+strategy_args= diffstat= no_commit= squash= no_ff= ff_only=\n+log_arg= verbosity= progress= recurse_submodules= verify_signatures=\n+merge_args= edit= rebase_args=\n+curr_branch=$(git symbolic-ref -q HEAD)\n+curr_branch_short=\"${curr_branch#refs/heads/}\"\n+rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)\n+if test -z \"$rebase\"\n+then\n+\trebase=$(bool_or_string_config pull.rebase)\n+fi\n+\n+# Setup default fast-forward options via `pull.ff`\n+pull_ff=$(git config pull.ff)\n+case \"$pull_ff\" in\n+false)\n+\tno_ff=--no-ff\n+\t;;\n+only)\n+\tff_only=--ff-only\n+\t;;\n+esac\n+\n+\n+dry_run=\n+while :\n+do\n+\tcase \"$1\" in\n+\t-q|--quiet)\n+\t\tverbosity=\"$verbosity -q\" ;;\n+\t-v|--verbose)\n+\t\tverbosity=\"$verbosity -v\" ;;\n+\t--progress)\n+\t\tprogress=--progress ;;\n+\t--no-progress)\n+\t\tprogress=--no-progress ;;\n+\t-n|--no-stat|--no-summary)\n+\t\tdiffstat=--no-stat ;;\n+\t--stat|--summary)\n+\t\tdiffstat=--stat ;;\n+\t--log|--log=*|--no-log)\n+\t\tlog_arg=\"$1\" ;;\n+\t--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)\n+\t\tno_commit=--no-commit ;;\n+\t--c|--co|--com|--comm|--commi|--commit)\n+\t\tno_commit=--commit ;;\n+\t-e|--edit)\n+\t\tedit=--edit ;;\n+\t--no-edit)\n+\t\tedit=--no-edit ;;\n+\t--sq|--squ|--squa|--squas|--squash)\n+\t\tsquash=--squash ;;\n+\t--no-sq|--no-squ|--no-squa|--no-squas|--no-squash)\n+\t\tsquash=--no-squash ;;\n+\t--ff)\n+\t\tno_ff=--ff ;;\n+\t--no-ff)\n+\t\tno_ff=--no-ff ;;\n+\t--ff-only)\n+\t\tff_only=--ff-only ;;\n+\t-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\\\n+\t\t--strateg=*|--strategy=*|\\\n+\t-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)\n+\t\tcase \"$#,$1\" in\n+\t\t*,*=*)\n+\t\t\tstrategy=$(expr \"z$1\" : 'z-[^=]*=\\(.*\\)') ;;\n+\t\t1,*)\n+\t\t\tusage ;;\n+\t\t*)\n+\t\t\tstrategy=\"$2\"\n+\t\t\tshift ;;\n+\t\tesac\n+\t\tstrategy_args=\"${strategy_args}-s $strategy \"\n+\t\t;;\n+\t-X*)\n+\t\tcase \"$#,$1\" in\n+\t\t1,-X)\n+\t\t\tusage ;;\n+\t\t*,-X)\n+\t\t\txx=\"-X $(git rev-parse --sq-quote \"$2\")\"\n+\t\t\tshift ;;\n+\t\t*,*)\n+\t\t\txx=$(git rev-parse --sq-quote \"$1\") ;;\n+\t\tesac\n+\t\tmerge_args=\"$merge_args$xx \"\n+\t\t;;\n+\t-r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*)\n+\t\trebase=\"${1#*=}\"\n+\t\t;;\n+\t-r|--r|--re|--reb|--reba|--rebas|--rebase)\n+\t\trebase=true\n+\t\t;;\n+\t--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)\n+\t\trebase=false\n+\t\t;;\n+\t--recurse-submodules)\n+\t\trecurse_submodules=--recurse-submodules\n+\t\t;;\n+\t--recurse-submodules=*)\n+\t\trecurse_submodules=\"$1\"\n+\t\t;;\n+\t--no-recurse-submodules)\n+\t\trecurse_submodules=--no-recurse-submodules\n+\t\t;;\n+\t--verify-signatures)\n+\t\tverify_signatures=--verify-signatures\n+\t\t;;\n+\t--no-verify-signatures)\n+\t\tverify_signatures=--no-verify-signatures\n+\t\t;;\n+\t--gpg-sign|-S)\n+\t\tgpg_sign_args=-S\n+\t\t;;\n+\t--gpg-sign=*)\n+\t\tgpg_sign_args=$(git rev-parse --sq-quote \"-S${1#--gpg-sign=}\")\n+\t\t;;\n+\t-S*)\n+\t\tgpg_sign_args=$(git rev-parse --sq-quote \"$1\")\n+\t\t;;\n+\t--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)\n+\t\tdry_run=--dry-run\n+\t\t;;\n+\t-h|--help-all)\n+\t\tusage\n+\t\t;;\n+\t*)\n+\t\t# Pass thru anything that may be meant for fetch.\n+\t\tbreak\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n+case \"$rebase\" in\n+preserve)\n+\trebase=true\n+\trebase_args=--preserve-merges\n+\t;;\n+true|false|'')\n+\t;;\n+*)\n+\techo \"Invalid value for --rebase, should be true, false, or preserve\"\n+\tusage\n+\texit 1\n+\t;;\n+esac\n+\n+error_on_no_merge_candidates () {\n+\texec >&2\n+\tfor opt\n+\tdo\n+\t\tcase \"$opt\" in\n+\t\t-t|--t|--ta|--tag|--tags)\n+\t\t\techo \"It doesn't make sense to pull all tags; you probably meant:\"\n+\t\t\techo \"  git fetch --tags\"\n+\t\t\texit 1\n+\t\tesac\n+\tdone\n+\n+\tif test true = \"$rebase\"\n+\tthen\n+\t\top_type=rebase\n+\t\top_prep=against\n+\telse\n+\t\top_type=merge\n+\t\top_prep=with\n+\tfi\n+\n+\tupstream=$(git config \"branch.$curr_branch_short.merge\")\n+\tremote=$(git config \"branch.$curr_branch_short.remote\")\n+\n+\tif [ $# -gt 1 ]; then\n+\t\tif [ \"$rebase\" = true ]; then\n+\t\t\tprintf \"There is no candidate for rebasing against \"\n+\t\telse\n+\t\t\tprintf \"There are no candidates for merging \"\n+\t\tfi\n+\t\techo \"among the refs that you just fetched.\"\n+\t\techo \"Generally this means that you provided a wildcard refspec which had no\"\n+\t\techo \"matches on the remote end.\"\n+\telif [ $# -gt 0 ] && [ \"$1\" != \"$remote\" ]; then\n+\t\techo \"You asked to pull from the remote '$1', but did not specify\"\n+\t\techo \"a branch. Because this is not the default configured remote\"\n+\t\techo \"for your current branch, you must specify a branch on the command line.\"\n+\telif [ -z \"$curr_branch\" -o -z \"$upstream\" ]; then\n+\t\t. git-parse-remote\n+\t\terror_on_missing_default_upstream \"pull\" $op_type $op_prep \\\n+\t\t\t\"git pull <remote> <branch>\"\n+\telse\n+\t\techo \"Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'\"\n+\t\techo \"from the remote, but no such ref was fetched.\"\n+\tfi\n+\texit 1\n+}\n+\n+test true = \"$rebase\" && {\n+\tif ! git rev-parse -q --verify HEAD >/dev/null\n+\tthen\n+\t\t# On an unborn branch\n+\t\tif test -f \"$(git rev-parse --git-path index)\"\n+\t\tthen\n+\t\t\tdie \"$(gettext \"updating an unborn branch with changes added to the index\")\"\n+\t\tfi\n+\telse\n+\t\trequire_clean_work_tree \"pull with rebase\" \"Please commit or stash them.\"\n+\tfi\n+\toldremoteref= &&\n+\ttest -n \"$curr_branch\" &&\n+\t. git-parse-remote &&\n+\tremoteref=\"$(get_remote_merge_branch \"$@\" 2>/dev/null)\" &&\n+\toldremoteref=$(git merge-base --fork-point \"$remoteref\" $curr_branch 2>/dev/null)\n+}\n+orig_head=$(git rev-parse -q --verify HEAD)\n+git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok \"$@\" || exit 1\n+test -z \"$dry_run\" || exit 0\n+\n+curr_head=$(git rev-parse -q --verify HEAD)\n+if test -n \"$orig_head\" && test \"$curr_head\" != \"$orig_head\"\n+then\n+\t# The fetch involved updating the current branch.\n+\n+\t# The working tree and the index file is still based on the\n+\t# $orig_head commit, but we are merging into $curr_head.\n+\t# First update the working tree to match $curr_head.\n+\n+\teval_gettextln \"Warning: fetch updated the current branch head.\n+Warning: fast-forwarding your working tree from\n+Warning: commit \\$orig_head.\" >&2\n+\tgit update-index -q --refresh\n+\tgit read-tree -u -m \"$orig_head\" \"$curr_head\" ||\n+\t\tdie \"$(eval_gettext \"Cannot fast-forward your working tree.\n+After making sure that you saved anything precious from\n+$ git diff \\$orig_head\n+output, run\n+$ git reset --hard\n+to recover.\")\"\n+\n+fi\n+\n+merge_head=$(sed -e '/\tnot-for-merge\t/d' \\\n+\t-e 's/\t.*//' \"$GIT_DIR\"/FETCH_HEAD | \\\n+\ttr '\\012' ' ')\n+\n+case \"$merge_head\" in\n+'')\n+\terror_on_no_merge_candidates \"$@\"\n+\t;;\n+?*' '?*)\n+\tif test -z \"$orig_head\"\n+\tthen\n+\t\tdie \"$(gettext \"Cannot merge multiple branches into empty head\")\"\n+\tfi\n+\tif test true = \"$rebase\"\n+\tthen\n+\t\tdie \"$(gettext \"Cannot rebase onto multiple branches\")\"\n+\tfi\n+\t;;\n+esac\n+\n+# Pulling into unborn branch: a shorthand for branching off\n+# FETCH_HEAD, for lazy typers.\n+if test -z \"$orig_head\"\n+then\n+\t# Two-way merge: we claim the index is based on an empty tree,\n+\t# and try to fast-forward to HEAD.  This ensures we will not\n+\t# lose index/worktree changes that the user already made on\n+\t# the unborn branch.\n+\tempty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904\n+\tgit read-tree -m -u $empty_tree $merge_head &&\n+\tgit update-ref -m \"initial pull\" HEAD $merge_head \"$curr_head\"\n+\texit\n+fi\n+\n+if test true = \"$rebase\"\n+then\n+\to=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)\n+\tif test \"$oldremoteref\" = \"$o\"\n+\tthen\n+\t\tunset oldremoteref\n+\tfi\n+fi\n+\n+case \"$rebase\" in\n+true)\n+\teval=\"git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity\"\n+\teval=\"$eval $gpg_sign_args\"\n+\teval=\"$eval --onto $merge_head ${oldremoteref:-$merge_head}\"\n+\t;;\n+*)\n+\teval=\"git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only\"\n+\teval=\"$eval $log_arg $strategy_args $merge_args $verbosity $progress\"\n+\teval=\"$eval $gpg_sign_args\"\n+\teval=\"$eval FETCH_HEAD\"\n+\t;;\n+esac\n+eval \"exec $eval\"\ndiff --git a/git-pull.sh b/git-pull.sh\ndeleted file mode 100755\nindex 5ff4545..0000000\n--- a/git-pull.sh\n+++ /dev/null\n@@ -1,339 +0,0 @@\n-#!/bin/sh\n-#\n-# Copyright (c) 2005 Junio C Hamano\n-#\n-# Fetch one or more remote refs and merge it/them into the current HEAD.\n-\n-USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff|--ff-only] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'\n-LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'\n-SUBDIRECTORY_OK=Yes\n-OPTIONS_SPEC=\n-. git-sh-setup\n-. git-sh-i18n\n-set_reflog_action \"pull${1+ $*}\"\n-require_work_tree_exists\n-cd_to_toplevel\n-\n-\n-die_conflict () {\n-    git diff-index --cached --name-status -r --ignore-submodules HEAD --\n-    if [ $(git config --bool --get advice.resolveConflict || echo true) = \"true\" ]; then\n-\tdie \"$(gettext \"Pull is not possible because you have unmerged files.\n-Please, fix them up in the work tree, and then use 'git add/rm <file>'\n-as appropriate to mark resolution and make a commit.\")\"\n-    else\n-\tdie \"$(gettext \"Pull is not possible because you have unmerged files.\")\"\n-    fi\n-}\n-\n-die_merge () {\n-    if [ $(git config --bool --get advice.resolveConflict || echo true) = \"true\" ]; then\n-\tdie \"$(gettext \"You have not concluded your merge (MERGE_HEAD exists).\n-Please, commit your changes before you can merge.\")\"\n-    else\n-\tdie \"$(gettext \"You have not concluded your merge (MERGE_HEAD exists).\")\"\n-    fi\n-}\n-\n-test -z \"$(git ls-files -u)\" || die_conflict\n-test -f \"$GIT_DIR/MERGE_HEAD\" && die_merge\n-\n-bool_or_string_config () {\n-\tgit config --bool \"$1\" 2>/dev/null || git config \"$1\"\n-}\n-\n-strategy_args= diffstat= no_commit= squash= no_ff= ff_only=\n-log_arg= verbosity= progress= recurse_submodules= verify_signatures=\n-merge_args= edit= rebase_args=\n-curr_branch=$(git symbolic-ref -q HEAD)\n-curr_branch_short=\"${curr_branch#refs/heads/}\"\n-rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)\n-if test -z \"$rebase\"\n-then\n-\trebase=$(bool_or_string_config pull.rebase)\n-fi\n-\n-# Setup default fast-forward options via `pull.ff`\n-pull_ff=$(git config pull.ff)\n-case \"$pull_ff\" in\n-false)\n-\tno_ff=--no-ff\n-\t;;\n-only)\n-\tff_only=--ff-only\n-\t;;\n-esac\n-\n-\n-dry_run=\n-while :\n-do\n-\tcase \"$1\" in\n-\t-q|--quiet)\n-\t\tverbosity=\"$verbosity -q\" ;;\n-\t-v|--verbose)\n-\t\tverbosity=\"$verbosity -v\" ;;\n-\t--progress)\n-\t\tprogress=--progress ;;\n-\t--no-progress)\n-\t\tprogress=--no-progress ;;\n-\t-n|--no-stat|--no-summary)\n-\t\tdiffstat=--no-stat ;;\n-\t--stat|--summary)\n-\t\tdiffstat=--stat ;;\n-\t--log|--log=*|--no-log)\n-\t\tlog_arg=\"$1\" ;;\n-\t--no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)\n-\t\tno_commit=--no-commit ;;\n-\t--c|--co|--com|--comm|--commi|--commit)\n-\t\tno_commit=--commit ;;\n-\t-e|--edit)\n-\t\tedit=--edit ;;\n-\t--no-edit)\n-\t\tedit=--no-edit ;;\n-\t--sq|--squ|--squa|--squas|--squash)\n-\t\tsquash=--squash ;;\n-\t--no-sq|--no-squ|--no-squa|--no-squas|--no-squash)\n-\t\tsquash=--no-squash ;;\n-\t--ff)\n-\t\tno_ff=--ff ;;\n-\t--no-ff)\n-\t\tno_ff=--no-ff ;;\n-\t--ff-only)\n-\t\tff_only=--ff-only ;;\n-\t-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\\\n-\t\t--strateg=*|--strategy=*|\\\n-\t-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)\n-\t\tcase \"$#,$1\" in\n-\t\t*,*=*)\n-\t\t\tstrategy=$(expr \"z$1\" : 'z-[^=]*=\\(.*\\)') ;;\n-\t\t1,*)\n-\t\t\tusage ;;\n-\t\t*)\n-\t\t\tstrategy=\"$2\"\n-\t\t\tshift ;;\n-\t\tesac\n-\t\tstrategy_args=\"${strategy_args}-s $strategy \"\n-\t\t;;\n-\t-X*)\n-\t\tcase \"$#,$1\" in\n-\t\t1,-X)\n-\t\t\tusage ;;\n-\t\t*,-X)\n-\t\t\txx=\"-X $(git rev-parse --sq-quote \"$2\")\"\n-\t\t\tshift ;;\n-\t\t*,*)\n-\t\t\txx=$(git rev-parse --sq-quote \"$1\") ;;\n-\t\tesac\n-\t\tmerge_args=\"$merge_args$xx \"\n-\t\t;;\n-\t-r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*)\n-\t\trebase=\"${1#*=}\"\n-\t\t;;\n-\t-r|--r|--re|--reb|--reba|--rebas|--rebase)\n-\t\trebase=true\n-\t\t;;\n-\t--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)\n-\t\trebase=false\n-\t\t;;\n-\t--recurse-submodules)\n-\t\trecurse_submodules=--recurse-submodules\n-\t\t;;\n-\t--recurse-submodules=*)\n-\t\trecurse_submodules=\"$1\"\n-\t\t;;\n-\t--no-recurse-submodules)\n-\t\trecurse_submodules=--no-recurse-submodules\n-\t\t;;\n-\t--verify-signatures)\n-\t\tverify_signatures=--verify-signatures\n-\t\t;;\n-\t--no-verify-signatures)\n-\t\tverify_signatures=--no-verify-signatures\n-\t\t;;\n-\t--gpg-sign|-S)\n-\t\tgpg_sign_args=-S\n-\t\t;;\n-\t--gpg-sign=*)\n-\t\tgpg_sign_args=$(git rev-parse --sq-quote \"-S${1#--gpg-sign=}\")\n-\t\t;;\n-\t-S*)\n-\t\tgpg_sign_args=$(git rev-parse --sq-quote \"$1\")\n-\t\t;;\n-\t--d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)\n-\t\tdry_run=--dry-run\n-\t\t;;\n-\t-h|--help-all)\n-\t\tusage\n-\t\t;;\n-\t*)\n-\t\t# Pass thru anything that may be meant for fetch.\n-\t\tbreak\n-\t\t;;\n-\tesac\n-\tshift\n-done\n-\n-case \"$rebase\" in\n-preserve)\n-\trebase=true\n-\trebase_args=--preserve-merges\n-\t;;\n-true|false|'')\n-\t;;\n-*)\n-\techo \"Invalid value for --rebase, should be true, false, or preserve\"\n-\tusage\n-\texit 1\n-\t;;\n-esac\n-\n-error_on_no_merge_candidates () {\n-\texec >&2\n-\tfor opt\n-\tdo\n-\t\tcase \"$opt\" in\n-\t\t-t|--t|--ta|--tag|--tags)\n-\t\t\techo \"It doesn't make sense to pull all tags; you probably meant:\"\n-\t\t\techo \"  git fetch --tags\"\n-\t\t\texit 1\n-\t\tesac\n-\tdone\n-\n-\tif test true = \"$rebase\"\n-\tthen\n-\t\top_type=rebase\n-\t\top_prep=against\n-\telse\n-\t\top_type=merge\n-\t\top_prep=with\n-\tfi\n-\n-\tupstream=$(git config \"branch.$curr_branch_short.merge\")\n-\tremote=$(git config \"branch.$curr_branch_short.remote\")\n-\n-\tif [ $# -gt 1 ]; then\n-\t\tif [ \"$rebase\" = true ]; then\n-\t\t\tprintf \"There is no candidate for rebasing against \"\n-\t\telse\n-\t\t\tprintf \"There are no candidates for merging \"\n-\t\tfi\n-\t\techo \"among the refs that you just fetched.\"\n-\t\techo \"Generally this means that you provided a wildcard refspec which had no\"\n-\t\techo \"matches on the remote end.\"\n-\telif [ $# -gt 0 ] && [ \"$1\" != \"$remote\" ]; then\n-\t\techo \"You asked to pull from the remote '$1', but did not specify\"\n-\t\techo \"a branch. Because this is not the default configured remote\"\n-\t\techo \"for your current branch, you must specify a branch on the command line.\"\n-\telif [ -z \"$curr_branch\" -o -z \"$upstream\" ]; then\n-\t\t. git-parse-remote\n-\t\terror_on_missing_default_upstream \"pull\" $op_type $op_prep \\\n-\t\t\t\"git pull <remote> <branch>\"\n-\telse\n-\t\techo \"Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'\"\n-\t\techo \"from the remote, but no such ref was fetched.\"\n-\tfi\n-\texit 1\n-}\n-\n-test true = \"$rebase\" && {\n-\tif ! git rev-parse -q --verify HEAD >/dev/null\n-\tthen\n-\t\t# On an unborn branch\n-\t\tif test -f \"$(git rev-parse --git-path index)\"\n-\t\tthen\n-\t\t\tdie \"$(gettext \"updating an unborn branch with changes added to the index\")\"\n-\t\tfi\n-\telse\n-\t\trequire_clean_work_tree \"pull with rebase\" \"Please commit or stash them.\"\n-\tfi\n-\toldremoteref= &&\n-\ttest -n \"$curr_branch\" &&\n-\t. git-parse-remote &&\n-\tremoteref=\"$(get_remote_merge_branch \"$@\" 2>/dev/null)\" &&\n-\toldremoteref=$(git merge-base --fork-point \"$remoteref\" $curr_branch 2>/dev/null)\n-}\n-orig_head=$(git rev-parse -q --verify HEAD)\n-git fetch $verbosity $progress $dry_run $recurse_submodules --update-head-ok \"$@\" || exit 1\n-test -z \"$dry_run\" || exit 0\n-\n-curr_head=$(git rev-parse -q --verify HEAD)\n-if test -n \"$orig_head\" && test \"$curr_head\" != \"$orig_head\"\n-then\n-\t# The fetch involved updating the current branch.\n-\n-\t# The working tree and the index file is still based on the\n-\t# $orig_head commit, but we are merging into $curr_head.\n-\t# First update the working tree to match $curr_head.\n-\n-\teval_gettextln \"Warning: fetch updated the current branch head.\n-Warning: fast-forwarding your working tree from\n-Warning: commit \\$orig_head.\" >&2\n-\tgit update-index -q --refresh\n-\tgit read-tree -u -m \"$orig_head\" \"$curr_head\" ||\n-\t\tdie \"$(eval_gettext \"Cannot fast-forward your working tree.\n-After making sure that you saved anything precious from\n-$ git diff \\$orig_head\n-output, run\n-$ git reset --hard\n-to recover.\")\"\n-\n-fi\n-\n-merge_head=$(sed -e '/\tnot-for-merge\t/d' \\\n-\t-e 's/\t.*//' \"$GIT_DIR\"/FETCH_HEAD | \\\n-\ttr '\\012' ' ')\n-\n-case \"$merge_head\" in\n-'')\n-\terror_on_no_merge_candidates \"$@\"\n-\t;;\n-?*' '?*)\n-\tif test -z \"$orig_head\"\n-\tthen\n-\t\tdie \"$(gettext \"Cannot merge multiple branches into empty head\")\"\n-\tfi\n-\tif test true = \"$rebase\"\n-\tthen\n-\t\tdie \"$(gettext \"Cannot rebase onto multiple branches\")\"\n-\tfi\n-\t;;\n-esac\n-\n-# Pulling into unborn branch: a shorthand for branching off\n-# FETCH_HEAD, for lazy typers.\n-if test -z \"$orig_head\"\n-then\n-\t# Two-way merge: we claim the index is based on an empty tree,\n-\t# and try to fast-forward to HEAD.  This ensures we will not\n-\t# lose index/worktree changes that the user already made on\n-\t# the unborn branch.\n-\tempty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904\n-\tgit read-tree -m -u $empty_tree $merge_head &&\n-\tgit update-ref -m \"initial pull\" HEAD $merge_head \"$curr_head\"\n-\texit\n-fi\n-\n-if test true = \"$rebase\"\n-then\n-\to=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)\n-\tif test \"$oldremoteref\" = \"$o\"\n-\tthen\n-\t\tunset oldremoteref\n-\tfi\n-fi\n-\n-case \"$rebase\" in\n-true)\n-\teval=\"git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity\"\n-\teval=\"$eval $gpg_sign_args\"\n-\teval=\"$eval --onto $merge_head ${oldremoteref:-$merge_head}\"\n-\t;;\n-*)\n-\teval=\"git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only\"\n-\teval=\"$eval $log_arg $strategy_args $merge_args $verbosity $progress\"\n-\teval=\"$eval $gpg_sign_args\"\n-\teval=\"$eval FETCH_HEAD\"\n-\t;;\n-esac\n-eval \"exec $eval\"\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 },\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 6ec6072..a11e48b 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -7,6 +7,9 @@ test_description='Various diff formatting options'\n \n . ./test-lib.sh\n \n+skip_all='setup for this test suite requires git pull -s'\n+test_done\n+\n LF='\n '\n \ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex 82c33b8..f5ea605 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -132,7 +132,7 @@ test_expect_success 'pull request when forgot to push' '\n \n '\n \n-test_expect_success 'pull request after push' '\n+test_expect_failure 'pull request after push' '\n \n \trm -fr downstream.git &&\n \tgit init --bare downstream.git &&\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 3a9b775..213b231 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -226,14 +226,14 @@ test_expect_success 'add two more (part 2)' '\n \tadd B69 $B68\n '\n \n-test_expect_success 'deepening pull in shallow repo' '\n+test_expect_failure 'deepening pull in shallow repo' '\n \t(\n \t\tcd shallow &&\n \t\tgit pull --depth 4 .. B\n \t)\n '\n \n-test_expect_success 'clone shallow object count' '\n+test_expect_failure 'clone shallow object count' '\n \t(\n \t\tcd shallow &&\n \t\tgit count-objects -v\n@@ -248,7 +248,7 @@ test_expect_success 'deepening fetch in shallow repo' '\n \t)\n '\n \n-test_expect_success 'clone shallow object count' '\n+test_expect_failure 'clone shallow object count' '\n \t(\n \t\tcd shallow &&\n \t\tgit count-objects -v\n@@ -272,11 +272,11 @@ test_expect_success 'additional simple shallow deepenings' '\n \t)\n '\n \n-test_expect_success 'clone shallow depth count' '\n+test_expect_failure 'clone shallow depth count' '\n \ttest \"`git --git-dir=shallow/.git rev-list --count HEAD`\" = 11\n '\n \n-test_expect_success 'clone shallow object count' '\n+test_expect_failure 'clone shallow object count' '\n \t(\n \t\tcd shallow &&\n \t\tgit count-objects -v\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 62dbfb5..f32b8cb 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -26,7 +26,7 @@ test_expect_success 'pulling into void' '\n \ttest_cmp file cloned/file\n '\n \n-test_expect_success 'pulling into void using master:master' '\n+test_expect_failure 'pulling into void using master:master' '\n \tgit init cloned-uho &&\n \t(\n \t\tcd cloned-uho &&\n@@ -76,7 +76,7 @@ test_expect_success 'pulling into void does not remove new staged files' '\n \t)\n '\n \n-test_expect_success 'pulling into void must not create an octopus' '\n+test_expect_failure 'pulling into void must not create an octopus' '\n \tgit init cloned-octopus &&\n \t(\n \t\tcd cloned-octopus &&\n@@ -85,7 +85,7 @@ test_expect_success 'pulling into void must not create an octopus' '\n \t)\n '\n \n-test_expect_success 'test . as a remote' '\n+test_expect_failure 'test . as a remote' '\n \tgit branch copy master &&\n \tgit config branch.copy.remote . &&\n \tgit config branch.copy.merge refs/heads/master &&\n@@ -101,7 +101,7 @@ test_expect_success 'test . as a remote' '\n \ttest_cmp reflog.expected reflog.fuzzy\n '\n \n-test_expect_success 'the default remote . should not break explicit pull' '\n+test_expect_failure 'the default remote . should not break explicit pull' '\n \tgit checkout -b second master^ &&\n \techo modified >file &&\n \tgit commit -a -m modified &&\n@@ -116,7 +116,7 @@ test_expect_success 'the default remote . should not break explicit pull' '\n \ttest_cmp reflog.expected reflog.fuzzy\n '\n \n-test_expect_success 'fail if wildcard spec does not match any refs' '\n+test_expect_failure 'fail if wildcard spec does not match any refs' '\n \tgit checkout -b test copy^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D test\" &&\n \ttest \"$(cat file)\" = file &&\n@@ -125,7 +125,7 @@ test_expect_success 'fail if wildcard spec does not match any refs' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_success 'fail if no branches specified with non-default remote' '\n+test_expect_failure 'fail if no branches specified with non-default remote' '\n \tgit remote add test_remote . &&\n \ttest_when_finished \"git remote remove test_remote\" &&\n \tgit checkout -b test copy^ &&\n@@ -137,7 +137,7 @@ test_expect_success 'fail if no branches specified with non-default remote' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_success 'fail if not on a branch' '\n+test_expect_failure 'fail if not on a branch' '\n \tgit remote add origin . &&\n \ttest_when_finished \"git remote remove origin\" &&\n \tgit checkout HEAD^ &&\n@@ -148,7 +148,7 @@ test_expect_success 'fail if not on a branch' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_success 'fail if no configuration for current branch' '\n+test_expect_failure 'fail if no configuration for current branch' '\n \tgit remote add test_remote . &&\n \ttest_when_finished \"git remote remove test_remote\" &&\n \tgit checkout -b test copy^ &&\n@@ -160,7 +160,7 @@ test_expect_success 'fail if no configuration for current branch' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_success 'fail if upstream branch does not exist' '\n+test_expect_failure 'fail if upstream branch does not exist' '\n \tgit checkout -b test copy^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D test\" &&\n \ttest_config branch.test.remote . &&\n@@ -171,7 +171,7 @@ test_expect_success 'fail if upstream branch does not exist' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_success 'fail if the index has unresolved entries' '\n+test_expect_failure 'fail if the index has unresolved entries' '\n \tgit checkout -b third second^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D third\" &&\n \ttest \"$(cat file)\" = file &&\n@@ -191,7 +191,7 @@ test_expect_success 'fail if the index has unresolved entries' '\n \ttest_cmp expected file\n '\n \n-test_expect_success 'fast-forwards working tree if branch head is updated' '\n+test_expect_failure 'fast-forwards working tree if branch head is updated' '\n \tgit checkout -b third second^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D third\" &&\n \ttest \"$(cat file)\" = file &&\n@@ -201,7 +201,7 @@ test_expect_success 'fast-forwards working tree if branch head is updated' '\n \ttest \"$(git rev-parse third)\" = \"$(git rev-parse second)\"\n '\n \n-test_expect_success 'fast-forward fails with conflicting work tree' '\n+test_expect_failure 'fast-forward fails with conflicting work tree' '\n \tgit checkout -b third second^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D third\" &&\n \ttest \"$(cat file)\" = file &&\n@@ -212,7 +212,7 @@ test_expect_success 'fast-forward fails with conflicting work tree' '\n \ttest \"$(git rev-parse third)\" = \"$(git rev-parse second)\"\n '\n \n-test_expect_success '--rebase' '\n+test_expect_failure '--rebase' '\n \tgit branch to-rebase &&\n \techo modified again > file &&\n \tgit commit -m file file &&\n@@ -226,7 +226,7 @@ test_expect_success '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n-test_expect_success '--rebase fails with multiple branches' '\n+test_expect_failure '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n \ttest \"$(git rev-parse HEAD)\" = \"$(git rev-parse before-rebase)\" &&\n@@ -234,7 +234,7 @@ test_expect_success '--rebase fails with multiple branches' '\n \ttest modified = \"$(git show HEAD:file)\"\n '\n \n-test_expect_success 'pull.rebase' '\n+test_expect_failure 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull . copy &&\n@@ -242,7 +242,7 @@ test_expect_success 'pull.rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n-test_expect_success 'branch.to-rebase.rebase' '\n+test_expect_failure 'branch.to-rebase.rebase' '\n \tgit reset --hard before-rebase &&\n \ttest_config branch.to-rebase.rebase true &&\n \tgit pull . copy &&\n@@ -280,7 +280,7 @@ test_expect_success 'pull.rebase=false create a new merge commit' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_success 'pull.rebase=true flattens keep-merge' '\n+test_expect_failure 'pull.rebase=true flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull . copy &&\n@@ -288,7 +288,7 @@ test_expect_success 'pull.rebase=true flattens keep-merge' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '\n+test_expect_failure 'pull.rebase=1 is treated as true and flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase 1 &&\n \tgit pull . copy &&\n@@ -296,7 +296,7 @@ test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '\n+test_expect_failure 'pull.rebase=preserve rebases and merges keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase preserve &&\n \tgit pull . copy &&\n@@ -304,13 +304,13 @@ test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '\n \ttest \"$(git rev-parse HEAD^2)\" = \"$(git rev-parse keep-merge)\"\n '\n \n-test_expect_success 'pull.rebase=invalid fails' '\n+test_expect_failure 'pull.rebase=invalid fails' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase invalid &&\n \t! git pull . copy\n '\n \n-test_expect_success '--rebase=false create a new merge commit' '\n+test_expect_failure '--rebase=false create a new merge commit' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull --rebase=false . copy &&\n@@ -319,7 +319,7 @@ test_expect_success '--rebase=false create a new merge commit' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_success '--rebase=true rebases and flattens keep-merge' '\n+test_expect_failure '--rebase=true rebases and flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase preserve &&\n \tgit pull --rebase=true . copy &&\n@@ -327,7 +327,7 @@ test_expect_success '--rebase=true rebases and flattens keep-merge' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_success '--rebase=preserve rebases and merges keep-merge' '\n+test_expect_failure '--rebase=preserve rebases and merges keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull --rebase=preserve . copy &&\n@@ -340,7 +340,7 @@ test_expect_success '--rebase=invalid fails' '\n \t! git pull --rebase=invalid . copy\n '\n \n-test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '\n+test_expect_failure '--rebase overrides pull.rebase=preserve and flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase preserve &&\n \tgit pull --rebase . copy &&\n@@ -348,7 +348,7 @@ test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-m\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_success '--rebase with rebased upstream' '\n+test_expect_failure '--rebase with rebased upstream' '\n \n \tgit remote add -f me . &&\n \tgit checkout copy &&\n@@ -366,7 +366,7 @@ test_expect_success '--rebase with rebased upstream' '\n \n '\n \n-test_expect_success '--rebase with rebased default upstream' '\n+test_expect_failure '--rebase with rebased default upstream' '\n \n \tgit update-ref refs/remotes/me/copy copy-orig &&\n \tgit checkout --track -b to-rebase2 me/copy &&\n@@ -377,7 +377,7 @@ test_expect_success '--rebase with rebased default upstream' '\n \n '\n \n-test_expect_success 'rebased upstream + fetch + pull --rebase' '\n+test_expect_failure 'rebased upstream + fetch + pull --rebase' '\n \n \tgit update-ref refs/remotes/me/copy copy-orig &&\n \tgit reset --hard to-rebase-orig &&\n@@ -390,7 +390,7 @@ test_expect_success 'rebased upstream + fetch + pull --rebase' '\n \n '\n \n-test_expect_success 'pull --rebase dies early with dirty working directory' '\n+test_expect_failure 'pull --rebase dies early with dirty working directory' '\n \n \tgit checkout to-rebase &&\n \tgit update-ref refs/remotes/me/copy copy^ &&\n@@ -409,7 +409,7 @@ test_expect_success 'pull --rebase dies early with dirty working directory' '\n \n '\n \n-test_expect_success 'pull --rebase works on branch yet to be born' '\n+test_expect_failure 'pull --rebase works on branch yet to be born' '\n \tgit rev-parse master >expect &&\n \tmkdir empty_repo &&\n \t(cd empty_repo &&\n@@ -420,7 +420,7 @@ test_expect_success 'pull --rebase works on branch yet to be born' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'pull --rebase fails on unborn branch with staged changes' '\n+test_expect_failure 'pull --rebase fails on unborn branch with staged changes' '\n \ttest_when_finished \"rm -rf empty_repo2\" &&\n \tgit init empty_repo2 &&\n \t(\n@@ -456,14 +456,14 @@ test_expect_success 'setup for detecting upstreamed changes' '\n \t)\n '\n \n-test_expect_success 'git pull --rebase detects upstreamed changes' '\n+test_expect_failure 'git pull --rebase detects upstreamed changes' '\n \t(cd dst &&\n \t git pull --rebase &&\n \t test -z \"$(git ls-files -u)\"\n \t)\n '\n \n-test_expect_success 'setup for avoiding reapplying old patches' '\n+test_expect_failure 'setup for avoiding reapplying old patches' '\n \t(cd dst &&\n \t test_might_fail git rebase --abort &&\n \t git reset --hard origin/master\n@@ -485,14 +485,14 @@ test_expect_success 'setup for avoiding reapplying old patches' '\n \t)\n '\n \n-test_expect_success 'git pull --rebase does not reapply old patches' '\n+test_expect_failure 'git pull --rebase does not reapply old patches' '\n \t(cd dst &&\n \t test_must_fail git pull --rebase &&\n \t test 1 = $(find .git/rebase-apply -name \"000*\" | wc -l)\n \t)\n '\n \n-test_expect_success 'git pull --rebase against local branch' '\n+test_expect_failure 'git pull --rebase against local branch' '\n \tgit checkout -b copy2 to-rebase-orig &&\n \tgit pull --rebase . to-rebase &&\n \ttest \"conflicting modification\" = \"$(cat file)\" &&\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex 56e7377..0f6094b 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -11,7 +11,7 @@ test_expect_success 'setup' '\n \t git commit -m one)\n '\n \n-test_expect_success 'git pull -q' '\n+test_expect_failure 'git pull -q' '\n \tmkdir clonedq &&\n \t(cd clonedq && git init &&\n \tgit pull -q \"../parent\" >out 2>err &&\n@@ -19,7 +19,7 @@ test_expect_success 'git pull -q' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_success 'git pull -q --rebase' '\n+test_expect_failure 'git pull -q --rebase' '\n \tmkdir clonedqrb &&\n \t(cd clonedqrb && git init &&\n \tgit pull -q --rebase \"../parent\" >out 2>err &&\n@@ -38,7 +38,7 @@ test_expect_success 'git pull' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_success 'git pull --rebase' '\n+test_expect_failure 'git pull --rebase' '\n \tmkdir clonedrb &&\n \t(cd clonedrb && git init &&\n \tgit pull --rebase \"../parent\" >out 2>err &&\n@@ -46,7 +46,7 @@ test_expect_success 'git pull --rebase' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_success 'git pull -v' '\n+test_expect_failure 'git pull -v' '\n \tmkdir clonedv &&\n \t(cd clonedv && git init &&\n \tgit pull -v \"../parent\" >out 2>err &&\n@@ -54,7 +54,7 @@ test_expect_success 'git pull -v' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_success 'git pull -v --rebase' '\n+test_expect_failure 'git pull -v --rebase' '\n \tmkdir clonedvrb &&\n \t(cd clonedvrb && git init &&\n \tgit pull -v --rebase \"../parent\" >out 2>err &&\n@@ -62,7 +62,7 @@ test_expect_success 'git pull -v --rebase' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_success 'git pull -v -q' '\n+test_expect_failure 'git pull -v -q' '\n \tmkdir clonedvq &&\n \t(cd clonedvq && git init &&\n \tgit pull -v -q \"../parent\" >out 2>err &&\n@@ -70,7 +70,7 @@ test_expect_success 'git pull -v -q' '\n \ttest_must_be_empty err)\n '\n \n-test_expect_success 'git pull -q -v' '\n+test_expect_failure 'git pull -q -v' '\n \tmkdir clonedqv &&\n \t(cd clonedqv && git init &&\n \tgit pull -q -v \"../parent\" >out 2>err &&\n@@ -78,7 +78,7 @@ test_expect_success 'git pull -q -v' '\n \ttest -s err)\n '\n \n-test_expect_success 'git pull --force' '\n+test_expect_failure 'git pull --force' '\n \tmkdir clonedoldstyle &&\n \t(cd clonedoldstyle && git init &&\n \tcat >>.git/config <<-\\EOF &&\n@@ -99,7 +99,7 @@ test_expect_success 'git pull --force' '\n \t)\n '\n \n-test_expect_success 'git pull --all' '\n+test_expect_failure 'git pull --all' '\n \tmkdir clonedmulti &&\n \t(cd clonedmulti && git init &&\n \tcat >>.git/config <<-\\EOF &&\n@@ -117,7 +117,7 @@ test_expect_success 'git pull --all' '\n \t)\n '\n \n-test_expect_success 'git pull --dry-run' '\n+test_expect_failure 'git pull --dry-run' '\n \ttest_when_finished \"rm -rf clonedry\" &&\n \tgit init clonedry &&\n \t(\ndiff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh\nindex eebb8c9..1ba8054 100755\n--- a/t/t5524-pull-msg.sh\n+++ b/t/t5524-pull-msg.sh\n@@ -25,7 +25,7 @@ test_expect_success setup '\n \tgit commit -m \"do not clobber $dollar signs\"\n '\n \n-test_expect_success pull '\n+test_expect_failure pull '\n (\n \tcd cloned &&\n \tgit pull --log &&\ndiff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh\nindex accfa5c..408c886 100755\n--- a/t/t5572-pull-submodule.sh\n+++ b/t/t5572-pull-submodule.sh\n@@ -5,6 +5,10 @@ test_description='pull can handle submodules'\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-submodule-update.sh\n \n+KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES=1\n+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1\n+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n+\n reset_branch_to_HEAD () {\n \tgit branch -D \"$1\" &&\n \tgit checkout -b \"$1\" HEAD &&\ndiff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh\nindex 73fc240..54af64e 100755\n--- a/t/t6029-merge-subtree.sh\n+++ b/t/t6029-merge-subtree.sh\n@@ -61,7 +61,7 @@ test_expect_success 'initial merge' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'merge update' '\n+test_expect_failure 'merge update' '\n \tcd ../git-gui &&\n \techo git-gui2 > git-gui.sh &&\n \to3=$(git hash-object git-gui.sh) &&\n@@ -95,7 +95,7 @@ test_expect_success 'initial ambiguous subtree' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'merge using explicit' '\n+test_expect_failure 'merge using explicit' '\n \tcd ../git &&\n \tgit reset --hard master2 &&\n \tgit pull -Xsubtree=git-gui gui master2 &&\n@@ -108,7 +108,7 @@ test_expect_success 'merge using explicit' '\n \ttest_cmp expected actual\n '\n \n-test_expect_success 'merge2 using explicit' '\n+test_expect_failure 'merge2 using explicit' '\n \tcd ../git &&\n \tgit reset --hard master2 &&\n \tgit pull -Xsubtree=git-gui2 gui master2 &&\ndiff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh\nindex 3889eca..876e23b 100755\n--- a/t/t6037-merge-ours-theirs.sh\n+++ b/t/t6037-merge-ours-theirs.sh\n@@ -65,7 +65,7 @@ test_expect_success 'binary file with -Xours/-Xtheirs' '\n \tgit diff --exit-code master HEAD -- file\n '\n \n-test_expect_success 'pull passes -X to underlying merge' '\n+test_expect_failure 'pull passes -X to underlying merge' '\n \tgit reset --hard master && git pull -s recursive -Xours . side &&\n \tgit reset --hard master && git pull -s recursive -X ours . side &&\n \tgit reset --hard master && git pull -s recursive -Xtheirs . side &&\ndiff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh\nindex 79bc135..b448562 100755\n--- a/t/t7403-submodule-sync.sh\n+++ b/t/t7403-submodule-sync.sh\n@@ -96,7 +96,7 @@ test_expect_success 'change submodule url' '\n \t)\n '\n \n-test_expect_success '\"git submodule sync\" should update submodule URLs' '\n+test_expect_failure '\"git submodule sync\" should update submodule URLs' '\n \t(\n \t\tcd super-clone &&\n \t\tgit pull --no-recurse-submodules &&\n@@ -121,7 +121,7 @@ test_expect_success '\"git submodule sync\" should update submodule URLs' '\n \t)\n '\n \n-test_expect_success '\"git submodule sync --recursive\" should update all submodule URLs' '\n+test_expect_failure '\"git submodule sync --recursive\" should update all submodule URLs' '\n \t(\n \t\tcd super-clone &&\n \t\t(\n@@ -149,7 +149,7 @@ test_expect_success 'reset submodule URLs' '\n \treset_submodule_urls super-clone\n '\n \n-test_expect_success '\"git submodule sync\" should update submodule URLs - subdirectory' '\n+test_expect_failure '\"git submodule sync\" should update submodule URLs - subdirectory' '\n \t(\n \t\tcd super-clone &&\n \t\tgit pull --no-recurse-submodules &&\n@@ -177,7 +177,7 @@ test_expect_success '\"git submodule sync\" should update submodule URLs - subdire\n \t)\n '\n \n-test_expect_success '\"git submodule sync --recursive\" should update all submodule URLs - subdirectory' '\n+test_expect_failure '\"git submodule sync --recursive\" should update all submodule URLs - subdirectory' '\n \t(\n \t\tcd super-clone &&\n \t\t(\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex dda3929..1ffd837 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -11,6 +11,8 @@ submodule and \"git submodule update --rebase/--merge\" does not detach the HEAD.\n \n . ./test-lib.sh\n \n+skip_all='skipping submodule update tests, requires git pull --rebase'\n+test_done\n \n compare_head()\n {\ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nindex f768c90..7a846a2 100755\n--- a/t/t7601-merge-pull-config.sh\n+++ b/t/t7601-merge-pull-config.sh\n@@ -45,7 +45,7 @@ test_expect_success 'fast-forward pull succeeds with \"true\" in pull.ff' '\n \ttest \"$(git rev-parse HEAD)\" = \"$(git rev-parse c1)\"\n '\n \n-test_expect_success 'fast-forward pull creates merge with \"false\" in pull.ff' '\n+test_expect_failure 'fast-forward pull creates merge with \"false\" in pull.ff' '\n \tgit reset --hard c0 &&\n \ttest_config pull.ff false &&\n \tgit pull . c1 &&\n@@ -53,7 +53,7 @@ test_expect_success 'fast-forward pull creates merge with \"false\" in pull.ff' '\n \ttest \"$(git rev-parse HEAD^2)\" = \"$(git rev-parse c1)\"\n '\n \n-test_expect_success 'pull prevents non-fast-forward with \"only\" in pull.ff' '\n+test_expect_failure 'pull prevents non-fast-forward with \"only\" in pull.ff' '\n \tgit reset --hard c1 &&\n \ttest_config pull.ff only &&\n \ttest_must_fail git pull . c3\n-- \n2.1.4\n"},{"id":"261453","messageId":"1431961571-20370-3-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 02/14] pull: pass verbosity, --progress flags to fetch and merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:05:59Z","receivedAt":"2015-05-18T15:05: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\nRe-implement support for this flag by introducing the option callback\nhandler parse_opt_passthru(). This callback is used to pass the\n\"--progress\" or \"--no-progress\" command-line switch to git-fetch and\ngit-merge.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c          | 60 +++++++++++++++++++++++++++++++++++++++++++++++++\n t/t5521-pull-options.sh |  8 +++----\n 2 files changed, 64 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 0b771b9..a4d9c92 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -11,16 +11,64 @@\n #include \"argv-array.h\"\n #include \"run-command.h\"\n \n+/**\n+ * Given an option opt, where opt->value points to a char* and opt->defval is a\n+ * string, sets opt->value to the evaluation of \"--$defval=$arg\". If `arg` is\n+ * NULL, then opt->value is set to \"--$defval\". If unset is true, then\n+ * opt->value is set to \"--no-$defval\".\n+ */\n+static int parse_opt_passthru(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tchar **opt_value = opt->value;\n+\n+\tassert(opt->defval);\n+\tstrbuf_addstr(&sb, unset ? \"--no-\" : \"--\");\n+\tstrbuf_addstr(&sb, (const char*) opt->defval);\n+\tif (arg) {\n+\t\tstrbuf_addch(&sb, '=');\n+\t\tstrbuf_addstr(&sb, arg);\n+\t}\n+\tif (*opt_value)\n+\t\tfree(*opt_value);\n+\t*opt_value = strbuf_detach(&sb, NULL);\n+\treturn 0;\n+}\n+\n static const char * const pull_usage[] = {\n \tN_(\"git pull [options] [<repo> [<refspec>...]]\"),\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+\t{ OPTION_CALLBACK, 0, \"progress\", &opt_progress, NULL,\n+\t  N_(\"force progress reporting\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru},\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@@ -45,6 +93,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 \twhile (*refspecs)\n@@ -63,6 +117,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);\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex 0f6094b..89e2104 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -11,7 +11,7 @@ test_expect_success 'setup' '\n \t git commit -m one)\n '\n \n-test_expect_failure 'git pull -q' '\n+test_expect_success 'git pull -q' '\n \tmkdir clonedq &&\n \t(cd clonedq && git init &&\n \tgit pull -q \"../parent\" >out 2>err &&\n@@ -46,7 +46,7 @@ test_expect_failure 'git pull --rebase' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_failure 'git pull -v' '\n+test_expect_success 'git pull -v' '\n \tmkdir clonedv &&\n \t(cd clonedv && git init &&\n \tgit pull -v \"../parent\" >out 2>err &&\n@@ -62,7 +62,7 @@ test_expect_failure 'git pull -v --rebase' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_failure 'git pull -v -q' '\n+test_expect_success 'git pull -v -q' '\n \tmkdir clonedvq &&\n \t(cd clonedvq && git init &&\n \tgit pull -v -q \"../parent\" >out 2>err &&\n@@ -70,7 +70,7 @@ test_expect_failure 'git pull -v -q' '\n \ttest_must_be_empty err)\n '\n \n-test_expect_failure 'git pull -q -v' '\n+test_expect_success 'git pull -q -v' '\n \tmkdir clonedqv &&\n \t(cd clonedqv && git init &&\n \tgit pull -q -v \"../parent\" >out 2>err &&\n-- \n2.1.4\n"},{"id":"261455","messageId":"1431961571-20370-4-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 03/14] pull: pass git-merge's options to git-merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:00Z","receivedAt":"2015-05-18T15:06: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\nWhile most options can be implemented through the parse_opt_passthru()\ncallback, --strategy and --strategy-option are implemented by\nintroducing argv_push_strategies() and argv_push_strategy_opts() as they\ncan be specified multiple times.\n\nAlso allow --log to take a value <n>, thus making the failing test\n'--log=1 limits shortlog length' pass.\n\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c               | 94 ++++++++++++++++++++++++++++++++++++++++++++\n t/t4013-diff-various.sh      |  3 --\n t/t5150-request-pull.sh      |  2 +-\n t/t5524-pull-msg.sh          |  2 +-\n t/t5572-pull-submodule.sh    |  4 --\n t/t6029-merge-subtree.sh     |  6 +--\n t/t6037-merge-ours-theirs.sh |  2 +-\n 7 files changed, 100 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex a4d9c92..573e4f6 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -44,6 +44,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 string_list opt_strategies = STRING_LIST_INIT_NODUP;\n+static struct string_list opt_strategy_opts = STRING_LIST_INIT_NODUP;\n+static char *opt_gpg_sign;\n+\n static struct option pull_options[] = {\n \t/* Shared options */\n \tOPT__VERBOSITY(&opt_verbosity),\n@@ -51,6 +63,47 @@ static struct option pull_options[] = {\n \t  N_(\"force progress reporting\"),\n \t  PARSE_OPT_NOARG, parse_opt_passthru},\n \n+\t/* Options passed to git-merge */\n+\tOPT_GROUP(N_(\"Options related to merging\")),\n+\t{ OPTION_CALLBACK, 'n', NULL, &opt_diffstat, NULL,\n+\t  N_(\"do not show a diffstat at the end of the merge\"),\n+\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_passthru, (intptr_t) \"no-stat\" },\n+\t{ OPTION_CALLBACK, 0, \"stat\", &opt_diffstat, NULL,\n+\t  N_(\"show a diffstat at the end of the merge\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"stat\" },\n+\t{ OPTION_CALLBACK, 0, \"summary\", &opt_diffstat, NULL,\n+\t  N_(\"(synonym to --stat)\"),\n+\t  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, parse_opt_passthru, (intptr_t) \"stat\" },\n+\t{ OPTION_CALLBACK, 0, \"log\", &opt_log, N_(\"n\"),\n+\t  N_(\"add (at most <n>) entries from shortlog to merge commit message\"),\n+\t  PARSE_OPT_OPTARG, parse_opt_passthru, (intptr_t) \"log\" },\n+\t{ OPTION_CALLBACK, 0, \"squash\", &opt_squash, NULL,\n+\t  N_(\"create a single commit instead of doing a merge\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"squash\" },\n+\t{ OPTION_CALLBACK, 0, \"commit\", &opt_commit, NULL,\n+\t  N_(\"perform a commit if the merge succeeds (default)\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"commit\" },\n+\t{ OPTION_CALLBACK, 0, \"edit\", &opt_edit, NULL,\n+\t  N_(\"edit message before committing\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"edit\" },\n+\t{ OPTION_CALLBACK, 0, \"ff\", &opt_ff, NULL,\n+\t  N_(\"allow fast-forward\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"ff\" },\n+\t{ OPTION_CALLBACK, 0, \"ff-only\", &opt_ff, NULL,\n+\t  N_(\"abort if fast-forward is not possible\"),\n+\t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_passthru, (intptr_t) \"ff-only\" },\n+\t{ OPTION_CALLBACK, 0, \"verify-signatures\", &opt_verify_signatures, NULL,\n+\t  N_(\"verify that the named commit has a valid GPG signature\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"verify-signatures\" },\n+\tOPT_STRING_LIST('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n+\t\t\tN_(\"merge strategy to use\")),\n+\tOPT_STRING_LIST('X', \"strategy-option\", &opt_strategy_opts,\n+\t\t\tN_(\"option=value\"),\n+\t\t\tN_(\"option for selected merge strategy\")),\n+\t{ OPTION_CALLBACK, 'S', \"gpg-sign\", &opt_gpg_sign, N_(\"key-id\"),\n+\t  N_(\"GPG sign commit\"),\n+\t  PARSE_OPT_OPTARG, parse_opt_passthru, (intptr_t) \"gpg-sign\" },\n+\n \tOPT_END()\n };\n \n@@ -69,6 +122,27 @@ static void argv_push_verbosity(struct argv_array *arr)\n }\n \n /**\n+ * Pushes opt_strategies as \"-s <strategy>\" flags into arr.\n+ */\n+static void argv_push_strategies(struct argv_array *arr)\n+{\n+\tstruct string_list_item *item;\n+\n+\tfor_each_string_list_item(item, &opt_strategies)\n+\t\targv_array_pushl(arr, \"-s\", item->string, NULL);\n+}\n+\n+/**\n+ * Pushes opt_strategy_opts as \"-X <option>\" flags into arr.\n+ */\n+static void argv_push_strategy_opts(struct argv_array *arr)\n+{\n+\tstruct string_list_item *item;\n+\tfor_each_string_list_item(item, &opt_strategy_opts)\n+\t\targv_array_pushl(arr, \"-X\", item->string, NULL);\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@@ -123,6 +197,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_push_strategies(&args);\n+\targv_push_strategy_opts(&args);\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);\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex a11e48b..6ec6072 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -7,9 +7,6 @@ test_description='Various diff formatting options'\n \n . ./test-lib.sh\n \n-skip_all='setup for this test suite requires git pull -s'\n-test_done\n-\n LF='\n '\n \ndiff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh\nindex f5ea605..82c33b8 100755\n--- a/t/t5150-request-pull.sh\n+++ b/t/t5150-request-pull.sh\n@@ -132,7 +132,7 @@ test_expect_success 'pull request when forgot to push' '\n \n '\n \n-test_expect_failure 'pull request after push' '\n+test_expect_success 'pull request after push' '\n \n \trm -fr downstream.git &&\n \tgit init --bare downstream.git &&\ndiff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh\nindex 1ba8054..eebb8c9 100755\n--- a/t/t5524-pull-msg.sh\n+++ b/t/t5524-pull-msg.sh\n@@ -25,7 +25,7 @@ test_expect_success setup '\n \tgit commit -m \"do not clobber $dollar signs\"\n '\n \n-test_expect_failure pull '\n+test_expect_success pull '\n (\n \tcd cloned &&\n \tgit pull --log &&\ndiff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh\nindex 408c886..accfa5c 100755\n--- a/t/t5572-pull-submodule.sh\n+++ b/t/t5572-pull-submodule.sh\n@@ -5,10 +5,6 @@ test_description='pull can handle submodules'\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-submodule-update.sh\n \n-KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES=1\n-KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1\n-KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1\n-\n reset_branch_to_HEAD () {\n \tgit branch -D \"$1\" &&\n \tgit checkout -b \"$1\" HEAD &&\ndiff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh\nindex 54af64e..73fc240 100755\n--- a/t/t6029-merge-subtree.sh\n+++ b/t/t6029-merge-subtree.sh\n@@ -61,7 +61,7 @@ test_expect_success 'initial merge' '\n \ttest_cmp expected actual\n '\n \n-test_expect_failure 'merge update' '\n+test_expect_success 'merge update' '\n \tcd ../git-gui &&\n \techo git-gui2 > git-gui.sh &&\n \to3=$(git hash-object git-gui.sh) &&\n@@ -95,7 +95,7 @@ test_expect_success 'initial ambiguous subtree' '\n \ttest_cmp expected actual\n '\n \n-test_expect_failure 'merge using explicit' '\n+test_expect_success 'merge using explicit' '\n \tcd ../git &&\n \tgit reset --hard master2 &&\n \tgit pull -Xsubtree=git-gui gui master2 &&\n@@ -108,7 +108,7 @@ test_expect_failure 'merge using explicit' '\n \ttest_cmp expected actual\n '\n \n-test_expect_failure 'merge2 using explicit' '\n+test_expect_success 'merge2 using explicit' '\n \tcd ../git &&\n \tgit reset --hard master2 &&\n \tgit pull -Xsubtree=git-gui2 gui master2 &&\ndiff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh\nindex 876e23b..3889eca 100755\n--- a/t/t6037-merge-ours-theirs.sh\n+++ b/t/t6037-merge-ours-theirs.sh\n@@ -65,7 +65,7 @@ test_expect_success 'binary file with -Xours/-Xtheirs' '\n \tgit diff --exit-code master HEAD -- file\n '\n \n-test_expect_failure 'pull passes -X to underlying merge' '\n+test_expect_success 'pull passes -X to underlying merge' '\n \tgit reset --hard master && git pull -s recursive -Xours . side &&\n \tgit reset --hard master && git pull -s recursive -X ours . side &&\n \tgit reset --hard master && git pull -s recursive -Xtheirs . side &&\n-- \n2.1.4\n"},{"id":"261457","messageId":"1431961571-20370-5-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 04/14] pull: pass git-fetch's options to git-fetch","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:01Z","receivedAt":"2015-05-18T15:06:01Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"git-pull.sh passed all unknown options to git-fetch, thus supporting\ngit-fetch's options.\n\nHowever, this led to problems as git-pull was not aware of the specifics\nof the options passed to git-fetch. For example, running\n\n\tgit-pull --all --dry-run\n\nwould pass the --dry-fetch option to git-fetch, but would still run\ngit-merge. This is because git-pull.sh is not aware of the --dry-run\noption.\n\nFix this by parsing all of git-fetch's options at git-pull. The options\nare:\n\n* --all\n\n* -a, --append\n\n* --upload-pack\n\n* -f, --force\n\n* -t, --tags\n\n* -p, --prune\n\n* --recurse-submodules\n\n* --dry-run\n\n* -k, --keep\n\n* --depth\n\n* --unshallow\n\n* --update-shallow\n\n* --refmap\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: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c            | 95 +++++++++++++++++++++++++++++++++++++++++++++++\n t/t5500-fetch-pack.sh     | 10 ++---\n t/t5521-pull-options.sh   |  6 +--\n t/t7403-submodule-sync.sh |  8 ++--\n 4 files changed, 107 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 573e4f6..07ad783 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -56,6 +56,21 @@ static struct string_list opt_strategies = STRING_LIST_INIT_NODUP;\n static struct string_list opt_strategy_opts = STRING_LIST_INIT_NODUP;\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@@ -104,6 +119,46 @@ static struct option pull_options[] = {\n \t  N_(\"GPG sign commit\"),\n \t  PARSE_OPT_OPTARG, parse_opt_passthru, (intptr_t) \"gpg-sign\" },\n \n+\t/* Options passed to git-fetch */\n+\tOPT_GROUP(N_(\"Options related to fetching\")),\n+\t{ OPTION_CALLBACK, 0, \"all\", &opt_all, 0,\n+\t  N_(\"fetch from all remotes\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"all\" },\n+\t{ OPTION_CALLBACK, 'a', \"append\", &opt_append, 0,\n+\t  N_(\"append to .git/FETCH_HEAD instead of overwriting\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"append\" },\n+\t{ OPTION_CALLBACK, 0, \"upload-pack\", &opt_upload_pack, N_(\"path\"),\n+\t  N_(\"path to upload pack on remote end\"),\n+\t  0, parse_opt_passthru, (intptr_t) \"upload-pack\" },\n+\tOPT__FORCE(&opt_force, N_(\"force overwrite of local branch\")),\n+\t{ OPTION_CALLBACK, 't', \"tags\", &opt_tags, 0,\n+\t  N_(\"fetch all tags and associated objects\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"tags\" },\n+\t{ OPTION_CALLBACK, 'p', \"prune\", &opt_prune, 0,\n+\t  N_(\"prune remote-tracking branches no longer on remote\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"prune\" },\n+\t{ OPTION_CALLBACK, 0, \"recurse-submodules\", &opt_recurse_submodules,\n+\t  N_(\"on-demand\"),\n+\t  N_(\"control recursive fetching of submodules\"),\n+\t  PARSE_OPT_OPTARG, parse_opt_passthru, (intptr_t) \"recurse-submodules\" },\n+\tOPT_BOOL(0, \"dry-run\", &opt_dry_run,\n+\t\tN_(\"dry run\")),\n+\t{ OPTION_CALLBACK, 'k', \"keep\", &opt_keep, 0,\n+\t  N_(\"keep downloaded pack\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"keep\" },\n+\t{ OPTION_CALLBACK, 0, \"depth\", &opt_depth, N_(\"depth\"),\n+\t  N_(\"deepen history of shallow clone\"),\n+\t  0, parse_opt_passthru, (intptr_t) \"depth\" },\n+\t{ OPTION_CALLBACK, 0, \"unshallow\", &opt_unshallow, 0,\n+\t  N_(\"convert to a complete repository\"),\n+\t  PARSE_OPT_NONEG | PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"unshallow\" },\n+\t{ OPTION_CALLBACK, 0, \"update-shallow\", &opt_update_shallow, 0,\n+\t  N_(\"accept refs that update .git/shallow\"),\n+\t  PARSE_OPT_NOARG, parse_opt_passthru, (intptr_t) \"update-shallow\" },\n+\t{ OPTION_CALLBACK, 0, \"refmap\", &opt_refmap, N_(\"refmap\"),\n+\t  N_(\"specify fetch refmap\"),\n+\t  PARSE_OPT_NONEG, parse_opt_passthru, (intptr_t) \"refmap\" },\n+\n \tOPT_END()\n };\n \n@@ -143,6 +198,16 @@ static void argv_push_strategy_opts(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@@ -173,6 +238,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 \twhile (*refspecs)\n@@ -233,5 +325,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 }\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 213b231..3a9b775 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -226,14 +226,14 @@ test_expect_success 'add two more (part 2)' '\n \tadd B69 $B68\n '\n \n-test_expect_failure 'deepening pull in shallow repo' '\n+test_expect_success 'deepening pull in shallow repo' '\n \t(\n \t\tcd shallow &&\n \t\tgit pull --depth 4 .. B\n \t)\n '\n \n-test_expect_failure 'clone shallow object count' '\n+test_expect_success 'clone shallow object count' '\n \t(\n \t\tcd shallow &&\n \t\tgit count-objects -v\n@@ -248,7 +248,7 @@ test_expect_success 'deepening fetch in shallow repo' '\n \t)\n '\n \n-test_expect_failure 'clone shallow object count' '\n+test_expect_success 'clone shallow object count' '\n \t(\n \t\tcd shallow &&\n \t\tgit count-objects -v\n@@ -272,11 +272,11 @@ test_expect_success 'additional simple shallow deepenings' '\n \t)\n '\n \n-test_expect_failure 'clone shallow depth count' '\n+test_expect_success 'clone shallow depth count' '\n \ttest \"`git --git-dir=shallow/.git rev-list --count HEAD`\" = 11\n '\n \n-test_expect_failure 'clone shallow object count' '\n+test_expect_success 'clone shallow object count' '\n \t(\n \t\tcd shallow &&\n \t\tgit count-objects -v\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex 89e2104..4176e11 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -78,7 +78,7 @@ test_expect_success 'git pull -q -v' '\n \ttest -s err)\n '\n \n-test_expect_failure 'git pull --force' '\n+test_expect_success 'git pull --force' '\n \tmkdir clonedoldstyle &&\n \t(cd clonedoldstyle && git init &&\n \tcat >>.git/config <<-\\EOF &&\n@@ -99,7 +99,7 @@ test_expect_failure 'git pull --force' '\n \t)\n '\n \n-test_expect_failure 'git pull --all' '\n+test_expect_success 'git pull --all' '\n \tmkdir clonedmulti &&\n \t(cd clonedmulti && git init &&\n \tcat >>.git/config <<-\\EOF &&\n@@ -117,7 +117,7 @@ test_expect_failure 'git pull --all' '\n \t)\n '\n \n-test_expect_failure 'git pull --dry-run' '\n+test_expect_success 'git pull --dry-run' '\n \ttest_when_finished \"rm -rf clonedry\" &&\n \tgit init clonedry &&\n \t(\ndiff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh\nindex b448562..79bc135 100755\n--- a/t/t7403-submodule-sync.sh\n+++ b/t/t7403-submodule-sync.sh\n@@ -96,7 +96,7 @@ test_expect_success 'change submodule url' '\n \t)\n '\n \n-test_expect_failure '\"git submodule sync\" should update submodule URLs' '\n+test_expect_success '\"git submodule sync\" should update submodule URLs' '\n \t(\n \t\tcd super-clone &&\n \t\tgit pull --no-recurse-submodules &&\n@@ -121,7 +121,7 @@ test_expect_failure '\"git submodule sync\" should update submodule URLs' '\n \t)\n '\n \n-test_expect_failure '\"git submodule sync --recursive\" should update all submodule URLs' '\n+test_expect_success '\"git submodule sync --recursive\" should update all submodule URLs' '\n \t(\n \t\tcd super-clone &&\n \t\t(\n@@ -149,7 +149,7 @@ test_expect_success 'reset submodule URLs' '\n \treset_submodule_urls super-clone\n '\n \n-test_expect_failure '\"git submodule sync\" should update submodule URLs - subdirectory' '\n+test_expect_success '\"git submodule sync\" should update submodule URLs - subdirectory' '\n \t(\n \t\tcd super-clone &&\n \t\tgit pull --no-recurse-submodules &&\n@@ -177,7 +177,7 @@ test_expect_failure '\"git submodule sync\" should update submodule URLs - subdire\n \t)\n '\n \n-test_expect_failure '\"git submodule sync --recursive\" should update all submodule URLs - subdirectory' '\n+test_expect_success '\"git submodule sync --recursive\" should update all submodule URLs - subdirectory' '\n \t(\n \t\tcd super-clone &&\n \t\t(\n-- \n2.1.4\n"},{"id":"261458","messageId":"1431961571-20370-6-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 05/14] pull: error on no merge candidates","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:02Z","receivedAt":"2015-05-18T15:06: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\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c  | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t5520-pull.sh |  10 ++---\n 2 files changed, 137 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 07ad783..8982fdf 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -10,6 +10,8 @@\n #include \"parse-options.h\"\n #include \"argv-array.h\"\n #include \"run-command.h\"\n+#include \"sha1-array.h\"\n+#include \"remote.h\"\n \n /**\n  * Given an option opt, where opt->value points to a char* and opt->defval is a\n@@ -207,6 +209,130 @@ static void argv_push_force(struct argv_array *arr)\n \t\targv_array_push(arr, \"-f\");\n }\n \n+struct known_remote {\n+\tstruct known_remote *next;\n+\tstruct remote *remote;\n+};\n+\n+/**\n+ * Use this callback with for_each_remote() to get the configured remotes as\n+ * a singly linked known_remote list. cb_data must be a pointer to a\n+ * struct known_remote*, which must be initialized to NULL. For example,\n+ * For example:\n+ *\n+ *     struct known_remote *list = NULL;\n+ *     for_each_remote(add_known_remote, &list);\n+ */\n+static int add_known_remote(struct remote *remote, void *cb_data)\n+{\n+\tstruct known_remote **list = cb_data;\n+\tstruct known_remote *item;\n+\n+\titem = xmalloc(sizeof(*item));\n+\titem->remote = remote;\n+\titem->next = *list;\n+\t*list = item;\n+\treturn 0;\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\"))\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+ * 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(stderr,\n+\t\t\t_(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n+\t\t\t\"Generally this means that you provided a wildcard refspec which had no\\n\"\n+\t\t\t\"matches on the remote end.\\n\"));\n+\t} else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {\n+\t\tfprintf(stderr,\n+\t\t\t_(\"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\"),\n+\t\t\trepo);\n+\t} else if (!curr_branch) {\n+\t\tfprintf(stderr,\n+\t\t\t_(\"You are not currently on a branch. Please specify which\\n\"\n+\t\t\t\"branch you want to merge with. See git-pull(1) for details.\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"    git pull <remote> <branch>\\n\"\n+\t\t\t\"\\n\"));\n+\t} else if (!curr_branch->merge_nr) {\n+\t\tstruct known_remote *remotes = NULL;\n+\t\tconst char *remote_name = \"<remote>\";\n+\n+\t\tfor_each_remote(add_known_remote, &remotes);\n+\t\tif (remotes && !remotes->next)\n+\t\t\tremote_name = remotes->remote->name;\n+\n+\t\tfprintf(stderr,\n+\t\t\t_(\"There is no tracking information for the current branch.\\n\"\n+\t\t\t\"Please specify which branch you want to merge with.\\n\"\n+\t\t\t\"See git-pull(1) for details.\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"    git pull <remote> <branch>\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"If you wish to set tracking information for this branch you can do so with:\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"    git branch --set-upstream-to=%s/<branch> %s\\n\"\n+\t\t\t\"\\n\"),\n+\t\t\tremote_name, curr_branch->name);\n+\t} else\n+\t\tfprintf(stderr,\n+\t\t\t_(\"Your configuration specifies to merge with the ref '%s'\\n\"\n+\t\t\t\"from the remote, but no such ref was fetched.\\n\"\n+\t\t\t\"\\n\"),\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@@ -318,6 +444,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 \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n@@ -328,5 +455,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 }\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex f32b8cb..0ff5df3 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -116,7 +116,7 @@ test_expect_failure 'the default remote . should not break explicit pull' '\n \ttest_cmp reflog.expected reflog.fuzzy\n '\n \n-test_expect_failure 'fail if wildcard spec does not match any refs' '\n+test_expect_success 'fail if wildcard spec does not match any refs' '\n \tgit checkout -b test copy^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D test\" &&\n \ttest \"$(cat file)\" = file &&\n@@ -125,7 +125,7 @@ test_expect_failure 'fail if wildcard spec does not match any refs' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_failure 'fail if no branches specified with non-default remote' '\n+test_expect_success 'fail if no branches specified with non-default remote' '\n \tgit remote add test_remote . &&\n \ttest_when_finished \"git remote remove test_remote\" &&\n \tgit checkout -b test copy^ &&\n@@ -137,7 +137,7 @@ test_expect_failure 'fail if no branches specified with non-default remote' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_failure 'fail if not on a branch' '\n+test_expect_success 'fail if not on a branch' '\n \tgit remote add origin . &&\n \ttest_when_finished \"git remote remove origin\" &&\n \tgit checkout HEAD^ &&\n@@ -148,7 +148,7 @@ test_expect_failure 'fail if not on a branch' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_failure 'fail if no configuration for current branch' '\n+test_expect_success 'fail if no configuration for current branch' '\n \tgit remote add test_remote . &&\n \ttest_when_finished \"git remote remove test_remote\" &&\n \tgit checkout -b test copy^ &&\n@@ -160,7 +160,7 @@ test_expect_failure 'fail if no configuration for current branch' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_failure 'fail if upstream branch does not exist' '\n+test_expect_success 'fail if upstream branch does not exist' '\n \tgit checkout -b test copy^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D test\" &&\n \ttest_config branch.test.remote . &&\n-- \n2.1.4\n"},{"id":"261462","messageId":"1431961571-20370-7-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 06/14] pull: support pull.ff config","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:03Z","receivedAt":"2015-05-18T15:06: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               | 25 +++++++++++++++++++++++++\n t/t7601-merge-pull-config.sh |  4 ++--\n 2 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 8982fdf..b305a47 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -209,6 +209,28 @@ static void argv_push_force(struct argv_array *arr)\n \t\targv_array_push(arr, \"-f\");\n }\n \n+/**\n+ * If pull.ff is \"true\", returns \"--ff\". If pull.ff is \"false\", returns\n+ * \"--no-ff\". If pull.ff is \"only\", returns \"--ff-only\". Otherwise, returns\n+ * NULL.\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+\tswitch (git_config_maybe_bool(\"pull.ff\", value)) {\n+\t\tcase 0:\n+\t\t\treturn \"--no-ff\";\n+\t\tcase 1:\n+\t\t\treturn \"--ff\";\n+\t}\n+\tif (!strcmp(\"pull.ff\", \"only\"))\n+\t\treturn \"--ff-only\";\n+\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n+}\n+\n struct known_remote {\n \tstruct known_remote *next;\n \tstruct remote *remote;\n@@ -449,6 +471,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\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 \ndiff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh\nindex 7a846a2..f768c90 100755\n--- a/t/t7601-merge-pull-config.sh\n+++ b/t/t7601-merge-pull-config.sh\n@@ -45,7 +45,7 @@ test_expect_success 'fast-forward pull succeeds with \"true\" in pull.ff' '\n \ttest \"$(git rev-parse HEAD)\" = \"$(git rev-parse c1)\"\n '\n \n-test_expect_failure 'fast-forward pull creates merge with \"false\" in pull.ff' '\n+test_expect_success 'fast-forward pull creates merge with \"false\" in pull.ff' '\n \tgit reset --hard c0 &&\n \ttest_config pull.ff false &&\n \tgit pull . c1 &&\n@@ -53,7 +53,7 @@ test_expect_failure 'fast-forward pull creates merge with \"false\" in pull.ff' '\n \ttest \"$(git rev-parse HEAD^2)\" = \"$(git rev-parse c1)\"\n '\n \n-test_expect_failure 'pull prevents non-fast-forward with \"only\" in pull.ff' '\n+test_expect_success 'pull prevents non-fast-forward with \"only\" in pull.ff' '\n \tgit reset --hard c1 &&\n \ttest_config pull.ff only &&\n \ttest_must_fail git pull . c3\n-- \n2.1.4\n"},{"id":"261460","messageId":"1431961571-20370-8-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 07/14] pull: check if in unresolved merge state","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:04Z","receivedAt":"2015-05-18T15:06:04Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Since, commit d38a30d (Be more user-friendly when refusing to do\nsomething because of conflict., 2010-01-12), git-pull will error out\nwith user-friendly advices if the user is in the middle of a merge or\nhas unmerged 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 t/t5520-pull.sh | 2 +-\n 4 files changed, 19 insertions(+), 1 deletion(-)\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 b305a47..9b06dfa 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 /**\n  * Given an option opt, where opt->value points to a char* and opt->defval is a\n@@ -471,6 +472,14 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\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 (!opt_ff)\n \t\topt_ff = xstrdup_or_null(config_get_ff());\n \ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 0ff5df3..4c52175 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -171,7 +171,7 @@ test_expect_success 'fail if upstream branch does not exist' '\n \ttest \"$(cat file)\" = file\n '\n \n-test_expect_failure 'fail if the index has unresolved entries' '\n+test_expect_success 'fail if the index has unresolved entries' '\n \tgit checkout -b third second^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D third\" &&\n \ttest \"$(cat file)\" = file &&\n-- \n2.1.4\n"},{"id":"261459","messageId":"1431961571-20370-9-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 08/14] pull: fast-forward working tree if head is updated","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:05Z","receivedAt":"2015-05-18T15:06: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 t/t5520-pull.sh |  4 ++--\n 2 files changed, 32 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 9b06dfa..3b7029f 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -468,6 +468,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 \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n@@ -483,12 +484,41 @@ 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 (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)\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 4c52175..3645a59 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -191,7 +191,7 @@ test_expect_success 'fail if the index has unresolved entries' '\n \ttest_cmp expected file\n '\n \n-test_expect_failure 'fast-forwards working tree if branch head is updated' '\n+test_expect_success 'fast-forwards working tree if branch head is updated' '\n \tgit checkout -b third second^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D third\" &&\n \ttest \"$(cat file)\" = file &&\n@@ -201,7 +201,7 @@ test_expect_failure 'fast-forwards working tree if branch head is updated' '\n \ttest \"$(git rev-parse third)\" = \"$(git rev-parse second)\"\n '\n \n-test_expect_failure 'fast-forward fails with conflicting work tree' '\n+test_expect_success 'fast-forward fails with conflicting work tree' '\n \tgit checkout -b third second^ &&\n \ttest_when_finished \"git checkout -f copy && git branch -D third\" &&\n \ttest \"$(cat file)\" = file &&\n-- \n2.1.4\n"},{"id":"261461","messageId":"1431961571-20370-10-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 09/14] pull: implement pulling into an unborn branch","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:06Z","receivedAt":"2015-05-18T15:06: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 t/t5520-pull.sh |  4 ++--\n 2 files changed, 30 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 3b7029f..ba2ff01 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 /**\n  * Given an option opt, where opt->value points to a char* and opt->defval is a\n@@ -424,6 +425,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(unsigned char merge_head[GIT_SHA1_RAWSZ],\n+\t\tunsigned char curr_head[GIT_SHA1_RAWSZ])\n+{\n+\t/*\n+\t * Two-way merge: we claim the index is 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@@ -524,5 +546,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 }\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 3645a59..2131749 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -26,7 +26,7 @@ test_expect_success 'pulling into void' '\n \ttest_cmp file cloned/file\n '\n \n-test_expect_failure 'pulling into void using master:master' '\n+test_expect_success 'pulling into void using master:master' '\n \tgit init cloned-uho &&\n \t(\n \t\tcd cloned-uho &&\n@@ -76,7 +76,7 @@ test_expect_success 'pulling into void does not remove new staged files' '\n \t)\n '\n \n-test_expect_failure 'pulling into void must not create an octopus' '\n+test_expect_success 'pulling into void must not create an octopus' '\n \tgit init cloned-octopus &&\n \t(\n \t\tcd cloned-octopus &&\n-- \n2.1.4\n"},{"id":"261463","messageId":"1431961571-20370-11-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 10/14] pull: set reflog message","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:07Z","receivedAt":"2015-05-18T15:06: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\nSigned-off-by: Stephen Robin <stephen.robin@gmail.com>\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c  | 23 +++++++++++++++++++++++\n t/t5520-pull.sh |  4 ++--\n 2 files changed, 25 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex ba2ff01..81e31a1 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -212,6 +212,26 @@ 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\tstrbuf_addstr(&msg, argv[i]);\n+\t\tstrbuf_addch(&msg, ' ');\n+\t}\n+\n+\tstrbuf_rtrim(&msg);\n+\n+\tsetenv(\"GIT_REFLOG_ACTION\", msg.buf, 0);\n+\n+\tstrbuf_release(&msg);\n+}\n+\n+/**\n  * If pull.ff is \"true\", returns \"--ff\". If pull.ff is \"false\", returns\n  * \"--no-ff\". If pull.ff is \"only\", returns \"--ff-only\". Otherwise, returns\n  * NULL.\n@@ -492,6 +512,9 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\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_REFLOG_ACTION\"))\n+\t\tset_reflog_message(argc, argv);\n+\n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n \ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 2131749..9414cc1 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -85,7 +85,7 @@ test_expect_success 'pulling into void must not create an octopus' '\n \t)\n '\n \n-test_expect_failure 'test . as a remote' '\n+test_expect_success 'test . as a remote' '\n \tgit branch copy master &&\n \tgit config branch.copy.remote . &&\n \tgit config branch.copy.merge refs/heads/master &&\n@@ -101,7 +101,7 @@ test_expect_failure 'test . as a remote' '\n \ttest_cmp reflog.expected reflog.fuzzy\n '\n \n-test_expect_failure 'the default remote . should not break explicit pull' '\n+test_expect_success 'the default remote . should not break explicit pull' '\n \tgit checkout -b second master^ &&\n \techo modified >file &&\n \tgit commit -a -m modified &&\n-- \n2.1.4\n"},{"id":"261464","messageId":"1431961571-20370-12-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 11/14] pull: teach git pull about --rebase","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:08Z","receivedAt":"2015-05-18T15:06: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\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c              | 227 +++++++++++++++++++++++++++++++++++++++++++-\n t/t5520-pull.sh             |  26 ++---\n t/t5521-pull-options.sh     |   6 +-\n t/t7406-submodule-update.sh |   2 -\n 4 files changed, 241 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 81e31a1..f18a21c 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -39,6 +39,42 @@ static int parse_opt_passthru(const struct option *opt, const char *arg, int uns\n \treturn 0;\n }\n \n+enum rebase_type {\n+\tREBASE_FALSE,\n+\tREBASE_TRUE,\n+\tREBASE_PRESERVE\n+};\n+\n+/**\n+ * Parses the value of --rebase, branch.*.rebase or pull.rebase. If value is a\n+ * false value, returns REBASE_FALSE. If value is a true value, returns\n+ * REBASE_TRUE. If value is \"preserve\", returns REBASE_PRESERVE. Otherwise,\n+ * returns -1 to signify an invalid value.\n+ */\n+static int parse_config_rebase(const char *value)\n+{\n+\tint v = git_config_maybe_bool(\"pull.rebase\", value);\n+\tif (v >= 0)\n+\t\treturn v;\n+\tif (!strcmp(value, \"preserve\"))\n+\t\treturn REBASE_PRESERVE;\n+\treturn -1;\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+\tint *value = (int*) opt->value;\n+\n+\tif (arg)\n+\t\t*value = parse_config_rebase(arg);\n+\telse\n+\t\t*value = unset ? REBASE_FALSE : REBASE_TRUE;\n+\treturn *value >= 0 ? 0 : -1;\n+}\n+\n static const char * const pull_usage[] = {\n \tN_(\"git pull [options] [<repo> [<refspec>...]]\"),\n \tNULL\n@@ -48,7 +84,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 int opt_rebase;\n static char *opt_diffstat;\n static char *opt_log;\n static char *opt_squash;\n@@ -82,8 +119,12 @@ static struct option pull_options[] = {\n \t  N_(\"force progress reporting\"),\n \t  PARSE_OPT_NOARG, parse_opt_passthru},\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, 0},\n \t{ OPTION_CALLBACK, 'n', NULL, &opt_diffstat, NULL,\n \t  N_(\"do not show a diffstat at the end of the merge\"),\n \t  PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_passthru, (intptr_t) \"no-stat\" },\n@@ -506,11 +547,185 @@ static int run_merge(void)\n \treturn ret;\n }\n \n+/**\n+ * Returns the merge branch for the current branch. Returns NULL if repo is not\n+ * a valid remote, HEAD does not point to a branch, repo is not the branch's\n+ * configured remote or the branch does not have any configured merge branch.\n+ */\n+static char *get_merge_branch_1(const char *repo)\n+{\n+\tstruct remote *rm;\n+\tstruct branch *curr_branch;\n+\n+\tif (repo && !(rm = remote_get(repo)))\n+\t\treturn NULL;\n+\tif (!(curr_branch = branch_get(\"HEAD\")))\n+\t\treturn NULL;\n+\tif (repo && curr_branch->remote != rm)\n+\t\treturn NULL;\n+\tif (!curr_branch->merge_nr)\n+\t\treturn NULL;\n+\treturn xstrdup(curr_branch->merge[0]->dst);\n+}\n+\n+/**\n+ * Given a refspec, returns the merge branch. Returns NULL if the refspec src\n+ * does not refer to a branch.\n+ *\n+ * FIXME: It should return the tracking branch. Currently only works with the\n+ * default mapping.\n+ */\n+static char *get_merge_branch_2(const char *repo, const char *refspec)\n+{\n+\tstruct refspec *spec;\n+\tconst char *remote;\n+\tchar *merge_branch;\n+\n+\tspec = parse_fetch_refspec(1, &refspec);\n+\tremote = spec->src;\n+\tif (!*remote || !strcmp(remote, \"HEAD\"))\n+\t\tremote = \"HEAD\";\n+\telse if (skip_prefix(remote, \"heads/\", &remote))\n+\t\t;\n+\telse if (skip_prefix(remote, \"refs/heads/\", &remote))\n+\t\t;\n+\telse if (starts_with(remote, \"refs/\") ||\n+\t\tstarts_with(remote, \"tags/\") ||\n+\t\tstarts_with(remote, \"remotes/\"))\n+\t\tremote = \"\";\n+\n+\tif (*remote) {\n+\t\tif (!strcmp(repo, \".\"))\n+\t\t\tmerge_branch = mkpathdup(\"refs/heads/%s\", remote);\n+\t\telse\n+\t\t\tmerge_branch = mkpathdup(\"refs/remotes/%s/%s\", repo, remote);\n+\t} else\n+\t\tmerge_branch = NULL;\n+\n+\tfree_refspec(1, spec);\n+\treturn merge_branch;\n+}\n+\n+/**\n+ * Sets fork_point to the point at which the current branch forked from its\n+ * remote merge branch. Returns 0 on success, -1 on failure.\n+ */\n+static int get_rebase_fork_point(unsigned char fork_point[GIT_SHA1_RAWSZ],\n+\t\tconst char *repo, const char *refspec)\n+{\n+\tint ret;\n+\tstruct branch *curr_branch;\n+\tchar *remote_merge_branch;\n+\tstruct argv_array args = ARGV_ARRAY_INIT;\n+\tstruct child_process cp = CHILD_PROCESS_INIT;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\n+\tif (!(curr_branch = branch_get(\"HEAD\")))\n+\t\treturn -1;\n+\n+\tif (refspec)\n+\t\tremote_merge_branch = get_merge_branch_2(repo, refspec);\n+\telse\n+\t\tremote_merge_branch = get_merge_branch_1(repo);\n+\n+\tif (!remote_merge_branch)\n+\t\treturn -1;\n+\n+\targv_array_pushl(&args, \"merge-base\", \"--fork-point\",\n+\t\t\tremote_merge_branch, curr_branch->name, NULL);\n+\tcp.argv = args.argv;\n+\tcp.no_stdin = 1;\n+\tcp.no_stderr = 1;\n+\tcp.git_cmd = 1;\n+\n+\tif ((ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ)))\n+\t\tgoto cleanup;\n+\n+\tif ((ret = get_sha1_hex(sb.buf, fork_point)))\n+\t\tgoto cleanup;\n+\n+cleanup:\n+\tfree(remote_merge_branch);\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[GIT_SHA1_HEXSZ],\n+\t\tunsigned char curr_head[GIT_SHA1_RAWSZ],\n+\t\tunsigned char merge_head[GIT_SHA1_RAWSZ],\n+\t\tunsigned char fork_point[GIT_SHA1_RAWSZ])\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(unsigned char curr_head[GIT_SHA1_RAWSZ],\n+\t\tunsigned char merge_head[GIT_SHA1_RAWSZ],\n+\t\tunsigned char fork_point[GIT_SHA1_RAWSZ])\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\thashclr(fork_point);\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_push_strategies(&args);\n+\targv_push_strategy_opts(&args);\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 (!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_REFLOG_ACTION\"))\n \t\tset_reflog_message(argc, argv);\n@@ -532,6 +747,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@@ -573,6 +792,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 }\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 9414cc1..3798b96 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -212,7 +212,7 @@ test_expect_success 'fast-forward fails with conflicting work tree' '\n \ttest \"$(git rev-parse third)\" = \"$(git rev-parse second)\"\n '\n \n-test_expect_failure '--rebase' '\n+test_expect_success '--rebase' '\n \tgit branch to-rebase &&\n \techo modified again > file &&\n \tgit commit -m file file &&\n@@ -226,7 +226,7 @@ test_expect_failure '--rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n-test_expect_failure '--rebase fails with multiple branches' '\n+test_expect_success '--rebase fails with multiple branches' '\n \tgit reset --hard before-rebase &&\n \ttest_must_fail git pull --rebase . copy master 2>err &&\n \ttest \"$(git rev-parse HEAD)\" = \"$(git rev-parse before-rebase)\" &&\n@@ -310,7 +310,7 @@ test_expect_failure 'pull.rebase=invalid fails' '\n \t! git pull . copy\n '\n \n-test_expect_failure '--rebase=false create a new merge commit' '\n+test_expect_success '--rebase=false create a new merge commit' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull --rebase=false . copy &&\n@@ -319,7 +319,7 @@ test_expect_failure '--rebase=false create a new merge commit' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_failure '--rebase=true rebases and flattens keep-merge' '\n+test_expect_success '--rebase=true rebases and flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase preserve &&\n \tgit pull --rebase=true . copy &&\n@@ -327,7 +327,7 @@ test_expect_failure '--rebase=true rebases and flattens keep-merge' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_failure '--rebase=preserve rebases and merges keep-merge' '\n+test_expect_success '--rebase=preserve rebases and merges keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull --rebase=preserve . copy &&\n@@ -340,7 +340,7 @@ test_expect_success '--rebase=invalid fails' '\n \t! git pull --rebase=invalid . copy\n '\n \n-test_expect_failure '--rebase overrides pull.rebase=preserve and flattens keep-merge' '\n+test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase preserve &&\n \tgit pull --rebase . copy &&\n@@ -348,7 +348,7 @@ test_expect_failure '--rebase overrides pull.rebase=preserve and flattens keep-m\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_failure '--rebase with rebased upstream' '\n+test_expect_success '--rebase with rebased upstream' '\n \n \tgit remote add -f me . &&\n \tgit checkout copy &&\n@@ -366,7 +366,7 @@ test_expect_failure '--rebase with rebased upstream' '\n \n '\n \n-test_expect_failure '--rebase with rebased default upstream' '\n+test_expect_success '--rebase with rebased default upstream' '\n \n \tgit update-ref refs/remotes/me/copy copy-orig &&\n \tgit checkout --track -b to-rebase2 me/copy &&\n@@ -377,7 +377,7 @@ test_expect_failure '--rebase with rebased default upstream' '\n \n '\n \n-test_expect_failure 'rebased upstream + fetch + pull --rebase' '\n+test_expect_success 'rebased upstream + fetch + pull --rebase' '\n \n \tgit update-ref refs/remotes/me/copy copy-orig &&\n \tgit reset --hard to-rebase-orig &&\n@@ -409,7 +409,7 @@ test_expect_failure 'pull --rebase dies early with dirty working directory' '\n \n '\n \n-test_expect_failure 'pull --rebase works on branch yet to be born' '\n+test_expect_success 'pull --rebase works on branch yet to be born' '\n \tgit rev-parse master >expect &&\n \tmkdir empty_repo &&\n \t(cd empty_repo &&\n@@ -456,14 +456,14 @@ test_expect_success 'setup for detecting upstreamed changes' '\n \t)\n '\n \n-test_expect_failure 'git pull --rebase detects upstreamed changes' '\n+test_expect_success 'git pull --rebase detects upstreamed changes' '\n \t(cd dst &&\n \t git pull --rebase &&\n \t test -z \"$(git ls-files -u)\"\n \t)\n '\n \n-test_expect_failure 'setup for avoiding reapplying old patches' '\n+test_expect_success 'setup for avoiding reapplying old patches' '\n \t(cd dst &&\n \t test_might_fail git rebase --abort &&\n \t git reset --hard origin/master\n@@ -485,7 +485,7 @@ test_expect_failure 'setup for avoiding reapplying old patches' '\n \t)\n '\n \n-test_expect_failure 'git pull --rebase does not reapply old patches' '\n+test_expect_success 'git pull --rebase does not reapply old patches' '\n \t(cd dst &&\n \t test_must_fail git pull --rebase &&\n \t test 1 = $(find .git/rebase-apply -name \"000*\" | wc -l)\ndiff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\nindex 4176e11..56e7377 100755\n--- a/t/t5521-pull-options.sh\n+++ b/t/t5521-pull-options.sh\n@@ -19,7 +19,7 @@ test_expect_success 'git pull -q' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_failure 'git pull -q --rebase' '\n+test_expect_success 'git pull -q --rebase' '\n \tmkdir clonedqrb &&\n \t(cd clonedqrb && git init &&\n \tgit pull -q --rebase \"../parent\" >out 2>err &&\n@@ -38,7 +38,7 @@ test_expect_success 'git pull' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_failure 'git pull --rebase' '\n+test_expect_success 'git pull --rebase' '\n \tmkdir clonedrb &&\n \t(cd clonedrb && git init &&\n \tgit pull --rebase \"../parent\" >out 2>err &&\n@@ -54,7 +54,7 @@ test_expect_success 'git pull -v' '\n \ttest_must_be_empty out)\n '\n \n-test_expect_failure 'git pull -v --rebase' '\n+test_expect_success 'git pull -v --rebase' '\n \tmkdir clonedvrb &&\n \t(cd clonedvrb && git init &&\n \tgit pull -v --rebase \"../parent\" >out 2>err &&\ndiff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\nindex 1ffd837..dda3929 100755\n--- a/t/t7406-submodule-update.sh\n+++ b/t/t7406-submodule-update.sh\n@@ -11,8 +11,6 @@ submodule and \"git submodule update --rebase/--merge\" does not detach the HEAD.\n \n . ./test-lib.sh\n \n-skip_all='skipping submodule update tests, requires git pull --rebase'\n-test_done\n \n compare_head()\n {\n-- \n2.1.4\n"},{"id":"261465","messageId":"1431961571-20370-13-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 12/14] pull: configure --rebase via branch.<name>.rebase or pull.rebase","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:09Z","receivedAt":"2015-05-18T15:06: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\nSigned-off-by: Paul Tan <pyokagan@gmail.com>\n---\n builtin/pull.c  | 35 +++++++++++++++++++++++++++++++++++\n t/t5520-pull.sh | 12 ++++++------\n 2 files changed, 41 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex f18a21c..a0958a7 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -294,6 +294,39 @@ static const char *config_get_ff(void)\n \tdie(_(\"Invalid value for pull.ff: %s\"), value);\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 int config_get_rebase(void)\n+{\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tstruct branch *curr_branch = branch_get(\"HEAD\");\n+\tconst char *key, *str;\n+\tint ret = -1, value;\n+\n+\tif (curr_branch) {\n+\t\tstrbuf_addf(&sb, \"branch.%s.rebase\", curr_branch->name);\n+\t\tkey = sb.buf;\n+\t\tret = git_config_get_value(sb.buf, &str);\n+\t}\n+\tif (ret) {\n+\t\tkey = \"pull.rebase\";\n+\t\tret = git_config_get_value(key, &str);\n+\t}\n+\tif (ret) {\n+\t\tstrbuf_release(&sb);\n+\t\treturn REBASE_FALSE;\n+\t}\n+\tif ((value = parse_config_rebase(str)) < 0)\n+\t\tdie(_(\"Invalid value for %s: %s\"), key, str);\n+\tstrbuf_release(&sb);\n+\treturn value;\n+}\n+\n struct known_remote {\n \tstruct known_remote *next;\n \tstruct remote *remote;\n@@ -730,6 +763,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (!getenv(\"GIT_REFLOG_ACTION\"))\n \t\tset_reflog_message(argc, argv);\n \n+\topt_rebase = config_get_rebase();\n+\n \targc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n \tparse_repo_refspecs(argc, argv, &repo, &refspecs);\n \ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 3798b96..17254ee 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -234,7 +234,7 @@ test_expect_success '--rebase fails with multiple branches' '\n \ttest modified = \"$(git show HEAD:file)\"\n '\n \n-test_expect_failure 'pull.rebase' '\n+test_expect_success 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull . copy &&\n@@ -242,7 +242,7 @@ test_expect_failure 'pull.rebase' '\n \ttest new = \"$(git show HEAD:file2)\"\n '\n \n-test_expect_failure 'branch.to-rebase.rebase' '\n+test_expect_success 'branch.to-rebase.rebase' '\n \tgit reset --hard before-rebase &&\n \ttest_config branch.to-rebase.rebase true &&\n \tgit pull . copy &&\n@@ -280,7 +280,7 @@ test_expect_success 'pull.rebase=false create a new merge commit' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_failure 'pull.rebase=true flattens keep-merge' '\n+test_expect_success 'pull.rebase=true flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase true &&\n \tgit pull . copy &&\n@@ -288,7 +288,7 @@ test_expect_failure 'pull.rebase=true flattens keep-merge' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_failure 'pull.rebase=1 is treated as true and flattens keep-merge' '\n+test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase 1 &&\n \tgit pull . copy &&\n@@ -296,7 +296,7 @@ test_expect_failure 'pull.rebase=1 is treated as true and flattens keep-merge' '\n \ttest file3 = \"$(git show HEAD:file3.t)\"\n '\n \n-test_expect_failure 'pull.rebase=preserve rebases and merges keep-merge' '\n+test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase preserve &&\n \tgit pull . copy &&\n@@ -304,7 +304,7 @@ test_expect_failure 'pull.rebase=preserve rebases and merges keep-merge' '\n \ttest \"$(git rev-parse HEAD^2)\" = \"$(git rev-parse keep-merge)\"\n '\n \n-test_expect_failure 'pull.rebase=invalid fails' '\n+test_expect_success 'pull.rebase=invalid fails' '\n \tgit reset --hard before-preserve-rebase &&\n \ttest_config pull.rebase invalid &&\n \t! git pull . copy\n-- \n2.1.4\n"},{"id":"261466","messageId":"1431961571-20370-14-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 13/14] pull --rebase: exit early when the working directory is dirty","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:10Z","receivedAt":"2015-05-18T15:06: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 t/t5520-pull.sh |  6 ++---\n 2 files changed, 79 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex a0958a7..c8d673d 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 /**\n  * Given an option opt, where opt->value points to a char* and opt->defval is a\n@@ -327,6 +329,73 @@ static int config_get_rebase(void)\n \treturn value;\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 struct known_remote {\n \tstruct known_remote *next;\n \tstruct remote *remote;\n@@ -782,9 +851,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;\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 17254ee..62dbfb5 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -390,7 +390,7 @@ test_expect_success 'rebased upstream + fetch + pull --rebase' '\n \n '\n \n-test_expect_failure 'pull --rebase dies early with dirty working directory' '\n+test_expect_success 'pull --rebase dies early with dirty working directory' '\n \n \tgit checkout to-rebase &&\n \tgit update-ref refs/remotes/me/copy copy^ &&\n@@ -420,7 +420,7 @@ test_expect_success 'pull --rebase works on branch yet to be born' '\n \ttest_cmp expect actual\n '\n \n-test_expect_failure 'pull --rebase fails on unborn branch with staged changes' '\n+test_expect_success 'pull --rebase fails on unborn branch with staged changes' '\n \ttest_when_finished \"rm -rf empty_repo2\" &&\n \tgit init empty_repo2 &&\n \t(\n@@ -492,7 +492,7 @@ test_expect_success 'git pull --rebase does not reapply old patches' '\n \t)\n '\n \n-test_expect_failure 'git pull --rebase against local branch' '\n+test_expect_success 'git pull --rebase against local branch' '\n \tgit checkout -b copy2 to-rebase-orig &&\n \tgit pull --rebase . to-rebase &&\n \ttest \"conflicting modification\" = \"$(cat file)\" &&\n-- \n2.1.4\n"},{"id":"261467","messageId":"1431961571-20370-15-git-send-email-pyokagan@gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"[PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-18T15:06:11Z","receivedAt":"2015-05-18T15:06: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 | 42 +++++++++++++++++++++++++-----------------\n 1 file changed, 25 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex c8d673d..15b65a0 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -474,10 +474,12 @@ 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(stderr,\n-\t\t\t_(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n-\t\t\t\"Generally this means that you provided a wildcard refspec which had no\\n\"\n-\t\t\t\"matches on the remote end.\\n\"));\n+\t\tif (opt_rebase)\n+\t\t\tfputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n+\t\telse\n+\t\t\tfputs(_(\"There are no candidates for merging among the refs that you just fetched.\"), stderr);\n+\t\tfputs(_(\"Generally this means that you provided a wildcard refspec which had no\\n\"\n+\t\t\t\"matches on the remote end.\"), stderr);\n \t} else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {\n \t\tfprintf(stderr,\n \t\t\t_(\"You asked to pull from the remote '%s', but did not specify\\n\"\n@@ -485,12 +487,15 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n \t\t\t\"for your current branch, you must specify a branch on the command line.\\n\"),\n \t\t\trepo);\n \t} else if (!curr_branch) {\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You are not currently on a branch. Please specify which\\n\"\n-\t\t\t\"branch you want to merge with. See git-pull(1) for details.\\n\"\n-\t\t\t\"\\n\"\n-\t\t\t\"    git pull <remote> <branch>\\n\"\n-\t\t\t\"\\n\"));\n+\t\tfputs(_(\"You are not currently on a branch.\"), stderr);\n+\t\tif (opt_rebase)\n+\t\t\tfputs(_(\"Please specify which branch you want to rebase against.\"), stderr);\n+\t\telse\n+\t\t\tfputs(_(\"Please specify which branch you want to merge with.\"), stderr);\n+\t\tfputs(_(\"See git-pull(1) for details.\"), stderr);\n+\t\tfputs(\"\", stderr);\n+\t\tfputs(\"    git pull <remote> <branch>\", stderr);\n+\t\tfputs(\"\", stderr);\n \t} else if (!curr_branch->merge_nr) {\n \t\tstruct known_remote *remotes = NULL;\n \t\tconst char *remote_name = \"<remote>\";\n@@ -499,14 +504,17 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n \t\tif (remotes && !remotes->next)\n \t\t\tremote_name = remotes->remote->name;\n \n+\t\tfputs(_(\"There is no tracking information for the current branch.\"), stderr);\n+\t\tif (opt_rebase)\n+\t\t\tfputs(_(\"Please specify which branch you want to rebase against.\"), stderr);\n+\t\telse\n+\t\t\tfputs(_(\"Please specify which branch you want to merge with.\"), stderr);\n+\t\tfputs(_(\"See git-pull(1) for details.\"), stderr);\n+\t\tfputs(\"\", stderr);\n+\t\tfputs(\"    git pull <remote> <branch>\", stderr);\n+\t\tfputs(\"\", stderr);\n \t\tfprintf(stderr,\n-\t\t\t_(\"There is no tracking information for the current branch.\\n\"\n-\t\t\t\"Please specify which branch you want to merge with.\\n\"\n-\t\t\t\"See git-pull(1) for details.\\n\"\n-\t\t\t\"\\n\"\n-\t\t\t\"    git pull <remote> <branch>\\n\"\n-\t\t\t\"\\n\"\n-\t\t\t\"If you wish to set tracking information for this branch you can do so with:\\n\"\n+\t\t\t_(\"If you wish to set tracking information for this branch you can do so with:\\n\"\n \t\t\t\"\\n\"\n \t\t\t\"    git branch --set-upstream-to=%s/<branch> %s\\n\"\n \t\t\t\"\\n\"),\n-- \n2.1.4\n"},{"id":"261474","messageId":"8fdf250d93dc34505fc77f01b415a43b@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-2-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 01/14] pull: implement fetch + merge","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T15:55:57Z","receivedAt":"2015-05-18T15:55:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:05, Paul Tan wrote:\n> Start off the rewrite of git-pull.sh to C by implementing a simple\n> git-pull builtin that:\n> \n> 1. Parses the command line arguments into [<repo> [<refspecs>...]]\n> \n> 2. Runs git-fetch, passing the repo and refspecs to it.\n> \n> 3. Runs git-merge FETCH_HEAD.\n> \n> Due to missing features, several tests are disabled:\n> \n> * Passing flags to git fetch, rebase, merge:\n> \n>   * t5521: verbosity flags -v, -s\n> \n> * Passing flags to git-merge, git-rebase:\n> \n>   * t5150, t5572: Pass --ff, --no-ff, --ff-only to git-merge\n> \n>   * t5520, t5521, t7406: Support --rebase, branch.*.rebase and\n>     pull.rebase config\n> \n>   * t6029, t4013: -s/--strategy\n> \n>   * t6037: -X/--strategy-option\n> \n>   * t5524: --log\n> \n> * Passing flags to git-fetch:\n> \n>   * t5521: --dry-run\n> \n>   * t5500: --depth\n> \n>   * t5521: --force\n> \n>   * t5521: --all\n> \n>   * t7403: --no-recurse-submodules\n> \n> * t7601: pull.ff config sets --ff, --no-ff, --ff-only\n> \n> * t5520: branch.*.rebase or pull.rebase sets --rebase, --rebase=preserve\n> \n> * t5520: Pulling into void\n> \n> * t5520: appropriate user-friendly error messages on no merge candidates\n>   case\n> \n> * t5520: appropriate user-friendly error messages if index has\n>   unresolved entries.\n> \n> * t5520: fast-forward working tree if branch head is updated by\n>   git-fetch\n> \n> * t5520: set reflog action to \"pull args...\"\n> \n> These features will be re-implemented in succeeding patches.\n> \n> Signed-off-by: Paul Tan <pyokagan@gmail.com>\n> ---\n>  Makefile                     |   2 +-\n>  builtin.h                    |   1 +\n>  builtin/pull.c               |  83 +++++++++++\n>  contrib/examples/git-pull.sh | 339 +++++++++++++++++++++++++++++++++++++++++++\n>  git-pull.sh                  | 339 -------------------------------------------\n>  git.c                        |   1 +\n>  t/t4013-diff-various.sh      |   3 +\n>  t/t5150-request-pull.sh      |   2 +-\n>  t/t5500-fetch-pack.sh        |  10 +-\n>  t/t5520-pull.sh              |  68 ++++-----\n>  t/t5521-pull-options.sh      |  20 +--\n>  t/t5524-pull-msg.sh          |   2 +-\n>  t/t5572-pull-submodule.sh    |   4 +\n>  t/t6029-merge-subtree.sh     |   6 +-\n>  t/t6037-merge-ours-theirs.sh |   2 +-\n>  t/t7403-submodule-sync.sh    |   8 +-\n>  t/t7406-submodule-update.sh  |   2 +\n>  t/t7601-merge-pull-config.sh |   4 +-\n\nThis is an excellent way to develop the patch series. For the final patch series, I would like to propose that `git-pull.sh` is replaced by the builtin with the very last patch, though. That way, the builtin can be implemented in the way you did, but without touching the tests. In the final form, the builtin git-pull will be a drop-in replacement for the shell script, and the final patch will just make that switch.\n\nIn other words, patch 1/N would leave SCRIPT_SH in the Makefile untouched, and leave out the \"pull\" line from `git.c`.\n\nOh, by the way, if you pass the `-M` option to `git format-patch`, the diffstat will report the move rather than an added and a deleted git-pull.sh.\n\nCiao,\nDscho\n"},{"id":"261476","messageId":"4213f4fa8fb52fb020c2e9b5d78fbf7b@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-3-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 02/14] pull: pass verbosity, --progress flags to fetch and merge","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T17:41:19Z","receivedAt":"2015-05-18T17:41:19Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:05, Paul Tan wrote:\n\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 0b771b9..a4d9c92 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -11,16 +11,64 @@\n>  #include \"argv-array.h\"\n>  #include \"run-command.h\"\n>  \n> +/**\n> + * Given an option opt, where opt->value points to a char* and opt->defval is a\n> + * string, sets opt->value to the evaluation of \"--$defval=$arg\". If `arg` is\n> + * NULL, then opt->value is set to \"--$defval\". If unset is true, then\n> + * opt->value is set to \"--no-$defval\".\n> + */\n> +static int parse_opt_passthru(const struct option *opt, const char\n> *arg, int unset)\n\nHow about adding this to parse-options-cb.c in a separate patch? I guess the description could say something like:\n\n/**\n * Given an option opt, recreate the command-line option, as strbuf. This is useful\n * when a command needs to parse a command-line option in order to pass it to yet\n * another command. This callback can be used in conjunction with the\n * PARSE_OPT_(OPTARG|NOARG|NONEG) options, but not with PARSE_OPT_LASTARG_DEFAULT\n * because it expects `defval` to be the name of the command-line option to\n * reconstruct.\n *\n * The result will be stored in opt->value, which is expected to be a pointer to an\n * strbuf.\n */\n\nImplied by my suggested description, I also propose to put the re-recreated command-line option into a strbuf instead of a char *, to make memory management easier (read: avoid memory leaks).\n\nYou might also want to verify that arg is `NULL` when `unset != 0`. Something like this:\n\nint parse_opt_passthru(const struct option *opt, const char *arg, int unset)\n{\n\tstruct strbuf *buf = opt->value;\n\n\tassert(opt->defval);\n\tstrbuf_reset(buf);\n\tif (unset) {\n\t\tassert(!arg);\n\t\tstrbuf_addf(buf, \"--no-%s\", opt->defval);\n\t}\n\telse {\n\t\tstrbuf_addf(buf, \"--%s\", opt->defval);\n\t\tif (arg)\n\t\t\tstrbuf_addf(buf, \"=%s\", arg);\n\t}\n\n\treturn 0;\n}\n\n>  static struct option pull_options[] = {\n> +\t/* Shared options */\n> +\tOPT__VERBOSITY(&opt_verbosity),\n> +\t{ OPTION_CALLBACK, 0, \"progress\", &opt_progress, NULL,\n> +\t  N_(\"force progress reporting\"),\n> +\t  PARSE_OPT_NOARG, parse_opt_passthru},\n\nI *think* there is a '(intptr_t) \"progress\"' missing...\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\nHmm... I guess this is *really* nit-picky, but I'd rather use \"i\" instead of \"verbosity\" because the second loop is about quietness instead of verbosity ;-)\n\nCiao,\nDscho\n"},{"id":"261493","messageId":"5c9746a0a2ce12b200c472bc534fd770@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-6-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 05/14] pull: error on no merge candidates","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T18:56:00Z","receivedAt":"2015-05-18T18:56:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:06, Paul Tan wrote:\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 07ad783..8982fdf 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -207,6 +209,130 @@ static void argv_push_force(struct argv_array *arr)\n>  \t\targv_array_push(arr, \"-f\");\n>  }\n>  \n> +struct known_remote {\n> +\tstruct known_remote *next;\n> +\tstruct remote *remote;\n> +};\n> +\n> +/**\n> + * Use this callback with for_each_remote() to get the configured remotes as\n> + * a singly linked known_remote list. cb_data must be a pointer to a\n> + * struct known_remote*, which must be initialized to NULL. For example,\n> + * For example:\n> + *\n> + *     struct known_remote *list = NULL;\n> + *     for_each_remote(add_known_remote, &list);\n> + */\n> +static int add_known_remote(struct remote *remote, void *cb_data)\n> +{\n> +\tstruct known_remote **list = cb_data;\n> +\tstruct known_remote *item;\n> +\n> +\titem = xmalloc(sizeof(*item));\n> +\titem->remote = remote;\n> +\titem->next = *list;\n> +\t*list = item;\n> +\treturn 0;\n> +}\n\nMy first reaction to this was: let's use an array and `ALLOC_GROW()` to make this look nicer. But then I saw that there is only one user:\n\n> +static void NORETURN die_no_merge_candidates(const char *repo, const\n> char **refspecs)\n> +{\n> [...]\n> +\t} else if (!curr_branch->merge_nr) {\n> +\t\tstruct known_remote *remotes = NULL;\n> +\t\tconst char *remote_name = \"<remote>\";\n> +\n> +\t\tfor_each_remote(add_known_remote, &remotes);\n> +\t\tif (remotes && !remotes->next)\n> +\t\t\tremote_name = remotes->remote->name;\n> +\n> [...]\n\nHow about this instead:\n\nstatic int get_only_remote(struct remote *remote, void *cb_data)\n{\n\tconst char **p = cb_data;\n\n\tif (*p)\n\t\treturn -1;\n\n\t*p = remote->name;\n\treturn 0;\n}\n\n[...]\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\nCiao,\nDscho\n"},{"id":"261494","messageId":"86694e742e55b6db71ec30d43c5643d7@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-7-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 06/14] pull: support pull.ff config","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T19:02:32Z","receivedAt":"2015-05-18T19:02:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:06, Paul Tan wrote:\n\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 8982fdf..b305a47 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -209,6 +209,28 @@ static void argv_push_force(struct argv_array *arr)\n>  \t\targv_array_push(arr, \"-f\");\n>  }\n>  \n> +/**\n> + * If pull.ff is \"true\", returns \"--ff\". If pull.ff is \"false\", returns\n> + * \"--no-ff\". If pull.ff is \"only\", returns \"--ff-only\". Otherwise, returns\n> + * NULL.\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> +\tswitch (git_config_maybe_bool(\"pull.ff\", value)) {\n> +\t\tcase 0:\n> +\t\t\treturn \"--no-ff\";\n> +\t\tcase 1:\n> +\t\t\treturn \"--ff\";\n> +\t}\n> +\tif (!strcmp(\"pull.ff\", \"only\"))\n\nI think you want to test `!strcmp(\"only\", value)` ;-)\n\nCiao,\nDscho\n"},{"id":"261495","messageId":"d9c322801fb2854061c63bf517e82d8f@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-8-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 07/14] pull: check if in unresolved merge state","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T19:06:33Z","receivedAt":"2015-05-18T19:06:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:06, Paul Tan wrote:\n> Since, commit d38a30d (Be more user-friendly when refusing to do\n\nLet's remove this comma ;-) The rest of the patch looks fine to my eyes.\n\nCiao,\nDscho\n"},{"id":"261504","messageId":"4148931c0c9476093184fd252f8428d9@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-9-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 08/14] pull: fast-forward working tree if head is updated","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T19:18:32Z","receivedAt":"2015-05-18T19:18:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:06, Paul Tan wrote:\n> Since b10ac50 (Fix pulling into the same branch., 2005-08-25), git-pull,\n> upon detecting that git-fetch updated the current head, will\n> fast-forward the working tree to the updated head commit.\n\nThis is just a quick note about something I noticed while reviewing: I was surprised that this code path would only trigger when `orig_head` is different from the null SHA-1, because in my mind, it is safe to update when we are on an unborn branch. However, I verified that the shell script does the same thing, so your transcription is faithful.\n\nCiao,\nDscho\n"},{"id":"261505","messageId":"xmqqh9r9bsji.fsf@gitster.dls.corp.google.com","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 00/14] Make git-pull a builtin","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-05-18T19:21:37Z","receivedAt":"2015-05-18T19:21:37Z","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 series rewrites git-pull.sh into a C builtin, thus improving its\n> performance and portability. It is part of my GSoC project to rewrite git-pull\n> and git-am into builtins[2].\n\nEarlier you were worried about 'git pull' being used in many tests\nfor the purpose of testing other parts of the system and not testing\n'pull' itself.  For a program as complex as 'git pull', you may want\nto take the 'competing implementations' approach.\n\n(1) write an empty cmd_pull() that relaunches \"git pull\" scripted\n    porcelain from the $GIT_EXEC_PATH with given parameters, and\n    wire all the necessary bits to git.c.\n\n(2) enhance cmd_pull() a bit by bit, but keep something like this\n\n\tif (getenv(GIT_USE_BUILTIN_PULL)) {\n        \t/* original run_command(\"git-pull.sh\") code */\n                exit $?\n\t}\n\n        ... your \"C\" version ...\n\n(3) add \"GIT_USE_BUILTIN_PULL=Yes; export GIT_USE_BUILTIN_PULL\" at\n    the beginning of \"t55??\" test scripts (but not others that rely\n    on working pull and that are not interested in catching bugs in\n    pull).\n\n(4) once cmd_pull() becomes fully operational, drop (3) and also the\n    conditional one you added in (2), and retire the environment\n    variable.  Retire the git-pull.sh script to contrib/examples/\n    boneyard.\n\nThat way, you will always have a reference you can use throughout\nthe development.\n\nJust a suggestion, not a requirement.\n"},{"id":"261506","messageId":"390e789e8f84122d52185f22e6e798f2@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-11-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 10/14] pull: set reflog message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T19:27:36Z","receivedAt":"2015-05-18T19:27:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-18 17:06, Paul Tan wrote:\n\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index ba2ff01..81e31a1 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -212,6 +212,26 @@ 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\tstrbuf_addstr(&msg, argv[i]);\n> +\t\tstrbuf_addch(&msg, ' ');\n> +\t}\n> +\n> +\tstrbuf_rtrim(&msg);\n\nSince this is not performance critical code, I would be slightly in favor of simplifying thusly:\n\n/* Join the argument list, separated by spaces */\nfor (i = 0; i < argc; i++)\n\tstrbuf_addf(&msg, \"%s%s\", i ? \" \" : \"\", argv[i]);\n\nJust a suggestion,\nDscho\n"},{"id":"261508","messageId":"33b02d3c1d68d61c9d81eabfbaf6be48@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-1-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 00/14] Make git-pull a builtin","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-18T19:41:12Z","receivedAt":"2015-05-18T19:41:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nI already commented on patches 1-10, and I will look in depth at 11-14 tomorrow.\n\nIt is a very pleasant read so far. Thank you.\n\nCiao,\nDscho\n\nP.S.: I have the feeling about patch 12 that reading `opt_rebase` from the config could be delayed until after we know that the user did not specify `--rebase`, but I'll have to think about that more deeply.\n"},{"id":"261519","messageId":"xmqqmw11a6yk.fsf@gitster.dls.corp.google.com","threadId":"39357","inReplyTo":"390e789e8f84122d52185f22e6e798f2@www.dscho.org","subject":"Re: [PATCH 10/14] pull: set reflog message","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-05-18T21:53:07Z","receivedAt":"2015-05-18T21:53:07Z","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>>  /**\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\tstrbuf_addstr(&msg, argv[i]);\n>> +\t\tstrbuf_addch(&msg, ' ');\n>> +\t}\n>> +\n>> +\tstrbuf_rtrim(&msg);\n>\n> Since this is not performance critical code, I would be slightly in\n> favor of simplifying thusly:\n>\n> /* Join the argument list, separated by spaces */\n> for (i = 0; i < argc; i++)\n> \tstrbuf_addf(&msg, \"%s%s\", i ? \" \" : \"\", argv[i]);\n\nYeah, either that, or \"insert separator only before adding to\nsomething else\" pattern, i.e.\n\n\tfor (i = 0; i < argc; i++) {\n        \tif (i)\n                \taddch(&msg, ' ');\n\t\taddstr(&msg, argv[i]);\n\t}\n\nBoth are easier to read than \"always append SP and trim at the end\".\nBesides, trimming at the end makes readers wonder if there are cases\nwhere argv[argc-1] ends with whitespaces and the code on purpose\nremoves them.\n\n\n\n\n                        \t\n"},{"id":"261525","messageId":"CAGZ79kaY6vr6EqRs7DzZnSBDVhB0oW4=LRg8+cGqKzm45aVypg@mail.gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-12-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 11/14] pull: teach git pull about --rebase","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2015-05-18T23:36:15Z","receivedAt":"2015-05-18T23:36:15Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 18, 2015 at 8:06 AM, Paul Tan <pyokagan@gmail.com> wrote:\n\n> +enum rebase_type {\n> +       REBASE_FALSE,\n> +       REBASE_TRUE,\n> +       REBASE_PRESERVE\n> +};\n> +\n> +/**\n> + * Parses the value of --rebase, branch.*.rebase or pull.rebase. If value is a\n> + * false value, returns REBASE_FALSE. If value is a true value, returns\n> + * REBASE_TRUE. If value is \"preserve\", returns REBASE_PRESERVE. Otherwise,\n> + * returns -1 to signify an invalid value.\n> + */\n> +static int parse_config_rebase(const char *value)\n> +{\n> +       int v = git_config_maybe_bool(\"pull.rebase\", value);\n> +       if (v >= 0)\n> +               return v;\n> +       if (!strcmp(value, \"preserve\"))\n> +               return REBASE_PRESERVE;\n> +       return -1;\n\nThere is no REBASE_FALSE nor REBASE_TRUE in this function,\nhow do you make sure it returns the correct value then?\n\nCurrently this is probably in sync with what git_config_maybe_bool\nreturns, but think of a future refactoring/change. Then it will be hard\nto have the connection between what the code in git_config_maybe_bool\ndoes, what the code here does and the comment here.\n\nYou could get rid of the enum all together, as you're using integer as a\nreturn type anyway. (so you lose type safety, a future change may introduce\na \"return 42\" which is not part of the enum)\n\nOr you could convert the code to use the enum more strictly and having it\nas the return type:\n\nenum rebase_type parse_config_rebase(const char *value) {\n    int v = git_config_maybe_bool(\"pull.rebase\", value);\n    switch (v) {\n        case 0:\n            return REBASE_FALSE;\n        case 1:\n            return REBASE_TRUE;\n        default:\n             if (!strcmp(value, \"preserve\"))\n                return  REBASE_PRESERVE;\n             else {\n                die(\"value pull.rebase must be one of {false, true, preserve}\");\n                // or if you're more tolerant:\n                return REBASE_VALUE_UNINTERPRETABLE;\n       }\n   }\n\n\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> +       int *value = (int*) opt->value;\n> +\n> +       if (arg)\n> +               *value = parse_config_rebase(arg);\n> +       else\n> +               *value = unset ? REBASE_FALSE : REBASE_TRUE;\n> +       return *value >= 0 ? 0 : -1;\n\nThis is repeating the check from above. If you'd go the way of using\nstricter enums,\nyou'd just check for the REBASE_VALUE_UNINTERPRETABLE\nvalue here.\n\n> +}\n> +\n>  static const char * const pull_usage[] = {\n>         N_(\"git pull [options] [<repo> [<refspec>...]]\"),\n>         NULL\n> @@ -48,7 +84,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 int opt_rebase;\n>  static char *opt_diffstat;\n>  static char *opt_log;\n>  static char *opt_squash;\n> @@ -82,8 +119,12 @@ static struct option pull_options[] = {\n>           N_(\"force progress reporting\"),\n>           PARSE_OPT_NOARG, parse_opt_passthru},\n>\n> -       /* Options passed to git-merge */\n> +       /* Options passed to git-merge or git-rebase */\n>         OPT_GROUP(N_(\"Options related to merging\")),\n> +       { OPTION_CALLBACK, 'r', \"rebase\", &opt_rebase,\n> +         N_(\"false|true|preserve\"),\n> +         N_(\"incorporate changes by rebasing rather than merging\"),\n> +         PARSE_OPT_OPTARG, parse_opt_rebase, 0},\n>         { OPTION_CALLBACK, 'n', NULL, &opt_diffstat, NULL,\n>           N_(\"do not show a diffstat at the end of the merge\"),\n>           PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_passthru, (intptr_t) \"no-stat\" },\n> @@ -506,11 +547,185 @@ static int run_merge(void)\n>         return ret;\n>  }\n>\n> +/**\n> + * Returns the merge branch for the current branch. Returns NULL if repo is not\n> + * a valid remote, HEAD does not point to a branch, repo is not the branch's\n> + * configured remote or the branch does not have any configured merge branch.\n> + */\n> +static char *get_merge_branch_1(const char *repo)\n> +{\n> +       struct remote *rm;\n> +       struct branch *curr_branch;\n> +\n> +       if (repo && !(rm = remote_get(repo)))\n> +               return NULL;\n> +       if (!(curr_branch = branch_get(\"HEAD\")))\n> +               return NULL;\n> +       if (repo && curr_branch->remote != rm)\n> +               return NULL;\n> +       if (!curr_branch->merge_nr)\n> +               return NULL;\n> +       return xstrdup(curr_branch->merge[0]->dst);\n> +}\n\nThis feels a bit tangled because of the repetition of checking\nfor repo and curr_branch. Also we do not do assignments inside\nof conditions (if, while, for), so maybe start with a\n\n    if (!repo)\n        return NULL;\n\nand then do the assignments and test on the assigned value.\n\n> +\n> +/**\n> + * Given a refspec, returns the merge branch. Returns NULL if the refspec src\n> + * does not refer to a branch.\n> + *\n> + * FIXME: It should return the tracking branch. Currently only works with the\n> + * default mapping.\n> + */\n> +static char *get_merge_branch_2(const char *repo, const char *refspec)\n> +{\n> +       struct refspec *spec;\n> +       const char *remote;\n> +       char *merge_branch;\n> +\n> +       spec = parse_fetch_refspec(1, &refspec);\n> +       remote = spec->src;\n> +       if (!*remote || !strcmp(remote, \"HEAD\"))\n> +               remote = \"HEAD\";\n> +       else if (skip_prefix(remote, \"heads/\", &remote))\n> +               ;\n> +       else if (skip_prefix(remote, \"refs/heads/\", &remote))\n> +               ;\n> +       else if (starts_with(remote, \"refs/\") ||\n> +               starts_with(remote, \"tags/\") ||\n> +               starts_with(remote, \"remotes/\"))\n> +               remote = \"\";\n> +\n> +       if (*remote) {\n> +               if (!strcmp(repo, \".\"))\n> +                       merge_branch = mkpathdup(\"refs/heads/%s\", remote);\n> +               else\n> +                       merge_branch = mkpathdup(\"refs/remotes/%s/%s\", repo, remote);\n> +       } else\n> +               merge_branch = NULL;\n> +\n> +       free_refspec(1, spec);\n> +       return merge_branch;\n> +}\n> +\n> +/**\n> + * Sets fork_point to the point at which the current branch forked from its\n> + * remote merge branch. Returns 0 on success, -1 on failure.\n> + */\n> +static int get_rebase_fork_point(unsigned char fork_point[GIT_SHA1_RAWSZ],\n> +               const char *repo, const char *refspec)\n> +{\n> +       int ret;\n> +       struct branch *curr_branch;\n> +       char *remote_merge_branch;\n> +       struct argv_array args = ARGV_ARRAY_INIT;\n> +       struct child_process cp = CHILD_PROCESS_INIT;\n> +       struct strbuf sb = STRBUF_INIT;\n> +\n> +       if (!(curr_branch = branch_get(\"HEAD\")))\n> +               return -1;\n> +\n> +       if (refspec)\n> +               remote_merge_branch = get_merge_branch_2(repo, refspec);\n> +       else\n> +               remote_merge_branch = get_merge_branch_1(repo);\n> +\n> +       if (!remote_merge_branch)\n> +               return -1;\n> +\n> +       argv_array_pushl(&args, \"merge-base\", \"--fork-point\",\n> +                       remote_merge_branch, curr_branch->name, NULL);\n> +       cp.argv = args.argv;\n> +       cp.no_stdin = 1;\n> +       cp.no_stderr = 1;\n> +       cp.git_cmd = 1;\n> +\n> +       if ((ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ)))\n> +               goto cleanup;\n> +\n> +       if ((ret = get_sha1_hex(sb.buf, fork_point)))\n> +               goto cleanup;\n> +\n> +cleanup:\n> +       free(remote_merge_branch);\n> +       strbuf_release(&sb);\n> +       return 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[GIT_SHA1_HEXSZ],\n> +               unsigned char curr_head[GIT_SHA1_RAWSZ],\n> +               unsigned char merge_head[GIT_SHA1_RAWSZ],\n> +               unsigned char fork_point[GIT_SHA1_RAWSZ])\n> +{\n> +       struct commit_list *revs = NULL, *result;\n> +\n> +       commit_list_insert(lookup_commit_reference(curr_head), &revs);\n> +       commit_list_insert(lookup_commit_reference(merge_head), &revs);\n> +       if (!is_null_sha1(fork_point))\n> +               commit_list_insert(lookup_commit_reference(fork_point), &revs);\n> +\n> +       result = reduce_heads(get_octopus_merge_bases(revs));\n> +       free_commit_list(revs);\n> +       if (!result)\n> +               return 1;\n> +\n> +       hashcpy(merge_base, result->item->object.sha1);\n> +       return 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(unsigned char curr_head[GIT_SHA1_RAWSZ],\n> +               unsigned char merge_head[GIT_SHA1_RAWSZ],\n> +               unsigned char fork_point[GIT_SHA1_RAWSZ])\n> +{\n> +       int ret;\n> +       unsigned char oct_merge_base[GIT_SHA1_RAWSZ];\n> +       struct argv_array args = ARGV_ARRAY_INIT;\n> +\n> +       if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point))\n> +               if (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point))\n> +                       hashclr(fork_point);\n> +\n> +       argv_array_push(&args, \"rebase\");\n> +\n> +       /* Shared options */\n> +       argv_push_verbosity(&args);\n> +\n> +       /* Options passed to git-rebase */\n> +       if (opt_rebase == REBASE_PRESERVE)\n> +               argv_array_push(&args, \"--preserve-merges\");\n> +       if (opt_diffstat)\n> +               argv_array_push(&args, opt_diffstat);\n> +       argv_push_strategies(&args);\n> +       argv_push_strategy_opts(&args);\n> +       if (opt_gpg_sign)\n> +               argv_array_push(&args, opt_gpg_sign);\n> +\n> +       argv_array_push(&args, \"--onto\");\n> +       argv_array_push(&args, sha1_to_hex(merge_head));\n> +\n> +       if (!is_null_sha1(fork_point))\n> +               argv_array_push(&args, sha1_to_hex(fork_point));\n> +       else\n> +               argv_array_push(&args, sha1_to_hex(merge_head));\n> +\n> +       ret = run_command_v_opt(args.argv, RUN_GIT_CMD);\n> +       argv_array_clear(&args);\n> +       return ret;\n> +}\n> +\n>  int cmd_pull(int argc, const char **argv, const char *prefix)\n>  {\n>         const char *repo, **refspecs;\n>         struct sha1_array merge_heads = SHA1_ARRAY_INIT;\n>         unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];\n> +       unsigned char rebase_fork_point[GIT_SHA1_RAWSZ];\n>\n>         if (!getenv(\"GIT_REFLOG_ACTION\"))\n>                 set_reflog_message(argc, argv);\n> @@ -532,6 +747,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n>         if (get_sha1(\"HEAD\", orig_head))\n>                 hashclr(orig_head);\n>\n> +       if (opt_rebase)\n> +               if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))\n> +                       hashclr(rebase_fork_point);\n> +\n>         if (run_fetch(repo, refspecs))\n>                 return 1;\n>\n> @@ -573,6 +792,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n>                 if (merge_heads.nr > 1)\n>                         die(_(\"Cannot merge multiple branches into empty head.\"));\n>                 return pull_into_void(*merge_heads.sha1, curr_head);\n> +       } else if (opt_rebase) {\n> +               if (merge_heads.nr > 1)\n> +                       die(_(\"Cannot rebase onto multiple branches.\"));\n> +               return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);\n>         } else\n>                 return run_merge();\n>  }\n> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> index 9414cc1..3798b96 100755\n> --- a/t/t5520-pull.sh\n> +++ b/t/t5520-pull.sh\n> @@ -212,7 +212,7 @@ test_expect_success 'fast-forward fails with conflicting work tree' '\n>         test \"$(git rev-parse third)\" = \"$(git rev-parse second)\"\n>  '\n>\n> -test_expect_failure '--rebase' '\n> +test_expect_success '--rebase' '\n>         git branch to-rebase &&\n>         echo modified again > file &&\n>         git commit -m file file &&\n> @@ -226,7 +226,7 @@ test_expect_failure '--rebase' '\n>         test new = \"$(git show HEAD:file2)\"\n>  '\n>\n> -test_expect_failure '--rebase fails with multiple branches' '\n> +test_expect_success '--rebase fails with multiple branches' '\n>         git reset --hard before-rebase &&\n>         test_must_fail git pull --rebase . copy master 2>err &&\n>         test \"$(git rev-parse HEAD)\" = \"$(git rev-parse before-rebase)\" &&\n> @@ -310,7 +310,7 @@ test_expect_failure 'pull.rebase=invalid fails' '\n>         ! git pull . copy\n>  '\n>\n> -test_expect_failure '--rebase=false create a new merge commit' '\n> +test_expect_success '--rebase=false create a new merge commit' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase true &&\n>         git pull --rebase=false . copy &&\n> @@ -319,7 +319,7 @@ test_expect_failure '--rebase=false create a new merge commit' '\n>         test file3 = \"$(git show HEAD:file3.t)\"\n>  '\n>\n> -test_expect_failure '--rebase=true rebases and flattens keep-merge' '\n> +test_expect_success '--rebase=true rebases and flattens keep-merge' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase preserve &&\n>         git pull --rebase=true . copy &&\n> @@ -327,7 +327,7 @@ test_expect_failure '--rebase=true rebases and flattens keep-merge' '\n>         test file3 = \"$(git show HEAD:file3.t)\"\n>  '\n>\n> -test_expect_failure '--rebase=preserve rebases and merges keep-merge' '\n> +test_expect_success '--rebase=preserve rebases and merges keep-merge' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase true &&\n>         git pull --rebase=preserve . copy &&\n> @@ -340,7 +340,7 @@ test_expect_success '--rebase=invalid fails' '\n>         ! git pull --rebase=invalid . copy\n>  '\n>\n> -test_expect_failure '--rebase overrides pull.rebase=preserve and flattens keep-merge' '\n> +test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase preserve &&\n>         git pull --rebase . copy &&\n> @@ -348,7 +348,7 @@ test_expect_failure '--rebase overrides pull.rebase=preserve and flattens keep-m\n>         test file3 = \"$(git show HEAD:file3.t)\"\n>  '\n>\n> -test_expect_failure '--rebase with rebased upstream' '\n> +test_expect_success '--rebase with rebased upstream' '\n>\n>         git remote add -f me . &&\n>         git checkout copy &&\n> @@ -366,7 +366,7 @@ test_expect_failure '--rebase with rebased upstream' '\n>\n>  '\n>\n> -test_expect_failure '--rebase with rebased default upstream' '\n> +test_expect_success '--rebase with rebased default upstream' '\n>\n>         git update-ref refs/remotes/me/copy copy-orig &&\n>         git checkout --track -b to-rebase2 me/copy &&\n> @@ -377,7 +377,7 @@ test_expect_failure '--rebase with rebased default upstream' '\n>\n>  '\n>\n> -test_expect_failure 'rebased upstream + fetch + pull --rebase' '\n> +test_expect_success 'rebased upstream + fetch + pull --rebase' '\n>\n>         git update-ref refs/remotes/me/copy copy-orig &&\n>         git reset --hard to-rebase-orig &&\n> @@ -409,7 +409,7 @@ test_expect_failure 'pull --rebase dies early with dirty working directory' '\n>\n>  '\n>\n> -test_expect_failure 'pull --rebase works on branch yet to be born' '\n> +test_expect_success 'pull --rebase works on branch yet to be born' '\n>         git rev-parse master >expect &&\n>         mkdir empty_repo &&\n>         (cd empty_repo &&\n> @@ -456,14 +456,14 @@ test_expect_success 'setup for detecting upstreamed changes' '\n>         )\n>  '\n>\n> -test_expect_failure 'git pull --rebase detects upstreamed changes' '\n> +test_expect_success 'git pull --rebase detects upstreamed changes' '\n>         (cd dst &&\n>          git pull --rebase &&\n>          test -z \"$(git ls-files -u)\"\n>         )\n>  '\n>\n> -test_expect_failure 'setup for avoiding reapplying old patches' '\n> +test_expect_success 'setup for avoiding reapplying old patches' '\n>         (cd dst &&\n>          test_might_fail git rebase --abort &&\n>          git reset --hard origin/master\n> @@ -485,7 +485,7 @@ test_expect_failure 'setup for avoiding reapplying old patches' '\n>         )\n>  '\n>\n> -test_expect_failure 'git pull --rebase does not reapply old patches' '\n> +test_expect_success 'git pull --rebase does not reapply old patches' '\n>         (cd dst &&\n>          test_must_fail git pull --rebase &&\n>          test 1 = $(find .git/rebase-apply -name \"000*\" | wc -l)\n> diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh\n> index 4176e11..56e7377 100755\n> --- a/t/t5521-pull-options.sh\n> +++ b/t/t5521-pull-options.sh\n> @@ -19,7 +19,7 @@ test_expect_success 'git pull -q' '\n>         test_must_be_empty out)\n>  '\n>\n> -test_expect_failure 'git pull -q --rebase' '\n> +test_expect_success 'git pull -q --rebase' '\n>         mkdir clonedqrb &&\n>         (cd clonedqrb && git init &&\n>         git pull -q --rebase \"../parent\" >out 2>err &&\n> @@ -38,7 +38,7 @@ test_expect_success 'git pull' '\n>         test_must_be_empty out)\n>  '\n>\n> -test_expect_failure 'git pull --rebase' '\n> +test_expect_success 'git pull --rebase' '\n>         mkdir clonedrb &&\n>         (cd clonedrb && git init &&\n>         git pull --rebase \"../parent\" >out 2>err &&\n> @@ -54,7 +54,7 @@ test_expect_success 'git pull -v' '\n>         test_must_be_empty out)\n>  '\n>\n> -test_expect_failure 'git pull -v --rebase' '\n> +test_expect_success 'git pull -v --rebase' '\n>         mkdir clonedvrb &&\n>         (cd clonedvrb && git init &&\n>         git pull -v --rebase \"../parent\" >out 2>err &&\n> diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh\n> index 1ffd837..dda3929 100755\n> --- a/t/t7406-submodule-update.sh\n> +++ b/t/t7406-submodule-update.sh\n> @@ -11,8 +11,6 @@ submodule and \"git submodule update --rebase/--merge\" does not detach the HEAD.\n>\n>  . ./test-lib.sh\n>\n> -skip_all='skipping submodule update tests, requires git pull --rebase'\n> -test_done\n>\n>  compare_head()\n>  {\n> --\n> 2.1.4\n>\n"},{"id":"261526","messageId":"CAGZ79kar5AOrK1-n3=PAxNETbh-YWTf1-9aUKk=j6o0Mk=jexg@mail.gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-13-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 12/14] pull: configure --rebase via branch.<name>.rebase or pull.rebase","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2015-05-18T23:58:43Z","receivedAt":"2015-05-18T23:58:43Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 18, 2015 at 8:06 AM, Paul Tan <pyokagan@gmail.com> wrote:\n> Since cd67e4d (Teach 'git pull' about --rebase, 2007-11-28),\n> fetch+rebase could be set by default by defining the config variable\n> branch.<name>.rebase. This setting can be overriden on the command line\n> by --rebase and --no-rebase.\n>\n> Since 6b37dff (pull: introduce a pull.rebase option to enable --rebase,\n> 2011-11-06), git-pull --rebase can also be configured via the\n> pull.rebase configuration option.\n>\n> Re-implement support for these two configuration settings by introducing\n> config_get_rebase() which is called before parse_options() to set the\n> default value of opt_rebase.\n>\n> Signed-off-by: Paul Tan <pyokagan@gmail.com>\n> ---\n>  builtin/pull.c  | 35 +++++++++++++++++++++++++++++++++++\n>  t/t5520-pull.sh | 12 ++++++------\n>  2 files changed, 41 insertions(+), 6 deletions(-)\n>\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index f18a21c..a0958a7 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -294,6 +294,39 @@ static const char *config_get_ff(void)\n>         die(_(\"Invalid value for pull.ff: %s\"), value);\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 int config_get_rebase(void)\n> +{\n> +       struct strbuf sb = STRBUF_INIT;\n> +       struct branch *curr_branch = branch_get(\"HEAD\");\n> +       const char *key, *str;\n> +       int ret = -1, value;\n> +\n> +       if (curr_branch) {\n> +               strbuf_addf(&sb, \"branch.%s.rebase\", curr_branch->name);\n> +               key = sb.buf;\n> +               ret = git_config_get_value(sb.buf, &str);\n> +       }\n> +       if (ret) {\n> +               key = \"pull.rebase\";\n> +               ret = git_config_get_value(key, &str);\n> +       }\n> +       if (ret) {\n> +               strbuf_release(&sb);\n> +               return REBASE_FALSE;\n> +       }\n> +       if ((value = parse_config_rebase(str)) < 0)\n\nAssignment inside an if (....).\nI just looked up the Documentation/CodingGuidelines\nwhich state it as:\n - We try to avoid assignments inside \"if ()\" conditions.\nso my hint from my previous mail (to also apply it to for/while)\nis wrong.\n\nInitially the flow confused me a little, so I tried to understand why,\nand I guess it's because of the return value used both as a return value\nand as conditional.\n\nMaybe you could move the check of the value out of this function\nand here you only focus on getting the order right?\nSomething like (completely untested code, beware):\n\nenum rebase_type config_get_rebase(void)\n{\n    struct strbuf sb = STRBUF_INIT;\n    enum rebase_type value;\n\n    struct branch *curr_branch = branch_get(\"HEAD\");\n\n    if (curr_branch) {\n        strbuf_addf(&sb, \"branch.%s.rebase\", curr_branch->name);\n        if (parse_config_rebase_or_die_on_unparsable(sb.buf, &value))\n            return value;\n    }\n\n    if (parse_config_rebase_or_die_on_unparsable(\"pull.rebase\", &value))\n        return value;\n\n    return REBASE_FALSE;\n}\n\nThis implementation leaks the strbuf though.\n\n\n> +               die(_(\"Invalid value for %s: %s\"), key, str);\n> +       strbuf_release(&sb);\n> +       return value;\n> +}\n> +\n>  struct known_remote {\n>         struct known_remote *next;\n>         struct remote *remote;\n> @@ -730,6 +763,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n>         if (!getenv(\"GIT_REFLOG_ACTION\"))\n>                 set_reflog_message(argc, argv);\n>\n> +       opt_rebase = config_get_rebase();\n> +\n>         argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);\n>         parse_repo_refspecs(argc, argv, &repo, &refspecs);\n>\n> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> index 3798b96..17254ee 100755\n> --- a/t/t5520-pull.sh\n> +++ b/t/t5520-pull.sh\n> @@ -234,7 +234,7 @@ test_expect_success '--rebase fails with multiple branches' '\n>         test modified = \"$(git show HEAD:file)\"\n>  '\n>\n> -test_expect_failure 'pull.rebase' '\n> +test_expect_success 'pull.rebase' '\n>         git reset --hard before-rebase &&\n>         test_config pull.rebase true &&\n>         git pull . copy &&\n> @@ -242,7 +242,7 @@ test_expect_failure 'pull.rebase' '\n>         test new = \"$(git show HEAD:file2)\"\n>  '\n>\n> -test_expect_failure 'branch.to-rebase.rebase' '\n> +test_expect_success 'branch.to-rebase.rebase' '\n>         git reset --hard before-rebase &&\n>         test_config branch.to-rebase.rebase true &&\n>         git pull . copy &&\n> @@ -280,7 +280,7 @@ test_expect_success 'pull.rebase=false create a new merge commit' '\n>         test file3 = \"$(git show HEAD:file3.t)\"\n>  '\n>\n> -test_expect_failure 'pull.rebase=true flattens keep-merge' '\n> +test_expect_success 'pull.rebase=true flattens keep-merge' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase true &&\n>         git pull . copy &&\n> @@ -288,7 +288,7 @@ test_expect_failure 'pull.rebase=true flattens keep-merge' '\n>         test file3 = \"$(git show HEAD:file3.t)\"\n>  '\n>\n> -test_expect_failure 'pull.rebase=1 is treated as true and flattens keep-merge' '\n> +test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase 1 &&\n>         git pull . copy &&\n> @@ -296,7 +296,7 @@ test_expect_failure 'pull.rebase=1 is treated as true and flattens keep-merge' '\n>         test file3 = \"$(git show HEAD:file3.t)\"\n>  '\n>\n> -test_expect_failure 'pull.rebase=preserve rebases and merges keep-merge' '\n> +test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase preserve &&\n>         git pull . copy &&\n> @@ -304,7 +304,7 @@ test_expect_failure 'pull.rebase=preserve rebases and merges keep-merge' '\n>         test \"$(git rev-parse HEAD^2)\" = \"$(git rev-parse keep-merge)\"\n>  '\n>\n> -test_expect_failure 'pull.rebase=invalid fails' '\n> +test_expect_success 'pull.rebase=invalid fails' '\n>         git reset --hard before-preserve-rebase &&\n>         test_config pull.rebase invalid &&\n>         ! git pull . copy\n> --\n> 2.1.4\n>\n"},{"id":"261527","messageId":"CAGZ79kbhA2+AJ1Vtz6khX5uEBCCUBUuXhpBezZApZsC_txhewA@mail.gmail.com","threadId":"39357","inReplyTo":"1431961571-20370-15-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2015-05-19T00:12:14Z","receivedAt":"2015-05-19T00:12:14Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Mon, May 18, 2015 at 8:06 AM, Paul Tan <pyokagan@gmail.com> wrote:\n> Tweak the error messages printed by die_no_merge_candidates() to take\n> into account that we may be \"rebasing against\" rather than \"merging\n> with\".\n>\n> Signed-off-by: Paul Tan <pyokagan@gmail.com>\n> ---\n>  builtin/pull.c | 42 +++++++++++++++++++++++++-----------------\n>  1 file changed, 25 insertions(+), 17 deletions(-)\n>\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index c8d673d..15b65a0 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -474,10 +474,12 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n>         const char *remote = curr_branch ? curr_branch->remote_name : NULL;\n>\n>         if (*refspecs) {\n> -               fprintf(stderr,\n> -                       _(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n> -                       \"Generally this means that you provided a wildcard refspec which had no\\n\"\n> -                       \"matches on the remote end.\\n\"));\n> +               if (opt_rebase)\n> +                       fputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n\nIs there a reason you switch to fputs, instead of fprintf?\n$grep -I -r fputs|wc -l\n123\n$ grep -I -r fprintf|wc -l\n689\n\nfputs seems to be used already, though I never came across these parts\nof the code\nmyself, so I wondered if we had fputs in the code base already.\n\nMy next guess would have been personal preference, though there seem to be\nactual reasons[1] why to use fputs.\n\nMaybe mention the switch in the commit message (As the formatting is\nunnecessary,\nswitch to the lighter fputs function)?\n\n[1] http://stackoverflow.com/questions/5690979/fputs-vs-fprintf\n\n\n> +               else\n> +                       fputs(_(\"There are no candidates for merging among the refs that you just fetched.\"), stderr);\n> +               fputs(_(\"Generally this means that you provided a wildcard refspec which had no\\n\"\n> +                       \"matches on the remote end.\"), stderr);\n>         } else if (repo && curr_branch && (!remote || strcmp(repo, remote))) {\n>                 fprintf(stderr,\n>                         _(\"You asked to pull from the remote '%s', but did not specify\\n\"\n> @@ -485,12 +487,15 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n>                         \"for your current branch, you must specify a branch on the command line.\\n\"),\n>                         repo);\n>         } else if (!curr_branch) {\n> -               fprintf(stderr,\n> -                       _(\"You are not currently on a branch. Please specify which\\n\"\n> -                       \"branch you want to merge with. See git-pull(1) for details.\\n\"\n> -                       \"\\n\"\n> -                       \"    git pull <remote> <branch>\\n\"\n> -                       \"\\n\"));\n> +               fputs(_(\"You are not currently on a branch.\"), stderr);\n> +               if (opt_rebase)\n> +                       fputs(_(\"Please specify which branch you want to rebase against.\"), stderr);\n> +               else\n> +                       fputs(_(\"Please specify which branch you want to merge with.\"), stderr);\n> +               fputs(_(\"See git-pull(1) for details.\"), stderr);\n> +               fputs(\"\", stderr);\n> +               fputs(\"    git pull <remote> <branch>\", stderr);\n> +               fputs(\"\", stderr);\n>         } else if (!curr_branch->merge_nr) {\n>                 struct known_remote *remotes = NULL;\n>                 const char *remote_name = \"<remote>\";\n> @@ -499,14 +504,17 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n>                 if (remotes && !remotes->next)\n>                         remote_name = remotes->remote->name;\n>\n> +               fputs(_(\"There is no tracking information for the current branch.\"), stderr);\n> +               if (opt_rebase)\n> +                       fputs(_(\"Please specify which branch you want to rebase against.\"), stderr);\n> +               else\n> +                       fputs(_(\"Please specify which branch you want to merge with.\"), stderr);\n> +               fputs(_(\"See git-pull(1) for details.\"), stderr);\n> +               fputs(\"\", stderr);\n> +               fputs(\"    git pull <remote> <branch>\", stderr);\n> +               fputs(\"\", stderr);\n>                 fprintf(stderr,\n> -                       _(\"There is no tracking information for the current branch.\\n\"\n> -                       \"Please specify which branch you want to merge with.\\n\"\n> -                       \"See git-pull(1) for details.\\n\"\n> -                       \"\\n\"\n> -                       \"    git pull <remote> <branch>\\n\"\n> -                       \"\\n\"\n> -                       \"If you wish to set tracking information for this branch you can do so with:\\n\"\n> +                       _(\"If you wish to set tracking information for this branch you can do so with:\\n\"\n>                         \"\\n\"\n>                         \"    git branch --set-upstream-to=%s/<branch> %s\\n\"\n>                         \"\\n\"),\n> --\n> 2.1.4\n>\n"},{"id":"261544","messageId":"ea07c7ecec761a0bb2d9f4936a8d2411@www.dscho.org","threadId":"39357","inReplyTo":"1431961571-20370-12-git-send-email-pyokagan@gmail.com","subject":"Re: [PATCH 11/14] pull: teach git pull about --rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-19T13:04:23Z","receivedAt":"2015-05-19T13:04:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nI would like to add more suggestions to Stefan's comments, please find them below.\n\nOn 2015-05-18 17:06, Paul Tan wrote:\n> @@ -506,11 +547,185 @@ static int run_merge(void)\n>  \treturn ret;\n>  }\n>  \n> +/**\n> + * Returns the merge branch for the current branch. Returns NULL if repo is not\n> + * a valid remote, HEAD does not point to a branch, repo is not the branch's\n> + * configured remote or the branch does not have any configured merge branch.\n> + */\n> +static char *get_merge_branch_1(const char *repo)\n\nA better name might be `get_upstream_branch(const char *repo)`, in line with the function in `sha1_name.c` that implements the `@{upstream}` functionality.\n\n> +{\n> +\tstruct remote *rm;\n> +\tstruct branch *curr_branch;\n> +\n> +\tif (repo && !(rm = remote_get(repo)))\n> +\t\treturn NULL;\n> +\tif (!(curr_branch = branch_get(\"HEAD\")))\n> +\t\treturn NULL;\n> +\tif (repo && curr_branch->remote != rm)\n> +\t\treturn NULL;\n> +\tif (!curr_branch->merge_nr)\n> +\t\treturn NULL;\n> +\treturn xstrdup(curr_branch->merge[0]->dst);\n> +}\n> +\n> +/**\n> + * Given a refspec, returns the merge branch. Returns NULL if the refspec src\n> + * does not refer to a branch.\n> + *\n> + * FIXME: It should return the tracking branch. Currently only works with the\n> + * default mapping.\n> + */\n> +static char *get_merge_branch_2(const char *repo, const char *refspec)\n> +{\n> +\tstruct refspec *spec;\n> +\tconst char *remote;\n> +\tchar *merge_branch;\n> +\n> +\tspec = parse_fetch_refspec(1, &refspec);\n> +\tremote = spec->src;\n> +\tif (!*remote || !strcmp(remote, \"HEAD\"))\n> +\t\tremote = \"HEAD\";\n> +\telse if (skip_prefix(remote, \"heads/\", &remote))\n> +\t\t;\n> +\telse if (skip_prefix(remote, \"refs/heads/\", &remote))\n> +\t\t;\n> +\telse if (starts_with(remote, \"refs/\") ||\n> +\t\tstarts_with(remote, \"tags/\") ||\n> +\t\tstarts_with(remote, \"remotes/\"))\n> +\t\tremote = \"\";\n> +\n> +\tif (*remote) {\n> +\t\tif (!strcmp(repo, \".\"))\n> +\t\t\tmerge_branch = mkpathdup(\"refs/heads/%s\", remote);\n> +\t\telse\n> +\t\t\tmerge_branch = mkpathdup(\"refs/remotes/%s/%s\", repo, remote);\n> +\t} else\n> +\t\tmerge_branch = NULL;\n> +\n> +\tfree_refspec(1, spec);\n> +\treturn merge_branch;\n> +}\n\nI have to admit that it took me a substantial amount of time to deduce from the code what `get_merge_branch_2()` really does (judging from the description, I thought that it would be essentially the same as `get_merge_branch_1()` without the hard-coded \"HEAD\"). If the comment above the function would have said something like:\n\n/**\n * Given a refspec, returns the name of the local tracking ref.\n */\n\nI would have had an easier time. Also, I wonder if something like this would do the job:\n\n\tspec = parse_fetch_refspec(1, &refspec);\n\tif (spec->dst)\n\t\treturn spec->dst;\n\tif (!(remote = get_remote(remote_name)))\n\t\treturn NULL;\n\tif (remote_find_tracking(remote, spec))\n\t\treturn spec->dst;\n\treturn NULL;\n\n(I guess we'd have to `xstrdup()` the return values because the return value of `get_merge_branch_1()` needs to be `free()`d, but maybe we can avoid so much `malloc()/free()`ing.) Again, the function name should probably be changed to something clearer, maybe to `get_tracking_branch()`.\n\nOne thing that is not clear at all to me is whether\n\n\tgit pull --rebase origin master next\n\nwould error out as expected, or simply rebase to `origin/master`.\n\n> +/**\n> + * Sets fork_point to the point at which the current branch forked from its\n> + * remote merge branch. Returns 0 on success, -1 on failure.\n> + */\n> +static int get_rebase_fork_point(unsigned char fork_point[GIT_SHA1_RAWSZ],\n> +\t\tconst char *repo, const char *refspec)\n> +{\n> +\tint ret;\n> +\tstruct branch *curr_branch;\n> +\tchar *remote_merge_branch;\n> +\tstruct argv_array args = ARGV_ARRAY_INIT;\n> +\tstruct child_process cp = CHILD_PROCESS_INIT;\n> +\tstruct strbuf sb = STRBUF_INIT;\n> +\n> +\tif (!(curr_branch = branch_get(\"HEAD\")))\n> +\t\treturn -1;\n> +\n> +\tif (refspec)\n> +\t\tremote_merge_branch = get_merge_branch_2(repo, refspec);\n> +\telse\n> +\t\tremote_merge_branch = get_merge_branch_1(repo);\n> +\n> +\tif (!remote_merge_branch)\n> +\t\treturn -1;\n\nWe should probably `return error(_\"No tracking branch found for %s@, refspec ? refspec : \"HEAD\");` so that the user has a chance to understand that there has been a problem and how to solve it.\n\n> +\targv_array_pushl(&args, \"merge-base\", \"--fork-point\",\n> +\t\t\tremote_merge_branch, curr_branch->name, NULL);\n> +\tcp.argv = args.argv;\n\nLet's just use `cp.args` directly...\n\n> +\tcp.no_stdin = 1;\n> +\tcp.no_stderr = 1;\n> +\tcp.git_cmd = 1;\n> +\n> +\tif ((ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ)))\n> +\t\tgoto cleanup;\n> +\n> +\tif ((ret = get_sha1_hex(sb.buf, fork_point)))\n> +\t\tgoto cleanup;\n> +\n> +cleanup:\n> +\tfree(remote_merge_branch);\n> +\tstrbuf_release(&sb);\n> +\treturn ret ? -1 : 0;\n> +}\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(unsigned char curr_head[GIT_SHA1_RAWSZ],\n> +\t\tunsigned char merge_head[GIT_SHA1_RAWSZ],\n> +\t\tunsigned char fork_point[GIT_SHA1_RAWSZ])\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,\n> fork_point))\n\nIt might be my mail program only that mangled the diff here. But it could also be that this line is a little long (by my count, it is 81 columns wide).\n\nOtherwise, this looks really good to me.\n\nCiao,\nDscho\n"},{"id":"261545","messageId":"5fcd2fa185fab4c64939ad09eb10e6d5@www.dscho.org","threadId":"39357","inReplyTo":"CAGZ79kbhA2+AJ1Vtz6khX5uEBCCUBUuXhpBezZApZsC_txhewA@mail.gmail.com","subject":"Re: [PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-19T13:10:51Z","receivedAt":"2015-05-19T13:10:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn 2015-05-19 02:12, Stefan Beller wrote:\n> On Mon, May 18, 2015 at 8:06 AM, Paul Tan <pyokagan@gmail.com> wrote:\n>> Tweak the error messages printed by die_no_merge_candidates() to take\n>> into account that we may be \"rebasing against\" rather than \"merging\n>> with\".\n>>\n>> Signed-off-by: Paul Tan <pyokagan@gmail.com>\n>> ---\n>>  builtin/pull.c | 42 +++++++++++++++++++++++++-----------------\n>>  1 file changed, 25 insertions(+), 17 deletions(-)\n>>\n>> diff --git a/builtin/pull.c b/builtin/pull.c\n>> index c8d673d..15b65a0 100644\n>> --- a/builtin/pull.c\n>> +++ b/builtin/pull.c\n>> @@ -474,10 +474,12 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs\n>>         const char *remote = curr_branch ? curr_branch->remote_name : NULL;\n>>\n>>         if (*refspecs) {\n>> -               fprintf(stderr,\n>> -                       _(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n>> -                       \"Generally this means that you provided a wildcard refspec which had no\\n\"\n>> -                       \"matches on the remote end.\\n\"));\n>> +               if (opt_rebase)\n>> +                       fputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n> \n> Is there a reason you switch to fputs, instead of fprintf?\n> $grep -I -r fputs|wc -l\n> 123\n> $ grep -I -r fprintf|wc -l\n> 689\n> \n> fputs seems to be used already, though I never came across these parts\n> of the code\n> myself, so I wondered if we had fputs in the code base already.\n\nMy guess was that the new-line I expected and did not see in the `fputs()` call was the reason. But according to http://pubs.opengroup.org/onlinepubs/009695399/functions/fputs.html:\n\n    The puts() function appends a <newline> while fputs() does not.\n\nThis comment concludes my review on this round of this patch series. It required quite some time to review, so it must have taken quite a bit more to actually write it: impressive work!\n\nCiao,\nDscho\n"},{"id":"261564","messageId":"xmqqoalg8re2.fsf@gitster.dls.corp.google.com","threadId":"39357","inReplyTo":"5fcd2fa185fab4c64939ad09eb10e6d5@www.dscho.org","subject":"Re: [PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-05-19T16:27:01Z","receivedAt":"2015-05-19T16:27:01Z","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>>> -               fprintf(stderr,\n>>> -                       _(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n>>> -                       \"Generally this means that you provided a wildcard refspec which had no\\n\"\n>>> -                       \"matches on the remote end.\\n\"));\n>>> +               if (opt_rebase)\n>>> +                       fputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n>> \n>     The puts() function appends a <newline> while fputs() does not.\n\nYup, so this update makes the command spew unterminated lines, which\nnot something intended...\n"},{"id":"261767","messageId":"CACRoPnRVcjcegRU8J6a=X6uN=b7fhJyD9=js4LqJ7ORX6gT=jg@mail.gmail.com","threadId":"39357","inReplyTo":"4213f4fa8fb52fb020c2e9b5d78fbf7b@www.dscho.org","subject":"Re: [PATCH 02/14] pull: pass verbosity, --progress flags to fetch and merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-21T09:48:46Z","receivedAt":"2015-05-21T09:48:46Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi,\n\nOn Tue, May 19, 2015 at 1:41 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> On 2015-05-18 17:05, Paul Tan wrote:\n>> diff --git a/builtin/pull.c b/builtin/pull.c\n>> index 0b771b9..a4d9c92 100644\n>> --- a/builtin/pull.c\n>> +++ b/builtin/pull.c\n>> @@ -11,16 +11,64 @@\n>>  #include \"argv-array.h\"\n>>  #include \"run-command.h\"\n>>\n>> +/**\n>> + * Given an option opt, where opt->value points to a char* and opt->defval is a\n>> + * string, sets opt->value to the evaluation of \"--$defval=$arg\". If `arg` is\n>> + * NULL, then opt->value is set to \"--$defval\". If unset is true, then\n>> + * opt->value is set to \"--no-$defval\".\n>> + */\n>> +static int parse_opt_passthru(const struct option *opt, const char\n>> *arg, int unset)\n>\n> How about adding this to parse-options-cb.c in a separate patch? I guess the description could say something like:\n\nYeah, I was also thinking if the callback could be generalized as well.\n\nSpecifically, I wonder if we should re-construct just the\nlast-provided option (the current solution), or all the options (e.g.\nsomething like OPT_STRING_LIST). I'm currently working on the rewrite\nfor git-am.sh as well, and it turns out it makes heavy use of the\nlatter, so we probably should support both.\n\n> /**\n>  * Given an option opt, recreate the command-line option, as strbuf. This is useful\n>  * when a command needs to parse a command-line option in order to pass it to yet\n>  * another command. This callback can be used in conjunction with the\n>  * PARSE_OPT_(OPTARG|NOARG|NONEG) options, but not with PARSE_OPT_LASTARG_DEFAULT\n>  * because it expects `defval` to be the name of the command-line option to\n>  * reconstruct.\n>  *\n>  * The result will be stored in opt->value, which is expected to be a pointer to an\n>  * strbuf.\n>  */\n>\n\nHeh, this docstring reads much better than mine.\n\n> Implied by my suggested description, I also propose to put the re-recreated command-line option into a strbuf instead of a char *, to make memory management easier (read: avoid memory leaks).\n\nUnfortunately, the usage of strbuf means that we lose the ability to\nknow if an option was not provided at all (the value is NULL). This is\nimportant as some of these options are used to override the default\nconfigured behavior.\n\n> You might also want to verify that arg is `NULL` when `unset != 0`. Something like this:\n\nHmm, would there be a situation where arg is NULL when `unset != 0`?\nAt first glance in the source code, it won't unless\nPARSE_OPT_LASTARG_DEFAULT is set, I guess. Anyway, there's no harm in\nbeing careful, though I think it's more likely that an invalid pointer\nis passed in through \"arg\", which we can't detect :-(.\n\n> int parse_opt_passthru(const struct option *opt, const char *arg, int unset)\n> {\n>         struct strbuf *buf = opt->value;\n>\n>         assert(opt->defval);\n>         strbuf_reset(buf);\n>         if (unset) {\n>                 assert(!arg);\n>                 strbuf_addf(buf, \"--no-%s\", opt->defval);\n>         }\n>         else {\n>                 strbuf_addf(buf, \"--%s\", opt->defval);\n>                 if (arg)\n>                         strbuf_addf(buf, \"=%s\", arg);\n>         }\n>\n>         return 0;\n> }\n>\n>>  static struct option pull_options[] = {\n>> +     /* Shared options */\n>> +     OPT__VERBOSITY(&opt_verbosity),\n>> +     { OPTION_CALLBACK, 0, \"progress\", &opt_progress, NULL,\n>> +       N_(\"force progress reporting\"),\n>> +       PARSE_OPT_NOARG, parse_opt_passthru},\n>\n> I *think* there is a '(intptr_t) \"progress\"' missing...\n\nUgh. I think this shows that relying on \"defval\" is too error-prone.\nAnyway, it is only required for the options -n (which maps to\n--no-stat), and --summary (which maps to --stat), so we should\nprobably make it such that if defval is not provided, we fallback to\nusing long_name or short_name.\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>> +     int verbosity;\n>> +\n>> +     for (verbosity = opt_verbosity; verbosity > 0; verbosity--)\n>> +             argv_array_push(arr, \"-v\");\n>> +\n>> +     for (verbosity = opt_verbosity; verbosity < 0; verbosity++)\n>> +             argv_array_push(arr, \"-q\");\n>> +}\n>\n> Hmm... I guess this is *really* nit-picky, but I'd rather use \"i\" instead of \"verbosity\" because the second loop is about quietness instead of verbosity ;-)\n\nWell, my thinking was that a negative verbosity *is* quietness ;-).\n\nThanks,\nPaul\n"},{"id":"261768","messageId":"CACRoPnQoZRc_DPM+kqxXmrepa4tKy7wX6eVjRNNMV8Sf9ue0vw@mail.gmail.com","threadId":"39357","inReplyTo":"86694e742e55b6db71ec30d43c5643d7@www.dscho.org","subject":"Re: [PATCH 06/14] pull: support pull.ff config","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-21T09:53:42Z","receivedAt":"2015-05-21T09:53:42Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi,\n\nOn Tue, May 19, 2015 at 3:02 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> Hi Paul,\n>\n> On 2015-05-18 17:06, Paul Tan wrote:\n>\n>> diff --git a/builtin/pull.c b/builtin/pull.c\n>> index 8982fdf..b305a47 100644\n>> --- a/builtin/pull.c\n>> +++ b/builtin/pull.c\n>> @@ -209,6 +209,28 @@ static void argv_push_force(struct argv_array *arr)\n>>               argv_array_push(arr, \"-f\");\n>>  }\n>>\n>> +/**\n>> + * If pull.ff is \"true\", returns \"--ff\". If pull.ff is \"false\", returns\n>> + * \"--no-ff\". If pull.ff is \"only\", returns \"--ff-only\". Otherwise, returns\n>> + * NULL.\n>> + */\n>> +static const char *config_get_ff(void)\n>> +{\n>> +     const char *value;\n>> +\n>> +     if (git_config_get_value(\"pull.ff\", &value))\n>> +             return NULL;\n>> +     switch (git_config_maybe_bool(\"pull.ff\", value)) {\n>> +             case 0:\n>> +                     return \"--no-ff\";\n>> +             case 1:\n>> +                     return \"--ff\";\n>> +     }\n>> +     if (!strcmp(\"pull.ff\", \"only\"))\n>\n> I think you want to test `!strcmp(\"only\", value)` ;-)\n\nUgh >.<\n\nFixed, thanks for catching.\n\nRegards,\nPaul\n"},{"id":"261771","messageId":"CACRoPnRJH9QiouDHQasa515X6ECGZpRNfhnTGrJi8SyKt0ZWBQ@mail.gmail.com","threadId":"39357","inReplyTo":"xmqqmw11a6yk.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 10/14] pull: set reflog message","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-21T10:08:13Z","receivedAt":"2015-05-21T10:08:13Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Tue, May 19, 2015 at 5:53 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Yeah, either that, or \"insert separator only before adding to\n> something else\" pattern, i.e.\n>\n>         for (i = 0; i < argc; i++) {\n>                 if (i)\n>                         addch(&msg, ' ');\n>                 addstr(&msg, argv[i]);\n>         }\n>\n> Both are easier to read than \"always append SP and trim at the end\".\n> Besides, trimming at the end makes readers wonder if there are cases\n> where argv[argc-1] ends with whitespaces and the code on purpose\n> removes them.\n\nYeah, this looks better and is more correct.\n\nThanks,\nPaul\n"},{"id":"261793","messageId":"f90387a786c1e0f4287c9fee405f8e2f@www.dscho.org","threadId":"39357","inReplyTo":"CACRoPnRVcjcegRU8J6a=X6uN=b7fhJyD9=js4LqJ7ORX6gT=jg@mail.gmail.com","subject":"Re: [PATCH 02/14] pull: pass verbosity, --progress flags to fetch and merge","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-21T15:59:09Z","receivedAt":"2015-05-21T15:59:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-21 11:48, Paul Tan wrote:\n> \n> On Tue, May 19, 2015 at 1:41 AM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n>> On 2015-05-18 17:05, Paul Tan wrote:\n>>> diff --git a/builtin/pull.c b/builtin/pull.c\n>>> index 0b771b9..a4d9c92 100644\n>>> --- a/builtin/pull.c\n>>> +++ b/builtin/pull.c\n>>> @@ -11,16 +11,64 @@\n>>>  #include \"argv-array.h\"\n>>>  #include \"run-command.h\"\n>>>\n>>> +/**\n>>> + * Given an option opt, where opt->value points to a char* and opt->defval is a\n>>> + * string, sets opt->value to the evaluation of \"--$defval=$arg\". If `arg` is\n>>> + * NULL, then opt->value is set to \"--$defval\". If unset is true, then\n>>> + * opt->value is set to \"--no-$defval\".\n>>> + */\n>>> +static int parse_opt_passthru(const struct option *opt, const char\n>>> *arg, int unset)\n>>\n>> Implied by my suggested description, I also propose to put the re-recreated command-line option into a strbuf instead of a char *, to make memory management easier (read: avoid memory leaks).\n> \n> Unfortunately, the usage of strbuf means that we lose the ability to\n> know if an option was not provided at all (the value is NULL). This is\n> important as some of these options are used to override the default\n> configured behavior.\n\nWould this not be implied by the strbuf's len being 0?\n\n>> You might also want to verify that arg is `NULL` when `unset != 0`. Something like this:\n> \n> Hmm, would there be a situation where arg is NULL when `unset != 0`?\n\nI forgot to say that my suggestion was purely meant as defensive coding. Yes, `arg` *should* be `NULL` when `unset != 0`. Should ;-)\n\nBy the way, just to make sure: My comments and suggestions are no demands; you should feel very free to reject them when you feel that your code is better without the suggested changes. I am just trying to be helpful.\n\nCiao,\nDscho\n"},{"id":"261905","messageId":"CACRoPnRR60DK7tE7yenSX0wouGSQDMDpcphjCdCr-GGdkfPrYQ@mail.gmail.com","threadId":"39357","inReplyTo":"f90387a786c1e0f4287c9fee405f8e2f@www.dscho.org","subject":"Re: [PATCH 02/14] pull: pass verbosity, --progress flags to fetch and merge","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-22T13:38:17Z","receivedAt":"2015-05-22T13:38:17Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi Johannes,\n\nOn Thu, May 21, 2015 at 11:59 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> Hi Paul,\n>\n> On 2015-05-21 11:48, Paul Tan wrote:\n>> Unfortunately, the usage of strbuf means that we lose the ability to\n>> know if an option was not provided at all (the value is NULL). This is\n>> important as some of these options are used to override the default\n>> configured behavior.\n>\n> Would this not be implied by the strbuf's len being 0?\n\nYou're right >.< I think I need more coffee as well ;-)\n\nIt would mean adding STRBUF_INIT's to all of the option variables, but\nI don't think it is a problem. strbufs it is, then.\n\n>>> You might also want to verify that arg is `NULL` when `unset != 0`. Something like this:\n>>\n>> Hmm, would there be a situation where arg is NULL when `unset != 0`?\n>\n> I forgot to say that my suggestion was purely meant as defensive coding. Yes, `arg` *should* be `NULL` when `unset != 0`. Should ;-)\n>\n> By the way, just to make sure: My comments and suggestions are no demands; you should feel very free to reject them when you feel that your code is better without the suggested changes. I am just trying to be helpful.\n\nThanks, your comments have been really helpful. I really do appreciate it :).\n\nRegards,\nPaul\n"},{"id":"261907","messageId":"CACRoPnR=ajnzvX42jwSpaYE1fgd2J-Ph7+_s=ch=OQJf5FRpQQ@mail.gmail.com","threadId":"39357","inReplyTo":"xmqqoalg8re2.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-22T13:48:52Z","receivedAt":"2015-05-22T13:48:52Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Wed, May 20, 2015 at 12:27 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>\n>>>> -               fprintf(stderr,\n>>>> -                       _(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n>>>> -                       \"Generally this means that you provided a wildcard refspec which had no\\n\"\n>>>> -                       \"matches on the remote end.\\n\"));\n>>>> +               if (opt_rebase)\n>>>> +                       fputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n>>>\n>>     The puts() function appends a <newline> while fputs() does not.\n>\n> Yup, so this update makes the command spew unterminated lines, which\n> not something intended...\n\nUgh >< Will put the \"\\n\" back.\n\nAnd yes, I used fputs() because it seems wasteful to use fprintf()\nwhich will scan the string looking for any '%' formatting units, when\nwe know there aren't.\n\nI will also update 05/14 to use fputs() as well where appropriate.\n\nThanks,\nPaul\n"},{"id":"261908","messageId":"632335d05ab160ea935c920249f62970@www.dscho.org","threadId":"39357","inReplyTo":"CACRoPnR=ajnzvX42jwSpaYE1fgd2J-Ph7+_s=ch=OQJf5FRpQQ@mail.gmail.com","subject":"Re: [PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-05-22T14:14:26Z","receivedAt":"2015-05-22T14:14:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Paul,\n\nOn 2015-05-22 15:48, Paul Tan wrote:\n> On Wed, May 20, 2015 at 12:27 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>>\n>>>>> -               fprintf(stderr,\n>>>>> -                       _(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n>>>>> -                       \"Generally this means that you provided a wildcard refspec which had no\\n\"\n>>>>> -                       \"matches on the remote end.\\n\"));\n>>>>> +               if (opt_rebase)\n>>>>> +                       fputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n>>>>\n>>>     The puts() function appends a <newline> while fputs() does not.\n>>\n>> Yup, so this update makes the command spew unterminated lines, which\n>> not something intended...\n> \n> Ugh >< Will put the \"\\n\" back.\n> \n> And yes, I used fputs() because it seems wasteful to use fprintf()\n> which will scan the string looking for any '%' formatting units, when\n> we know there aren't.\n> \n> I will also update 05/14 to use fputs() as well where appropriate.\n\nI believe the common thinking is that consistency beats speed in error messages, so it would be easier to read and maintain the code if all of those error messages were just using `fprintf(stderr, ...);`. It's not as if we spit out hundreds of thousands of error messages per second where that `%s` parsing would hurt ;-)\n\nCiao,\nDscho\n"},{"id":"261928","messageId":"CAGZ79kad1aunDXkg8K3HQrOkG31-FJ51k04O=S1mk_JLbpsjKw@mail.gmail.com","threadId":"39357","inReplyTo":"632335d05ab160ea935c920249f62970@www.dscho.org","subject":"Re: [PATCH 14/14] pull --rebase: error on no merge candidate cases","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2015-05-22T17:12:57Z","receivedAt":"2015-05-22T17:12:57Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Fri, May 22, 2015 at 7:14 AM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> Hi Paul,\n>\n> On 2015-05-22 15:48, Paul Tan wrote:\n>> On Wed, May 20, 2015 at 12:27 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>>>\n>>>>>> -               fprintf(stderr,\n>>>>>> -                       _(\"There are no candidates for merging among the refs that you just fetched.\\n\"\n>>>>>> -                       \"Generally this means that you provided a wildcard refspec which had no\\n\"\n>>>>>> -                       \"matches on the remote end.\\n\"));\n>>>>>> +               if (opt_rebase)\n>>>>>> +                       fputs(_(\"There is no candidate for rebasing against among the refs that you just fetched.\"), stderr);\n>>>>>\n>>>>     The puts() function appends a <newline> while fputs() does not.\n>>>\n>>> Yup, so this update makes the command spew unterminated lines, which\n>>> not something intended...\n>>\n>> Ugh >< Will put the \"\\n\" back.\n>>\n>> And yes, I used fputs() because it seems wasteful to use fprintf()\n>> which will scan the string looking for any '%' formatting units, when\n>> we know there aren't.\n>>\n>> I will also update 05/14 to use fputs() as well where appropriate.\n>\n> I believe the common thinking is that consistency beats speed in error messages, so it would be easier to read and maintain the code if all of those error messages were just using `fprintf(stderr, ...);`. It's not as if we spit out hundreds of thousands of error messages per second where that `%s` parsing would hurt ;-)\n>\n> Ciao,\n> Dscho\n\nAs soon as we spit out one error message,\nthe speed game is over anyway. (IO is slow,\nand I believe in the error case correctness\nis the most critical thing to get right, so no\nneed to pay attention to performance).\nThough I don't mind having a fputs/fprintf\nmixture. I just happened to work on parts\nof code without any fputs before, that's why\nI brought up this discussion.\n\nActually I think I'd prefer fputs when there is no %.\n"},{"id":"262462","messageId":"CACRoPnThv97e+f3ymuRCFB_u7_dbcOUcCtxUh6BmwDcUZMPjmA@mail.gmail.com","threadId":"39357","inReplyTo":"xmqqh9r9bsji.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 00/14] Make git-pull a builtin","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-30T07:29:58Z","receivedAt":"2015-05-30T07:29:58Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi,\n\nOn Tue, May 19, 2015 at 3:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Paul Tan <pyokagan@gmail.com> writes:\n>\n>> This series rewrites git-pull.sh into a C builtin, thus improving its\n>> performance and portability. It is part of my GSoC project to rewrite git-pull\n>> and git-am into builtins[2].\n>\n> Earlier you were worried about 'git pull' being used in many tests\n> for the purpose of testing other parts of the system and not testing\n> 'pull' itself.  For a program as complex as 'git pull', you may want\n> to take the 'competing implementations' approach.\n>\n> (1) write an empty cmd_pull() that relaunches \"git pull\" scripted\n>     porcelain from the $GIT_EXEC_PATH with given parameters, and\n>     wire all the necessary bits to git.c.\n>\n> (2) enhance cmd_pull() a bit by bit, but keep something like this\n>\n>         if (getenv(GIT_USE_BUILTIN_PULL)) {\n>                 /* original run_command(\"git-pull.sh\") code */\n>                 exit $?\n>         }\n>\n>         ... your \"C\" version ...\n>\n> (3) add \"GIT_USE_BUILTIN_PULL=Yes; export GIT_USE_BUILTIN_PULL\" at\n>     the beginning of \"t55??\" test scripts (but not others that rely\n>     on working pull and that are not interested in catching bugs in\n>     pull).\n>\n> (4) once cmd_pull() becomes fully operational, drop (3) and also the\n>     conditional one you added in (2), and retire the environment\n>     variable.  Retire the git-pull.sh script to contrib/examples/\n>     boneyard.\n>\n> That way, you will always have a reference you can use throughout\n> the development.\n>\n> Just a suggestion, not a requirement.\n\nOkay, I'm trying this out in the next re-roll. I do agree that this\npatch series should not touch anything in t/ at all.\n\nOne problem(?) is that putting builtins/pull.o in the BUILTIN_OBJS and\nleaving git-pull.sh in SCRIPT_SH in the Makefile will generate 2\ntargets to ./git-pull (they will clobber each other). For GNU Make,\nthe last defined target will win, so in this case it just happens that\ngit-pull.sh will win because the build targets for the shell scripts\nare defined after the build targets for the builtins, so this works in\nour favor I guess.\n\nRegards,\nPaul\n"},{"id":"262463","messageId":"CACRoPnSCT58AYAvXuELX0eemdobPsHMkkJd3oLg8Sv_DxOf1CQ@mail.gmail.com","threadId":"39357","inReplyTo":"CACRoPnThv97e+f3ymuRCFB_u7_dbcOUcCtxUh6BmwDcUZMPjmA@mail.gmail.com","subject":"Re: [PATCH 00/14] Make git-pull a builtin","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-30T08:00:42Z","receivedAt":"2015-05-30T08:00:42Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Sat, May 30, 2015 at 3:29 PM, Paul Tan <pyokagan@gmail.com> wrote:\n> Hi,\n> Okay, I'm trying this out in the next re-roll. I do agree that this\n> patch series should not touch anything in t/ at all.\n>\n> One problem(?) is that putting builtins/pull.o in the BUILTIN_OBJS and\n> leaving git-pull.sh in SCRIPT_SH in the Makefile will generate 2\n> targets to ./git-pull (they will clobber each other). For GNU Make,\n> the last defined target will win, so in this case it just happens that\n> git-pull.sh will win because the build targets for the shell scripts\n> are defined after the build targets for the builtins, so this works in\n> our favor I guess.\n>\n> Regards,\n> Paul\n\nJust to add on, I just discovered that test-lib.sh unsets all GIT_*\nenvironment variables \"for repeatability\", so the name\n\"GIT_USE_BUILTIN_PULL\" cannot be used. I'm tempted to just add a\nunderscore just before the name (\"_GIT_USE_BUILTIN_PULL\") to work\naround this, since it's just a temporary thing.\n\nThanks,\nPaul\n"},{"id":"262502","messageId":"CACRoPnRvjyjtbdT4CY7f4kFqq1rTiksp7eUnpFza+h8ZAq-4gg@mail.gmail.com","threadId":"39357","inReplyTo":"ea07c7ecec761a0bb2d9f4936a8d2411@www.dscho.org","subject":"Re: [PATCH 11/14] pull: teach git pull about --rebase","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-05-31T08:18:56Z","receivedAt":"2015-05-31T08:18:56Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"Hi Johannes,\n\nOn Tue, May 19, 2015 at 9:04 PM, Johannes Schindelin\n<johannes.schindelin@gmx.de> wrote:\n> On 2015-05-18 17:06, Paul Tan wrote:\n>> +/**\n>> + * Given a refspec, returns the merge branch. Returns NULL if the refspec src\n>> + * does not refer to a branch.\n>> + *\n>> + * FIXME: It should return the tracking branch. Currently only works with the\n>> + * default mapping.\n>> + */\n>> +static char *get_merge_branch_2(const char *repo, const char *refspec)\n>> +{\n>> +     struct refspec *spec;\n>> +     const char *remote;\n>> +     char *merge_branch;\n>> +\n>> +     spec = parse_fetch_refspec(1, &refspec);\n>> +     remote = spec->src;\n>> +     if (!*remote || !strcmp(remote, \"HEAD\"))\n>> +             remote = \"HEAD\";\n>> +     else if (skip_prefix(remote, \"heads/\", &remote))\n>> +             ;\n>> +     else if (skip_prefix(remote, \"refs/heads/\", &remote))\n>> +             ;\n>> +     else if (starts_with(remote, \"refs/\") ||\n>> +             starts_with(remote, \"tags/\") ||\n>> +             starts_with(remote, \"remotes/\"))\n>> +             remote = \"\";\n>> +\n>> +     if (*remote) {\n>> +             if (!strcmp(repo, \".\"))\n>> +                     merge_branch = mkpathdup(\"refs/heads/%s\", remote);\n>> +             else\n>> +                     merge_branch = mkpathdup(\"refs/remotes/%s/%s\", repo, remote);\n>> +     } else\n>> +             merge_branch = NULL;\n>> +\n>> +     free_refspec(1, spec);\n>> +     return merge_branch;\n>> +}\n>\n> I have to admit that it took me a substantial amount of time to deduce from the code what `get_merge_branch_2()` really does (judging from the description, I thought that it would be essentially the same as `get_merge_branch_1()` without the hard-coded \"HEAD\"). If the comment above the function would have said something like:\n>\n> /**\n>  * Given a refspec, returns the name of the local tracking ref.\n>  */\n>\n> I would have had an easier time. Also, I wonder if something like this would do the job:\n\nYeah whoops, this came from a confusion over the difference over\n\"merge branch\" and \"remote tracking branch\". A merge branch would be a\nremote tracking branch, but a remote tracking branch is not\nnecessarily a merge branch.\n\n>         spec = parse_fetch_refspec(1, &refspec);\n>         if (spec->dst)\n>                 return spec->dst;\n\nHmm, I notice that get_remote_merge_branch() only looks at the src\npart of the refspec. However, I guess it is true that if the dst part\nis provided, the user may be wishing for that to be interpreted as the\n\"remote tracking branch\", so we should be looking at it to calculate\nthe fork point.\n\n>         if (!(remote = get_remote(remote_name)))\n>                 return NULL;\n>         if (remote_find_tracking(remote, spec))\n>                 return spec->dst;\n\n... and if the dst part of the refspec is not provided, we fall back\nto see if there is any remote tracking branch in the repo for the src\npart of the ref, which matches the intention of\nget_remote_merge_branch() I think, while being better because\nremote_find_tracking() takes into account the actual configured fetch\nrefspecs for the remote.\n\nHowever, we also need to consider if the user provided a wildcard\nrefspec, as it will not make sense in this case. From my reading,\nremote_find_tracking(), which calls query_refspecs(), would just match\nthe src part literally, so I guess we should explicitly detect and\nerror out in this case.\n\n>         return NULL;\n>\n> (I guess we'd have to `xstrdup()` the return values because the return value of `get_merge_branch_1()` needs to be `free()`d, but maybe we can avoid so much `malloc()/free()`ing.) Again, the function name should probably be changed to something clearer, maybe to `get_tracking_branch()`.\n\nYeah, it should be changed to get_tracking_branch(), since it is\ndifferent from get_merge_branch().\n\n> One thing that is not clear at all to me is whether\n>\n>         git pull --rebase origin master next\n>\n> would error out as expected, or simply rebase to `origin/master`.\n\nIn git-pull.sh, for the rebase fork point calculation it just used the\nfirst refspec. However, when running git-rebase it checked to see if\nthere was only one merge base, which is replicated here:\n\n    @@ -573,6 +792,10 @@ int cmd_pull(int argc, const char **argv,\nconst char *prefix)\n                    if (merge_heads.nr > 1)\n                            die(_(\"Cannot merge multiple branches into\nempty head.\"));\n                    return pull_into_void(*merge_heads.sha1, curr_head);\n    +       } else if (opt_rebase) {\n    +               if (merge_heads.nr > 1)\n    +                       die(_(\"Cannot rebase onto multiple branches.\"));\n    +               return run_rebase(curr_head, *merge_heads.sha1,\nrebase_fork_point);\n            } else\n                    return run_merge();\n     }\n\nSince this is just a 1:1 rewrite I just tried to keep as close to the\noriginal as possible. However, thinking about it, since we *are* just\nusing the first refspec for fork point calculation, I do agree that we\nshould probably give an warning() here as well if the user provided\nmore than one refspec, like \"Cannot calculate rebase fork point as you\nprovided more than one refspec. git-pull will not be able to handle a\nrebased upstream\". I do not think it is fatal enough that we should\nerror() or die(), as e.g. the first refspec may be a wildcard refspec\nthat matches nothing, and the second refspec that matches one merge\nhead for rebasing. This is pretty contrived though, but still\ntechnically legitimate. I dunno.\n\n>> +/**\n>> + * Sets fork_point to the point at which the current branch forked from its\n>> + * remote merge branch. Returns 0 on success, -1 on failure.\n>> + */\n>> +static int get_rebase_fork_point(unsigned char fork_point[GIT_SHA1_RAWSZ],\n>> +             const char *repo, const char *refspec)\n>> +{\n>> +     int ret;\n>> +     struct branch *curr_branch;\n>> +     char *remote_merge_branch;\n>> +     struct argv_array args = ARGV_ARRAY_INIT;\n>> +     struct child_process cp = CHILD_PROCESS_INIT;\n>> +     struct strbuf sb = STRBUF_INIT;\n>> +\n>> +     if (!(curr_branch = branch_get(\"HEAD\")))\n>> +             return -1;\n>> +\n>> +     if (refspec)\n>> +             remote_merge_branch = get_merge_branch_2(repo, refspec);\n>> +     else\n>> +             remote_merge_branch = get_merge_branch_1(repo);\n>> +\n>> +     if (!remote_merge_branch)\n>> +             return -1;\n>\n> We should probably `return error(_\"No tracking branch found for %s@, refspec ? refspec : \"HEAD\");` so that the user has a chance to understand that there has been a problem and how to solve it.\n\nJust like the above, I don't think this is serious enough to be\nconsidered an error() though. Sure, this means that we cannot properly\nhandle the case where the upstream has been rebased, but this is not\nalways the case. We could probably have a warning() here, but I think\nthe message should be improved to tell the user what exactly she is\nlosing out on. e.g. \"No tracking branch found for %s. git-pull will\nnot be able to handle a rebased upstream.\"\n\n>> +     argv_array_pushl(&args, \"merge-base\", \"--fork-point\",\n>> +                     remote_merge_branch, curr_branch->name, NULL);\n>> +     cp.argv = args.argv;\n>\n> Let's just use `cp.args` directly...\n\nYeah, whoops.\n\n>> +     cp.no_stdin = 1;\n>> +     cp.no_stderr = 1;\n>> +     cp.git_cmd = 1;\n>> +\n>> +     if ((ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ)))\n>> +             goto cleanup;\n>> +\n>> +     if ((ret = get_sha1_hex(sb.buf, fork_point)))\n>> +             goto cleanup;\n>> +\n>> +cleanup:\n>> +     free(remote_merge_branch);\n>> +     strbuf_release(&sb);\n>> +     return ret ? -1 : 0;\n>> +}\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(unsigned char curr_head[GIT_SHA1_RAWSZ],\n>> +             unsigned char merge_head[GIT_SHA1_RAWSZ],\n>> +             unsigned char fork_point[GIT_SHA1_RAWSZ])\n>> +{\n>> +     int ret;\n>> +     unsigned char oct_merge_base[GIT_SHA1_RAWSZ];\n>> +     struct argv_array args = ARGV_ARRAY_INIT;\n>> +\n>> +     if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head,\n>> fork_point))\n>\n> It might be my mail program only that mangled the diff here. But it could also be that this line is a little long (by my count, it is 81 columns wide).\n\nYeah, it is, but I felt that breaking the line would reduce readability.\n\nThanks, these were some really hard questions that you raised ;-).\n\nRegards,\nPaul\n"},{"id":"262705","messageId":"CACRoPnQiqkm0SS0TQ-ovbp+k1oyZ-d+SnSqE-+yForHJAgkn2w@mail.gmail.com","threadId":"39357","inReplyTo":"CACRoPnRvjyjtbdT4CY7f4kFqq1rTiksp7eUnpFza+h8ZAq-4gg@mail.gmail.com","subject":"Re: [PATCH 11/14] pull: teach git pull about --rebase","fromName":"Paul Tan","fromEmail":"pyokagan@gmail.com","sentAt":"2015-06-02T11:26:06Z","receivedAt":"2015-06-02T11:26:06Z","isPatch":true,"sender":{"key":"pyokagan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/109479?v=4"},"body":"On Sun, May 31, 2015 at 4:18 PM, Paul Tan <pyokagan@gmail.com> wrote:\n> On Tue, May 19, 2015 at 9:04 PM, Johannes Schindelin\n> <johannes.schindelin@gmx.de> wrote:\n>> Also, I wonder if something like this would do the job:\n>>         spec = parse_fetch_refspec(1, &refspec);\n>>         if (spec->dst)\n>>                 return spec->dst;\n>\n> Hmm, I notice that get_remote_merge_branch() only looks at the src\n> part of the refspec. However, I guess it is true that if the dst part\n> is provided, the user may be wishing for that to be interpreted as the\n> \"remote tracking branch\", so we should be looking at it to calculate\n> the fork point.\n>\n>>         if (!(remote = get_remote(remote_name)))\n>>                 return NULL;\n>>         if (remote_find_tracking(remote, spec))\n>>                 return spec->dst;\n>\n> ... and if the dst part of the refspec is not provided, we fall back\n> to see if there is any remote tracking branch in the repo for the src\n> part of the ref, which matches the intention of\n> get_remote_merge_branch() I think, while being better because\n> remote_find_tracking() takes into account the actual configured fetch\n> refspecs for the remote.\n>\n> However, we also need to consider if the user provided a wildcard\n> refspec, as it will not make sense in this case. From my reading,\n> remote_find_tracking(), which calls query_refspecs(), would just match\n> the src part literally, so I guess we should explicitly detect and\n> error out in this case.\n\nWith all that said, after thinking about it I feel that this patch\nseries should focus solely on rewriting git-pull.sh 1:1. While I do\nagree with the above suggested improvements, I think they should be\nimplemented as separated patch(es) on top of this series since we\nwould be technically changing git-pull's behavior, even if we are\nimproving it.\n\nAs such, the issue that I think should be focused on for this patch\nis: does get_merge_branch_1() and get_merge_branch_2() in this patch\nimplement the same behavior as get_remote_merge_branch() in\ngit-parse.remote.sh? If it does, then its purpose is fulfilled.\n\nSo, I'll keep the overall logic of get_merge_branch_2() the same for\nthe next re-roll. (Other than renaming the function and fixing code\nstyle issues). Once this series is okay, I'll look into doing a\nseparate patch on top that changes the function to use\nremote_find_tracking() so that we fix the assumption that the default\nfetch mapping is used.\n\nThe other possibility is that we fix this in git-parse-remote.sh, but\nI'm personally getting a bit tired from having to re-implement the\nsame thing in shell script and C. Furthermore, the only script using\nget_remote_merge_branch() is git-pull.sh.\n\n> [...]\n> Since this is just a 1:1 rewrite I just tried to keep as close to the\n> original as possible. However, thinking about it, since we *are* just\n> using the first refspec for fork point calculation, I do agree that we\n> should probably give an warning() here as well if the user provided\n> more than one refspec, like \"Cannot calculate rebase fork point as you\n> provided more than one refspec. git-pull will not be able to handle a\n> rebased upstream\". I do not think it is fatal enough that we should\n> error() or die(), as e.g. the first refspec may be a wildcard refspec\n> that matches nothing, and the second refspec that matches one merge\n> head for rebasing. This is pretty contrived though, but still\n> technically legitimate. I dunno.\n>[...]\n>> We should probably `return error(_\"No tracking branch found for %s@, refspec ? refspec : \"HEAD\");` so that the user has a chance to understand that there has been a problem and how to solve it.\n>\n> Just like the above, I don't think this is serious enough to be\n> considered an error() though. Sure, this means that we cannot properly\n> handle the case where the upstream has been rebased, but this is not\n> always the case. We could probably have a warning() here, but I think\n> the message should be improved to tell the user what exactly she is\n> losing out on. e.g. \"No tracking branch found for %s. git-pull will\n> not be able to handle a rebased upstream.\"\n\nLikewise, I won't introduce the error()s or warning()s for now.\n\nOther than that, all other code style related issues have been/will be\nfixed. Thanks for the reviews.\n\nRegards,\nPaul\n"}]}