{"thread":{"id":"40767","subject":"[PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream","startedAt":"2015-11-11T02:11:20Z","lastAt":"2015-12-01T00:24:07Z","messageCount":13,"participants":["Michael Rappazzo","Michael J Gruber","Mike Rappazzo","Johannes Schindelin","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"273172","messageId":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","threadId":"40767","inReplyTo":null,"subject":"[PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T02:11:20Z","receivedAt":"2015-11-11T02:11:20Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"This patch series is built on (based on) 'next' because it relies on\nworktree.c\n\n`ff-refs` will update local branches which can be fast-forwarded to their\nupstream tracking branch.  Any branch which has diverged from the upstream\nwill be left untouched by this command.  Additionally, there are options\nfor '--dry-run' and to '--skip-worktrees'.\n\nThere are two primary update mechanisms for fast-forwarding a branch.\n  - For a checked out branch, emulate `git-merge --ff-only`\n  - For a non-checked out branch, emulate `git update-ref`\n\nWhen run on a repo with multiple worktrees (created with git-worktree add),\ngit-ff-refs will take that into account when fast-forwarding.  That is, it\nwill run in 'merge --ff-only' emulation mode when a branch is checked out\nin a worktree, rather than in 'update-ref' mode.\n\nThe primary benefit of ff-refs will come for those who maintain several\nlocal branches which track upstream remote branches that update often.  The\nintended usage pattern is to run `git-fetch` followed by `git-ff-refs`.\n\nMichael Rappazzo (5):\n  ff-refs: builtin cmd to check and fast forward local refs to their\n    upstream\n  ff-refs: update each updatable ref\n  ff-refs: add --dry-run and --skip-worktree options\n  ff-refs: Add documentation\n  ff-refs: Add tests\n\n .gitignore                    |   1 +\n Documentation/git-ff-refs.txt |  55 +++++++++\n Makefile                      |   1 +\n builtin.h                     |   1 +\n builtin/ff-refs.c             | 272 ++++++++++++++++++++++++++++++++++++++++++\n command-list.txt              |   1 +\n git.c                         |   1 +\n t/t7900-ff-refs.sh            | 164 +++++++++++++++++++++++++\n 8 files changed, 496 insertions(+)\n create mode 100644 Documentation/git-ff-refs.txt\n create mode 100644 builtin/ff-refs.c\n create mode 100755 t/t7900-ff-refs.sh\n\n-- \n2.6.2\n"},{"id":"273170","messageId":"1447207885-10911-2-git-send-email-rappazzo@gmail.com","threadId":"40767","inReplyTo":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH 1/5] ff-refs: builtin cmd to check and fast forward local refs to their upstream","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T02:11:21Z","receivedAt":"2015-11-11T02:11:21Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"Each local branch with an upstream remote is checked to see if it can be\nfast-forwarded to its upstream.  If fast-forward applies to the branch,\nthen this is reported to the user.\n\nThe statuses are\n\tUP-TO-DATE - The local branch is the same or equal to the upstream\n\tWOULD-UPDATE - The branch would be fast forwarded\n\tREMOTE-MISSING - The branch is tracking an upstream that is not present\n\tNON-FAST-FORWARD - The branch has diverged from the upstream\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n .gitignore        |   1 +\n Makefile          |   1 +\n builtin.h         |   1 +\n builtin/ff-refs.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n command-list.txt  |   1 +\n git.c             |   1 +\n 6 files changed, 226 insertions(+)\n create mode 100644 builtin/ff-refs.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 1c2f832..e86a490 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -53,6 +53,7 @@\n /git-difftool--helper\n /git-describe\n /git-fast-export\n+/git-ff-refs\n /git-fast-import\n /git-fetch\n /git-fetch-pack\ndiff --git a/Makefile b/Makefile\nindex 43ceeb9..8e312ad 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -853,6 +853,7 @@ BUILTIN_OBJS += builtin/diff.o\n BUILTIN_OBJS += builtin/fast-export.o\n BUILTIN_OBJS += builtin/fetch-pack.o\n BUILTIN_OBJS += builtin/fetch.o\n+BUILTIN_OBJS += builtin/ff-refs.o\n BUILTIN_OBJS += builtin/fmt-merge-msg.o\n BUILTIN_OBJS += builtin/for-each-ref.o\n BUILTIN_OBJS += builtin/fsck.o\ndiff --git a/builtin.h b/builtin.h\nindex 6b95006..5680e33 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -63,6 +63,7 @@ extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_fast_export(int argc, const char **argv, const char *prefix);\n extern int cmd_fetch(int argc, const char **argv, const char *prefix);\n extern int cmd_fetch_pack(int argc, const char **argv, const char *prefix);\n+extern int cmd_ff_refs(int argc, const char **argv, const char *prefix);\n extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);\n extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_format_patch(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/ff-refs.c b/builtin/ff-refs.c\nnew file mode 100644\nindex 0000000..94a4649\n--- /dev/null\n+++ b/builtin/ff-refs.c\n@@ -0,0 +1,221 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"builtin.h\"\n+#include \"remote.h\"\n+#include \"run-command.h\"\n+#include \"worktree.h\"\n+\n+struct worktree **worktrees;\n+const char *padding = \".....................................................\";\n+\n+static const char * const builtin_ff_refs_usage[] = {\n+\tN_(\"git ff-refs [<options>]\"),\n+\tNULL\n+};\n+\n+enum ff_result_type {\n+\tUP_TO_DATE,\n+\tUPDATABLE,\n+\tREMOTE_MISSING,\n+\tNON_FAST_FORWARD,\n+\tUNABLE_TO_UPDATE\n+};\n+\n+struct ff_ref_details {\n+\tstruct branch *branch;\n+\tconst char *upstream;\n+\tconst char *shortened_upstream;\n+\tint names_length;\n+\tenum ff_result_type result_type;\n+\n+\tstruct commit *branch_commit;\n+\tstruct commit *upstream_commit;\n+\tstruct commit *merge_base;\n+\tstruct worktree *wt;\n+};\n+\n+struct ff_ref_data {\n+\tint max_names_length;\n+\n+\tint detail_counter;\n+\tint detail_alloc;\n+\tstruct ff_ref_details **detail_list;\n+};\n+\n+static const char *result_type_str(enum ff_result_type result_type)\n+{\n+\tswitch (result_type) {\n+\tcase UP_TO_DATE:\n+\t\treturn _(\"UP-TO-DATE\");\n+\tcase UPDATABLE:\n+\t\treturn _(\"WOULD-UPDATE\");\n+\tcase REMOTE_MISSING:\n+\t\treturn _(\"REMOTE-MISSING\");\n+\tcase NON_FAST_FORWARD:\n+\t\treturn _(\"NON-FAST-FORWARD\");\n+\tdefault:\n+\t\treturn _(\"UNABLE-TO-UPDATE\");\n+\t}\n+}\n+\n+/**\n+ * return the worktree with the given refname checked out, or NULL if that\n+ * ref is not checked out in any branch.\n+ *\n+ * This implementation assumes a small number of worktrees (since it loops\n+ * through each worktree for every ref).  If a repository has a large number\n+ * of worktrees, then it might be beneficial to implement this as a hashmap\n+ * lookup instead.\n+ */\n+static struct worktree *find_worktree(const char *refname)\n+{\n+\tint i = 0;\n+\n+\tfor (i = 0; worktrees[i]; i++) {\n+\t\tif (!worktrees[i]->is_detached && !strcmp(worktrees[i]->head_ref, refname)) {\n+\t\t\treturn worktrees[i];\n+\t\t}\n+\t}\n+\treturn NULL;\n+}\n+\n+/**\n+ * After all of the relevant refs have been collected, process the\n+ * interesting ones\n+ */\n+static void process_refs(struct ff_ref_data *data)\n+{\n+\tint i = 0;\n+\n+\tfor (i = 0; data->detail_list[i]; i++) {\n+\t\tstruct ff_ref_details *details;\n+\t\tint padLen;\n+\n+\t\tdetails = data->detail_list[i];\n+\t\tpadLen = 3 + data->max_names_length - details->names_length;\n+\t\tif (padLen < 0)\n+\t\t\tpadLen = 0;\n+\n+\t\tprintf(\"     %s -> %s%*.*s\",\n+\t\t\tdetails->branch->name, details->shortened_upstream, padLen, padLen, padding);\n+\t\tprintf(\"[%s]\\n\", result_type_str(details->result_type));\n+\t}\n+}\n+\n+static void add_to_detail_list(struct ff_ref_data *data,\n+\t\tstruct ff_ref_details *details)\n+{\n+\tif (!data->detail_alloc) {\n+\t\tdata->detail_list = xmalloc(sizeof(struct ff_ref_details *));\n+\t\tdata->detail_alloc = 1;\n+\t} else\n+\t\tALLOC_GROW(data->detail_list, data->detail_counter + 1, data->detail_alloc);\n+\n+\tif (details && details->names_length > data->max_names_length)\n+\t\tdata->max_names_length = details->names_length;\n+\n+\tdata->detail_list[data->detail_counter++] = details;\n+}\n+\n+/**\n+ * Look for refs which have an upstream configured.  Each ref with an upstream\n+ * is added to a list to later possibly make changes on.  All of the necessary\n+ * read-only data is gleaned here.\n+ */\n+static int analize_refs(const char *refname,\n+\t\t\tconst struct object_id *oid, int flags, void *cb_data) {\n+\n+\tstruct branch *branch;\n+\tconst char *upstream;\n+\tstruct ff_ref_data *data = cb_data;\n+\n+\tbranch = branch_get(shorten_unambiguous_ref(refname, 0));\n+\tupstream = branch_get_upstream(branch, NULL);\n+\tif (upstream) {\n+\t\tstruct ff_ref_details *details = xmalloc(sizeof(struct ff_ref_details));\n+\t\tunsigned char upstream_hash[GIT_SHA1_RAWSZ];\n+\n+\t\tdetails->branch = branch;\n+\t\tdetails->upstream = upstream;\n+\n+\t\tdetails->shortened_upstream = shorten_unambiguous_ref(upstream, 0);\n+\t\tdetails->branch_commit = NULL;\n+\t\tdetails->upstream_commit = NULL;\n+\t\tdetails->merge_base = NULL;\n+\t\tdetails->result_type = UNABLE_TO_UPDATE;\n+\t\tdetails->names_length = strlen(branch->name) +\n+\t\t\t\tstrlen(details->shortened_upstream);\n+\t\tdetails->wt = find_worktree(details->branch->refname);\n+\n+\t\tif (!resolve_ref_unsafe(details->upstream, RESOLVE_REF_READING,\n+\t\t\t\tupstream_hash, NULL))\n+\t\t\tdetails->result_type = REMOTE_MISSING;\n+\n+\t\telse if (!hashcmp(oid->hash, upstream_hash))\n+\t\t\tdetails->result_type = UP_TO_DATE;\n+\t\telse {\n+\t\t\tstruct commit_list *bases;\n+\n+\t\t\tdetails->branch_commit = lookup_commit_reference(oid->hash);\n+\t\t\tdetails->upstream_commit = lookup_commit_reference(upstream_hash);\n+\t\t\tbases = get_merge_bases(details->branch_commit,\n+\t\t\t\t\tdetails->upstream_commit);\n+\t\t\tdetails->merge_base = bases->item;\n+\n+\t\t\tif (!hashcmp(upstream_hash, details->merge_base->object.sha1))\n+\t\t\t\tdetails->result_type = UP_TO_DATE;\n+\n+\t\t\telse if (!in_merge_bases(details->branch_commit, details->upstream_commit))\n+\t\t\t\tdetails->result_type = NON_FAST_FORWARD;\n+\n+\t\t\telse\n+\t\t\t\tdetails->result_type = UPDATABLE;\n+\t\t}\n+\t\tadd_to_detail_list(data, details);\n+\t}\n+\treturn 0;\n+}\n+\n+/**\n+ * Free the memory allocated for all of the data\n+ */\n+static void free_data(struct ff_ref_data *data)\n+{\n+\tint i = 0;\n+\n+\tfor (i = 0; data->detail_list[i]; i++)\n+\t\tfree(data->detail_list[i]);\n+\tfree(data);\n+}\n+\n+int cmd_ff_refs(int argc, const char **argv, const char *prefix)\n+{\n+\tint ret = 0;\n+\n+\tstruct option options[] = {\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, builtin_ff_refs_usage, 0);\n+\tif (argc)\n+\t\tusage_with_options(builtin_ff_refs_usage, options);\n+\telse {\n+\t\tstruct ff_ref_data *data = NULL;\n+\n+\t\tworktrees = get_worktrees();\n+\t\tdata = xmalloc(sizeof(struct ff_ref_data));\n+\t\tdata->detail_alloc = 0;\n+\t\tdata->detail_counter = 0;\n+\t\tdata->max_names_length = 0;\n+\n+\t\tret = for_each_ref(&analize_refs, data);\n+\t\tadd_to_detail_list(data, NULL);\n+\n+\t\t//for each detail\n+\t\tprocess_refs(data);\n+\n+\t\tfree_worktrees(worktrees);\n+\t\tfree_data(data);\n+\t}\n+\treturn ret;\n+}\ndiff --git a/command-list.txt b/command-list.txt\nindex 2a94137..b766ea8 100644\n--- a/command-list.txt\n+++ b/command-list.txt\n@@ -54,6 +54,7 @@ git-fast-export                         ancillarymanipulators\n git-fast-import                         ancillarymanipulators\n git-fetch                               mainporcelain           remote\n git-fetch-pack                          synchingrepositories\n+git-ff-refs                             mainporcelain           history\n git-filter-branch                       ancillarymanipulators\n git-fmt-merge-msg                       purehelpers\n git-for-each-ref                        plumbinginterrogators\ndiff --git a/git.c b/git.c\nindex 6ed824c..1c75156 100644\n--- a/git.c\n+++ b/git.c\n@@ -404,6 +404,7 @@ static struct cmd_struct commands[] = {\n \t{ \"fast-export\", cmd_fast_export, RUN_SETUP },\n \t{ \"fetch\", cmd_fetch, RUN_SETUP },\n \t{ \"fetch-pack\", cmd_fetch_pack, RUN_SETUP },\n+\t{ \"ff-refs\", cmd_ff_refs, RUN_SETUP },\n \t{ \"fmt-merge-msg\", cmd_fmt_merge_msg, RUN_SETUP },\n \t{ \"for-each-ref\", cmd_for_each_ref, RUN_SETUP },\n \t{ \"format-patch\", cmd_format_patch, RUN_SETUP },\n-- \n2.6.2\n"},{"id":"273171","messageId":"1447207885-10911-3-git-send-email-rappazzo@gmail.com","threadId":"40767","inReplyTo":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH 2/5] ff-refs: update each updatable ref","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T02:11:22Z","receivedAt":"2015-11-11T02:11:22Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"If an updatable ref is checked out in any worktree, emulate merge\n--ff-only to also update the local work tree (including executing the\npost-merge hook).\n\nIf an updatable ref is not checked out in any worktree, the ref is\nsimply updated.\n\nIf a ref update is successful, that ref is reported as 'UPDATED'.  If\nit is not successful, the ref is reported as 'UNABLE-TO-UPDATE'.\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n builtin/ff-refs.c | 40 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 40 insertions(+)\n\ndiff --git a/builtin/ff-refs.c b/builtin/ff-refs.c\nindex 94a4649..f14afc5 100644\n--- a/builtin/ff-refs.c\n+++ b/builtin/ff-refs.c\n@@ -59,6 +59,43 @@ static const char *result_type_str(enum ff_result_type result_type)\n }\n \n /**\n+ * Do the ref update.\n+ *  - If the ref is checked out in any worktree, emulate merge --ff-only to\n+ *    also update the local work tree (including executing the post-merge hook).\n+ *\n+ *  - If the ref is not checked out in any worktree, update it\n+ *\n+ *  - If any of the ref updates fails, the result_type is set to UNABLE-TO-UPDATE\n+ */\n+static void do_ref_update(struct ff_ref_data *data, struct ff_ref_details *details)\n+{\n+\tconst char *refname = details->branch->refname;\n+\n+\tif (details->wt) {\n+\t\tstruct strbuf path = STRBUF_INIT;\n+\n+\t\tstrbuf_getcwd(&path);\n+\t\tchdir(details->wt->path);\n+\t\tset_git_dir(details->wt->git_dir);\n+\t\tread_index(&the_index);\n+\n+\t\tif (checkout_fast_forward(details->branch_commit->object.sha1,\n+\t\t\t\tdetails->upstream_commit->object.sha1, 1))\n+\t\t\tdetails->result_type = NON_FAST_FORWARD;\n+\t\telse if (update_ref(\"ff-refs\", refname, details->upstream_commit->object.sha1,\n+\t\t\t\tdetails->branch_commit->object.sha1, 0, UPDATE_REFS_QUIET_ON_ERR)) {\n+\t\t\tdetails->result_type = UNABLE_TO_UPDATE;\n+\t\t\trun_hook_le(NULL, \"post-merge\", \"0\", NULL);\n+\t\t}\n+\t\tdiscard_index(&the_index);\n+\t\tchdir(path.buf);\n+\t\tstrbuf_release(&path);\n+\t} else if (update_ref(\"ff-refs\", refname, details->upstream_commit->object.sha1,\n+\t\t\tdetails->branch_commit->object.sha1, 0, UPDATE_REFS_QUIET_ON_ERR))\n+\t\tdetails->result_type = UNABLE_TO_UPDATE;\n+}\n+\n+/**\n  * return the worktree with the given refname checked out, or NULL if that\n  * ref is not checked out in any branch.\n  *\n@@ -98,6 +135,9 @@ static void process_refs(struct ff_ref_data *data)\n \n \t\tprintf(\"     %s -> %s%*.*s\",\n \t\t\tdetails->branch->name, details->shortened_upstream, padLen, padLen, padding);\n+\t\tif (details->result_type == UPDATABLE)\n+\t\t\tdo_ref_update(data, details);\n+\n \t\tprintf(\"[%s]\\n\", result_type_str(details->result_type));\n \t}\n }\n-- \n2.6.2\n"},{"id":"273173","messageId":"1447207885-10911-4-git-send-email-rappazzo@gmail.com","threadId":"40767","inReplyTo":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH 3/5] ff-refs: add --dry-run and --skip-worktree options","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T02:11:23Z","receivedAt":"2015-11-11T02:11:23Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"'--dry-run' reports the updatable state of each ref without acutally\nupdating any refs.  Refs which are fast-forwardable are reported as\n'WOULD-UPDATE'.\n\n'--skip-worktrees' does not update refs which are checked out in any\nworktree.  Branches which are fast-forwardable but checked out in a\nworktree are reported as 'SKIPPED'.  With `--dry-run` these are reported\n'WOULD-SKIP'.\n\nSigned-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n builtin/ff-refs.c | 15 +++++++++++++--\n 1 file changed, 13 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/ff-refs.c b/builtin/ff-refs.c\nindex f14afc5..ae68cfb 100644\n--- a/builtin/ff-refs.c\n+++ b/builtin/ff-refs.c\n@@ -5,6 +5,8 @@\n #include \"run-command.h\"\n #include \"worktree.h\"\n \n+int dry_run = 0;\n+int no_wt = 0;\n struct worktree **worktrees;\n const char *padding = \".....................................................\";\n \n@@ -18,6 +20,7 @@ enum ff_result_type {\n \tUPDATABLE,\n \tREMOTE_MISSING,\n \tNON_FAST_FORWARD,\n+\tSKIPPED,\n \tUNABLE_TO_UPDATE\n };\n \n@@ -48,11 +51,13 @@ static const char *result_type_str(enum ff_result_type result_type)\n \tcase UP_TO_DATE:\n \t\treturn _(\"UP-TO-DATE\");\n \tcase UPDATABLE:\n-\t\treturn _(\"WOULD-UPDATE\");\n+\t\treturn dry_run ? _(\"WOULD-UPDATE\") : _(\"UPDATED\");\n \tcase REMOTE_MISSING:\n \t\treturn _(\"REMOTE-MISSING\");\n \tcase NON_FAST_FORWARD:\n \t\treturn _(\"NON-FAST-FORWARD\");\n+\tcase SKIPPED:\n+\t\treturn dry_run ? _(\"WOULD-SKIP\") : _(\"SKIPPED\");\n \tdefault:\n \t\treturn _(\"UNABLE-TO-UPDATE\");\n \t}\n@@ -135,7 +140,7 @@ static void process_refs(struct ff_ref_data *data)\n \n \t\tprintf(\"     %s -> %s%*.*s\",\n \t\t\tdetails->branch->name, details->shortened_upstream, padLen, padLen, padding);\n-\t\tif (details->result_type == UPDATABLE)\n+\t\tif (!dry_run && details->result_type == UPDATABLE)\n \t\t\tdo_ref_update(data, details);\n \n \t\tprintf(\"[%s]\\n\", result_type_str(details->result_type));\n@@ -208,6 +213,9 @@ static int analize_refs(const char *refname,\n \t\t\telse if (!in_merge_bases(details->branch_commit, details->upstream_commit))\n \t\t\t\tdetails->result_type = NON_FAST_FORWARD;\n \n+\t\t\telse if (no_wt && details->wt)\n+\t\t\t\tdetails->result_type = SKIPPED;\n+\n \t\t\telse\n \t\t\t\tdetails->result_type = UPDATABLE;\n \t\t}\n@@ -233,6 +241,9 @@ int cmd_ff_refs(int argc, const char **argv, const char *prefix)\n \tint ret = 0;\n \n \tstruct option options[] = {\n+\t\tOPT_BOOL(0, \"dry-run\", &dry_run, N_(\"show what would be fast-forwarded\")),\n+\t\tOPT_BOOL(0, \"skip-worktrees\", &no_wt,\n+\t\t\t\tN_(\"skip refs checked out in any worktree\")),\n \t\tOPT_END()\n \t};\n \n-- \n2.6.2\n"},{"id":"273174","messageId":"1447207885-10911-5-git-send-email-rappazzo@gmail.com","threadId":"40767","inReplyTo":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH 4/5] ff-refs: Add documentation","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T02:11:24Z","receivedAt":"2015-11-11T02:11:24Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n Documentation/git-ff-refs.txt | 55 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 55 insertions(+)\n create mode 100644 Documentation/git-ff-refs.txt\n\ndiff --git a/Documentation/git-ff-refs.txt b/Documentation/git-ff-refs.txt\nnew file mode 100644\nindex 0000000..569d1d4\n--- /dev/null\n+++ b/Documentation/git-ff-refs.txt\n@@ -0,0 +1,55 @@\n+git-ff-refs(1)\n+============\n+\n+NAME\n+----\n+git-ff-refs - Fast-forward each local branch to its upstream (if possible)\n+\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git ff-refs' [<options>]\n+\n+\n+DESCRIPTION\n+-----------\n+Update local branches to their tracked upstream if and only if the local branch\n+can be fast-forwarded to the upstream ref.  Local branches which have diverged\n+from the upstream will not be updated.\n+\n+Each local branch tracking an upstream will be reported with a status based on\n+whether or not it it was updated.  The statuses are:\n+\n+\tUP-TO-DATE - The local branch is the same or equal to the upstream\n+\tUPDATED - The branch was fast forwarded\n+\tWOULD-UPDATE - With `--dry-run`, the branch is fast forwardable\n+\tREMOTE-MISSING - The branch is tracking an upstream that is not present\n+\tNON-FAST-FORWARD - The branch has diverged from the upstream\n+\tSKIPPED - With `--skip-worktrees` the branch is fast forwardable but checked\n+\t\tout in a local worktree\n+\tWOULD-SKIP - With `--skip-worktrees` and `--dry-run`, the branch is fast\n+\t\tforwardable but checked out in a local worktree\n+\tUNABLE-TO-UPDATE - An error occurred while trying to update the branch\n+\n+OPTIONS\n+-------\n+--dry-run::\n+\tReport the updatable state of each branch without acutally updating anything.\n+\tIn a dry run, fast-forwardable branches are marked 'WOULD-UPDATE' instead of\n+\t'UPDATED'.\n+\n+--skip-worktrees::\n+\tDoes not update branches which are checked out in any worktree.  Branches\n+\twhich are fast-forwardable that are checked out in any worktree are marked\n+\t'SKIPPED'.  If `--dry-run` is also set, those branches are marked 'WOULD-SKIP'.\n+\n+\n+SEE ALSO\n+--------\n+linkgit:git-update-ref[1]\n+linkgit:git-merge[1]\n+\n+GIT\n+---\n+Part of the linkgit:git[1] suite\n-- \n2.6.2\n"},{"id":"273175","messageId":"1447207885-10911-6-git-send-email-rappazzo@gmail.com","threadId":"40767","inReplyTo":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","subject":"[PATCH 5/5] ff-refs: Add tests","fromName":"Michael Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T02:11:25Z","receivedAt":"2015-11-11T02:11:25Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>\n---\n t/t7900-ff-refs.sh | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 164 insertions(+)\n create mode 100755 t/t7900-ff-refs.sh\n\ndiff --git a/t/t7900-ff-refs.sh b/t/t7900-ff-refs.sh\nnew file mode 100755\nindex 0000000..3cfbcb8\n--- /dev/null\n+++ b/t/t7900-ff-refs.sh\n@@ -0,0 +1,164 @@\n+#!/bin/sh\n+\n+# SKIPPED\n+\n+test_description='test ff-refs'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit init &&\n+\tfor i in $(test_seq 1 9)\n+\tdo\n+\t\techo \"data\" >file_$i &&\n+\t\tgit add file_$i &&\n+\t\tgit commit -m\"Commit $i\" &&\n+\t\tgit branch br_$i\n+\tdone\n+'\n+\n+test_expect_success 'UP-TO-DATE for equal branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit ff-refs >actual &&\n+\t\tgrep \"UP-TO-DATE\" actual\n+\t)\n+'\n+\n+test_expect_success 'UP-TO-DATE for ahead local branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit ff-refs >actual &&\n+\t\techo \"data\" >file_new &&\n+\t\tgit add file_new &&\n+\t\tgit commit -m\"Commit new\" &&\n+\t\tgrep \"UP-TO-DATE\" actual\n+\t)\n+'\n+\n+test_expect_success 'REMOTE-MISSING by local config change' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit config --replace branch.master.merge refs/heads/nothing &&\n+\t\tgit ff-refs >actual &&\n+\t\tgrep \"REMOTE-MISSING\" actual\n+\t)\n+'\n+\n+test_expect_success 'NON-FAST-FORWARD for diverged branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\techo \"data\" >file_new &&\n+\t\tgit add file_new &&\n+\t\tgit commit -m\"Commit new\" &&\n+\t\tgit ff-refs >actual &&\n+\t\tgrep \"NON-FAST-FORWARD\" actual\n+\t)\n+'\n+\n+test_expect_success 'UPDATED for fast-forwardable branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit ff-refs >actual &&\n+\t\tgrep \"UPDATED\" actual\n+\t)\n+'\n+\n+test_expect_success 'WOULD-UPDATE for dry-run on fast-forwardable branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit ff-refs --dry-run >actual &&\n+\t\tgrep \"WOULD-UPDATE\" actual\n+\t)\n+'\n+\n+test_expect_success 'SKIPPED for skip-worktrees on fast-forwardable branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit ff-refs --skip-worktrees >actual &&\n+\t\tgrep \"SKIPPED\" actual\n+\t)\n+'\n+\n+test_expect_success 'WOULD-SKIP for dry-run, skip-worktrees on fast-forwardable branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit ff-refs --dry-run --skip-worktrees >actual &&\n+\t\tgrep \"WOULD-SKIP\" actual\n+\t)\n+'\n+\n+test_expect_success 'UPDATE for fast-forwardable, not checked-out branch' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit checkout -b other origin/br_3 &&\n+\t\tgit ff-refs >actual &&\n+\t\tgrep \"master\" actual | grep \"UPDATED\"\n+\t)\n+'\n+\n+test_expect_success 'UPDATE for fast-forwardable, not checked-out branch using --skip-worktrees' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit checkout -b other origin/br_3 &&\n+\t\tgit ff-refs --skip-worktrees >actual &&\n+\t\tgrep \"master\" actual | grep \"UPDATED\"\n+\t)\n+'\n+\n+test_expect_success 'UPDATE multiple' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit checkout -b other origin/br_5 &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit ff-refs >actual &&\n+\t\tgrep \"master\" actual | grep \"UPDATED\" &&\n+\t\tgrep \"other\" actual | grep \"UPDATED\"\n+\t)\n+'\n+\n+test_expect_success 'UPDATE one, skip worktree on another' '\n+\ttest_when_finished \"rm -rf local\" &&\n+\tgit clone . local &&\n+\t(\n+\t\tcd local &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit checkout -b other origin/br_5 &&\n+\t\tgit reset --hard origin/br_3 &&\n+\t\tgit ff-refs --skip-worktrees >actual &&\n+\t\tgrep \"master\" actual | grep \"UPDATED\" &&\n+\t\tgrep \"other\" actual | grep \"SKIPPED\"\n+\t)\n+'\n+\n+test_done\n-- \n2.6.2\n"},{"id":"273185","messageId":"56431B69.9010007@drmicha.warpmail.net","threadId":"40767","inReplyTo":"1447207885-10911-1-git-send-email-rappazzo@gmail.com","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2015-11-11T10:41:45Z","receivedAt":"2015-11-11T10:41:45Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Michael Rappazzo venit, vidit, dixit 11.11.2015 03:11:\n> This patch series is built on (based on) 'next' because it relies on\n> worktree.c\n> \n> `ff-refs` will update local branches which can be fast-forwarded to their\n> upstream tracking branch.  Any branch which has diverged from the upstream\n> will be left untouched by this command.  Additionally, there are options\n> for '--dry-run' and to '--skip-worktrees'.\n> \n> There are two primary update mechanisms for fast-forwarding a branch.\n>   - For a checked out branch, emulate `git-merge --ff-only`\n>   - For a non-checked out branch, emulate `git update-ref`\n> \n> When run on a repo with multiple worktrees (created with git-worktree add),\n> git-ff-refs will take that into account when fast-forwarding.  That is, it\n> will run in 'merge --ff-only' emulation mode when a branch is checked out\n> in a worktree, rather than in 'update-ref' mode.\n> \n> The primary benefit of ff-refs will come for those who maintain several\n> local branches which track upstream remote branches that update often.  The\n> intended usage pattern is to run `git-fetch` followed by `git-ff-refs`.\n\nI'm sorry, but I don't see why this deserves a new command. If refspec\nwith and without \"+\" are not enough then maybe \"git fetch --all\" or \"git\nremote update\" should learn a new \"--ff-only\" option (ignoring all \"+\")\nlike merge has.\n\nAs for updating worktrees: This shouldn't be taken too lightly anyways.\nBut the worktree interface still has some rough edges, and I would hope\nthat it learns a \"foreach\" subcommand very much like the submodule\nversion. That would allow you to\n\ngit worktree foreach git merge --ff-only\n\nwith a systematic aproach that opens many other opportunities.\n\nMichael\n"},{"id":"273188","messageId":"CANoM8SV77Jg8qYsn7UZ=a18WvrA_ayAWCnAjN9Tf6Re=r1Ggsg@mail.gmail.com","threadId":"40767","inReplyTo":"56431B69.9010007@drmicha.warpmail.net","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Mike Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-11T12:32:37Z","receivedAt":"2015-11-11T12:32:37Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"On Wed, Nov 11, 2015 at 5:41 AM, Michael J Gruber\n<git@drmicha.warpmail.net> wrote:\n> Michael Rappazzo venit, vidit, dixit 11.11.2015 03:11:\n>> This patch series is built on (based on) 'next' because it relies on\n>> worktree.c\n>>\n>> `ff-refs` will update local branches which can be fast-forwarded to their\n>> upstream tracking branch.  Any branch which has diverged from the upstream\n>> will be left untouched by this command.  Additionally, there are options\n>> for '--dry-run' and to '--skip-worktrees'.\n>>\n>> There are two primary update mechanisms for fast-forwarding a branch.\n>>   - For a checked out branch, emulate `git-merge --ff-only`\n>>   - For a non-checked out branch, emulate `git update-ref`\n>>\n>> When run on a repo with multiple worktrees (created with git-worktree add),\n>> git-ff-refs will take that into account when fast-forwarding.  That is, it\n>> will run in 'merge --ff-only' emulation mode when a branch is checked out\n>> in a worktree, rather than in 'update-ref' mode.\n>>\n>> The primary benefit of ff-refs will come for those who maintain several\n>> local branches which track upstream remote branches that update often.  The\n>> intended usage pattern is to run `git-fetch` followed by `git-ff-refs`.\n>\n> I'm sorry, but I don't see why this deserves a new command. If refspec\n> with and without \"+\" are not enough then maybe \"git fetch --all\" or \"git\n> remote update\" should learn a new \"--ff-only\" option (ignoring all \"+\")\n> like merge has.\n\nMaybe I wasn't clear in my description, or maybe I misunderstand\nsomething.  This command is about updating local refs (branches,\nreally), not the local copy of a remote ref.  If, for example I have\nlocal branches:\n\n    master -> origin/master\n    next -> origin/next\n    pu -> origin/pu\n    feature1 -> features/feature1\n    feature2 -> features/feature2\n    feature3 -> features/feature3\n    bug1 -> features/bugs/bug1\n    bug2 -> features/bugs/bug2\n\nIf I don't use multiple worktrees, I probably only have one of those\nchecked out at any one time.  If any of the upstream branches are\nupdated, then when I fetch those branches will be behind.  If I wanted\nto make sure that the branches I am not touching are updated, I would\nhave to do it individually (AFAIK).  And why not update my local\nworktree if it is a fast-forward?.  This command aims to put that\nlocal branch update into a single command.\n\n    > git fetch --all\n    fetching origin...\n        abc1234..abc1235  next -> origin/next\n        abd1234..abd1235  pu -> origin/pu\n\n    fetching features...\n        123abcd..123abce  feature1 -> features/feature1\n      + 124abcd...124abce feature2 -> features/feature2\n        125abcd..125abce  feature3 -> features/feature3\n    > git ff-refs\n        master -> origin/master.........[UP-TO-DATE]\n        next -> origin/next.............[UPDATED]\n        pu -> origin/pu.................[UPDATED]\n        feature1 -> features/feature1...[UPDATED]\n        feature2 -> features/feature2...[NON-FAST-FORWARD]\n        feature3 -> features/feature3...[UPDATED]\n        bug1 -> features/bugs/bug1......[UP-TO-DATE]\n        bug2 -> features/bugs/bug2......[UP-TO-DATE]\n\nFor reference, I have been using a scripted version of this command\n[1].  Assuming that I change your mind on this command, I will add\nthis example to the help doc.\n\n>\n> As for updating worktrees: This shouldn't be taken too lightly anyways.\n> But the worktree interface still has some rough edges, and I would hope\n> that it learns a \"foreach\" subcommand very much like the submodule\n> version. That would allow you to\n>\n> git worktree foreach git merge --ff-only\n>\n> with a systematic aproach that opens many other opportunities.\n>\n> Michael\n\nI am aware of the current status of the worktrees command (I worked on\nthe 'list' command).  If a user only wants to update unchecked out\nbranches, there is a command line option provided, '--skip-worktrees'.\n\nThe foreach command sounds like a good idea, but I don't know that it\nwould help here, as ff-refs is looping through all of the refs already\n(ala for-each-ref).  If you are proposing foreach-worktree as an\nalternative, that is good for half of the command, but I would still\nwant to update the unchecked out refs.\n\n_Mike\n\n\n[1] https://github.com/rappazzo/dotfiles/blob/ff-refs/bin/git-ff-refs\n"},{"id":"273435","messageId":"564B47AC.7070609@drmicha.warpmail.net","threadId":"40767","inReplyTo":"CANoM8SWxMeDjwy-GwVc+En8D7N8LyzzsBKtX_MbiS4Z49DjD7g@mail.gmail.com","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2015-11-17T15:28:44Z","receivedAt":"2015-11-17T15:28:44Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Mike Rappazzo venit, vidit, dixit 17.11.2015 14:58:\n> (This message is off list)\n[cut the message part, though no big secrets there]\n\nHi Mike,\n\nI don't think there's a point in discussing this off-list.\n\nYour intentions are clearer now, yes, or else I would have asked more.\n\nI still don't like the idea of having a new command just for the purpose\nof fast-forwarding local branches from specified upstreams.\n\nWhat's wrong with \"git merge --ff-only\" once you check them out? We have\nall the gory messages when you checkout a branch or use the git prompt\nor \"branch -vv\". And if you don't - how is forgetting to \"ff-refs\"\nbetter than forgetting to \"merge --ff-only\"?\n\nIn short, I don't see a problem that this is solving, but maybe it's\nbecause we use local branches differently, I dunno.\n\nIf other people were interested they should or would have come up with\ncomments, I think (as a general rule).\n\nCheers,\nMichael\n"},{"id":"273436","messageId":"CANoM8SXrRmXPZQotQgJNNonJcb3rs5LM=JwOYoh4mX4nw2XB-g@mail.gmail.com","threadId":"40767","inReplyTo":"564B47AC.7070609@drmicha.warpmail.net","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Mike Rappazzo","fromEmail":"rappazzo@gmail.com","sentAt":"2015-11-17T15:36:36Z","receivedAt":"2015-11-17T15:36:36Z","isPatch":true,"sender":{"key":"rappazzo@gmail.com","avatar":"https://avatars.githubusercontent.com/u/525287?v=4"},"body":"On Tue, Nov 17, 2015 at 10:28 AM, Michael J Gruber\n<git@drmicha.warpmail.net> wrote:\n> Mike Rappazzo venit, vidit, dixit 17.11.2015 14:58:\n>\n> I still don't like the idea of having a new command just for the purpose\n> of fast-forwarding local branches from specified upstreams.\n>\n> What's wrong with \"git merge --ff-only\" once you check them out? We have\n> all the gory messages when you checkout a branch or use the git prompt\n> or \"branch -vv\". And if you don't - how is forgetting to \"ff-refs\"\n> better than forgetting to \"merge --ff-only\"?\n>\n> In short, I don't see a problem that this is solving, but maybe it's\n> because we use local branches differently, I dunno.\n\nFor me I use this command more as a post-fetch:\n\ngit fetch --all --prune && git-ff-refs\n\nI imagine that the big difference is in the number of branches that I\nmaintain, and perhaps in the way that I use gitk to visualize them.  I\nwould be happy to add another option to git-fetch for --ff-refs as an\nalternative if that would feel better than a full-on builtin.\n\n>\n> If other people were interested they should or would have come up with\n> comments, I think (as a general rule).\n>\n> Cheers,\n> Michael\n"},{"id":"273468","messageId":"alpine.DEB.1.00.1511181052130.1686@s15462909.onlinehome-server.info","threadId":"40767","inReplyTo":"CANoM8SXrRmXPZQotQgJNNonJcb3rs5LM=JwOYoh4mX4nw2XB-g@mail.gmail.com","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2015-11-18T09:56:02Z","receivedAt":"2015-11-18T09:56:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Mike,\n\nOn Tue, 17 Nov 2015, Mike Rappazzo wrote:\n\n> On Tue, Nov 17, 2015 at 10:28 AM, Michael J Gruber\n> <git@drmicha.warpmail.net> wrote:\n> > Mike Rappazzo venit, vidit, dixit 17.11.2015 14:58:\n> >\n> > I still don't like the idea of having a new command just for the purpose\n> > of fast-forwarding local branches from specified upstreams.\n> >\n> > What's wrong with \"git merge --ff-only\" once you check them out? We have\n> > all the gory messages when you checkout a branch or use the git prompt\n> > or \"branch -vv\". And if you don't - how is forgetting to \"ff-refs\"\n> > better than forgetting to \"merge --ff-only\"?\n> >\n> > In short, I don't see a problem that this is solving, but maybe it's\n> > because we use local branches differently, I dunno.\n> \n> For me I use this command more as a post-fetch:\n> \n> git fetch --all --prune && git-ff-refs\n> \n> I imagine that the big difference is in the number of branches that I\n> maintain, and perhaps in the way that I use gitk to visualize them.  I\n> would be happy to add another option to git-fetch for --ff-refs as an\n> alternative if that would feel better than a full-on builtin.\n\nI would much prefer, say, `git fetch --all --prune\n--fast-forward-tracking-branches` (with maybe `-T` as short option for\n`--fast-forward-tracking-branches` and/or a shorter `--ff-tracking`) to a\nnew builtin.\n\nBut yeah, I can see how it is convenient when you have to work with N\ntracking branches where N > 2.\n\nThanks,\nDscho\n"},{"id":"273666","messageId":"20151124223903.GG29185@sigill.intra.peff.net","threadId":"40767","inReplyTo":"alpine.DEB.1.00.1511181052130.1686@s15462909.onlinehome-server.info","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2015-11-24T22:39:03Z","receivedAt":"2015-11-24T22:39:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Nov 18, 2015 at 10:56:02AM +0100, Johannes Schindelin wrote:\n\n> > For me I use this command more as a post-fetch:\n> > \n> > git fetch --all --prune && git-ff-refs\n> > \n> > I imagine that the big difference is in the number of branches that I\n> > maintain, and perhaps in the way that I use gitk to visualize them.  I\n> > would be happy to add another option to git-fetch for --ff-refs as an\n> > alternative if that would feel better than a full-on builtin.\n> \n> I would much prefer, say, `git fetch --all --prune\n> --fast-forward-tracking-branches` (with maybe `-T` as short option for\n> `--fast-forward-tracking-branches` and/or a shorter `--ff-tracking`) to a\n> new builtin.\n\nFWIW, that makes a lot more sense to me, as it would presumably touch\nonly branches which track whatever we just updated, and not other random\nrefs.\n\nI have to admit that I'm a little wary of something like ff-refs meeting\nall needs, though. I have custom scripts that match my workflow and tell\nme when a branch could be updated. I could replace part of them with\n\"ff-refs --dry-run\", but that is really not much code. Basically:\n\n  git for-each-ref --format='%(refname) %(upstream)' refs/heads |\n  while read ref upstream; do\n    git merge-base --is-ancestor $ref $upstream &&\n      echo \"$ref can fast-forward\"\n  done\n\n-Peff\n"},{"id":"273849","messageId":"xmqqio4j6moo.fsf@gitster.mtv.corp.google.com","threadId":"40767","inReplyTo":"20151124223903.GG29185@sigill.intra.peff.net","subject":"Re: [PATCH 0/5] ff-refs: builtin command to fast-forward local refs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2015-12-01T00:24:07Z","receivedAt":"2015-12-01T00:24:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Wed, Nov 18, 2015 at 10:56:02AM +0100, Johannes Schindelin wrote:\n>\n>> > For me I use this command more as a post-fetch:\n>> > \n>> > git fetch --all --prune && git-ff-refs\n>> > \n>> > I imagine that the big difference is in the number of branches that I\n>> > maintain, and perhaps in the way that I use gitk to visualize them.  I\n>> > would be happy to add another option to git-fetch for --ff-refs as an\n>> > alternative if that would feel better than a full-on builtin.\n>> \n>> I would much prefer, say, `git fetch --all --prune\n>> --fast-forward-tracking-branches` (with maybe `-T` as short option for\n>> `--fast-forward-tracking-branches` and/or a shorter `--ff-tracking`) to a\n>> new builtin.\n\nHmph, I am not sure it is a good idea to allow \"git fetch\" affect\nrefs that it was not told to \"fetch\", but that is why you give a new\noption from the command line to update refs that are not involved in\nthe fetch based on what was fetched, so it might be OK.\n\nBut the above is *NOT* fast-forwarding \"tracking\" branch.  It is\ndoing something else: fast-forwarding the local branch that is based\non a remote-tracking branch.\n\n    They have refs/heads/master, and they call it their 'master'\n    branch.\n\n    You have refs/remotes/origin/master, and that is the\n    remote-tracking branch for their 'master'.\n\n    You may have prepared your 'master' to build on their 'master'\n    branch.  That is not a 'tracking branch' for anything.\n\nSo --ff-tracking and the other name above need to be rethought.\n\n> FWIW, that makes a lot more sense to me, as it would presumably touch\n> only branches which track whatever we just updated, and not other random\n> refs.\n>\n\nThis ff-refs series breaks build for me by introducing calls to chdir()\nwhose return values are not checked -Werror=unused-result, by the way.\n\n> I have to admit that I'm a little wary of something like ff-refs meeting\n> all needs, though. I have custom scripts that match my workflow and tell\n> me when a branch could be updated. I could replace part of them with\n> \"ff-refs --dry-run\", but that is really not much code. Basically:\n>\n>   git for-each-ref --format='%(refname) %(upstream)' refs/heads |\n>   while read ref upstream; do\n>     git merge-base --is-ancestor $ref $upstream &&\n>       echo \"$ref can fast-forward\"\n>   done\n\nYup.  I like that one.\n"}]}