{"thread":{"id":"42103","subject":"[PATCH v5 0/4] Add --base option to git-format-patch to record base tree info","startedAt":"2016-04-22T05:42:32Z","lastAt":"2016-04-24T04:36:14Z","messageCount":12,"participants":["Xiaolong Ye","Junio C Hamano","Ye Xiaolong"],"isPatch":true,"patchVersion":5,"patchTotal":4},"messages":[{"id":"284091","messageId":"1461303756-25975-1-git-send-email-xiaolong.ye@intel.com","threadId":"42103","inReplyTo":null,"subject":"[PATCH v5 0/4] Add --base option to git-format-patch to record base tree info","fromName":"Xiaolong Ye","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-22T05:42:32Z","receivedAt":"2016-04-22T05:42:32Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"Thanks for Junio's reviews and suggestions.\n\nThis version contains the following changes since v4:\n\n - Refine the commit log as well as the documentation according to\n   Junio's comments.\n\n - Separate out get_base_commit function from prepare_bases to obtain\n   the base commit.\n\n - Use repeated pair-wise computation to get the merge base for the\n   validation of base commit.\n\n - Extract \"auto handling thing\" from prepare_bases and put it into\n   get_base_commit.\n\n - Use format.useAutoBase boolean variable for the auto configuration\n   in format section.\n\n\nThanks,\nXiaolong.\n\nXiaolong Ye (4):\n  patch-ids: make commit_patch_id() a public helper function\n  format-patch: add '--base' option to record base tree info\n  format-patch: introduce --base=auto option\n  format-patch: introduce format.useAutoBase configuration\n\n Documentation/config.txt           |   5 ++\n Documentation/git-format-patch.txt |  60 ++++++++++++++\n builtin/log.c                      | 165 +++++++++++++++++++++++++++++++++++++\n patch-ids.c                        |   2 +-\n patch-ids.h                        |   2 +\n t/t4014-format-patch.sh            |  48 +++++++++++\n 6 files changed, 281 insertions(+), 1 deletion(-)\n\n-- \n2.8.1.221.ga4c6ba7\n\nbase-commit: e6ac6e1f7d54584c2b03f073b5f329a37f4a9561\n"},{"id":"284092","messageId":"1461303756-25975-2-git-send-email-xiaolong.ye@intel.com","threadId":"42103","inReplyTo":"1461303756-25975-1-git-send-email-xiaolong.ye@intel.com","subject":"[PATCH v5 1/4] patch-ids: make commit_patch_id() a public helper function","fromName":"Xiaolong Ye","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-22T05:42:33Z","receivedAt":"2016-04-22T05:42:33Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"Make commit_patch_id() available to other builtins.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Xiaolong Ye <xiaolong.ye@intel.com>\n---\n patch-ids.c | 2 +-\n patch-ids.h | 2 ++\n 2 files changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/patch-ids.c b/patch-ids.c\nindex b7b3e5a..a4d0016 100644\n--- a/patch-ids.c\n+++ b/patch-ids.c\n@@ -4,7 +4,7 @@\n #include \"sha1-lookup.h\"\n #include \"patch-ids.h\"\n \n-static int commit_patch_id(struct commit *commit, struct diff_options *options,\n+int commit_patch_id(struct commit *commit, struct diff_options *options,\n \t\t    unsigned char *sha1)\n {\n \tif (commit->parents)\ndiff --git a/patch-ids.h b/patch-ids.h\nindex c8c7ca1..eeb56b3 100644\n--- a/patch-ids.h\n+++ b/patch-ids.h\n@@ -13,6 +13,8 @@ struct patch_ids {\n \tstruct patch_id_bucket *patches;\n };\n \n+int commit_patch_id(struct commit *commit, struct diff_options *options,\n+\t\t    unsigned char *sha1);\n int init_patch_ids(struct patch_ids *);\n int free_patch_ids(struct patch_ids *);\n struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);\n-- \n2.8.1.221.ga4c6ba7\n"},{"id":"284093","messageId":"1461303756-25975-3-git-send-email-xiaolong.ye@intel.com","threadId":"42103","inReplyTo":"1461303756-25975-1-git-send-email-xiaolong.ye@intel.com","subject":"[PATCH v5 2/4] format-patch: add '--base' option to record base tree info","fromName":"Xiaolong Ye","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-22T05:42:34Z","receivedAt":"2016-04-22T05:42:34Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"Maintainers or third party testers may want to know the exact base tree\nthe patch series applies to. Teach git format-patch a '--base' option\nto record the base tree info and append it at the end of the first\nmessage (either the cover letter or the first patch in the series).\n\nThe base tree info consists of the \"base commit\", which is a well-known\ncommit that is part of the stable part of the project history everybody\nelse works off of, and zero or more \"prerequisite patches\", which are\nwell-known patches in flight that is not yet part of the \"base commit\"\nthat need to be applied on top of \"base commit\" in topological order\nbefore the patches can be applied.\n\nThe \"base commit\" is shown as \"base-commit: \" followed by the 40-hex of\nthe commit object name.  A \"prerequisite patch\" is shown as\n\"prerequisite-patch-id: \" followed by the 40-hex \"patch id\", which can\nbe obtained by passing the patch through the \"git patch-id --stable\"\ncommand.\n\nImagine that on top of the public commit P, you applied well-known\npatches X, Y and Z from somebody else, and then built your three-patch\nseries A, B, C, the history would be like:\n\n---P---X---Y---Z---A---B---C\n\nWith \"git format-patch --base=P -3 C\" (or variants thereof, e.g. with\n\"--cover-letter\" of using \"Z..C\" instead of \"-3 C\" to specify the\nrange), the base tree information block is shown at the end of the\nfirst message the command outputs (either the first patch, or the\ncover letter), like this:\n\nbase-commit: P\nprerequisite-patch-id: X\nprerequisite-patch-id: Y\nprerequisite-patch-id: Z\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Wu Fengguang <fengguang.wu@intel.com>\nSigned-off-by: Xiaolong Ye <xiaolong.ye@intel.com>\n---\n Documentation/git-format-patch.txt |  54 ++++++++++++++\n builtin/log.c                      | 139 +++++++++++++++++++++++++++++++++++++\n t/t4014-format-patch.sh            |  15 ++++\n 3 files changed, 208 insertions(+)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 6821441..1d790f1 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -265,6 +265,11 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.\n   Output an all-zero hash in each patch's From header instead\n   of the hash of the commit.\n \n+--base=<commit>::\n+\tRecord the base tree information to identify the state the\n+\tpatch series applies to.  See the BASE TREE INFORMATION section\n+\tbelow for details.\n+\n --root::\n \tTreat the revision argument as a <revision range>, even if it\n \tis just a single commit (that would normally be treated as a\n@@ -520,6 +525,55 @@ This should help you to submit patches inline using KMail.\n 5. Back in the compose window: add whatever other text you wish to the\n    message, complete the addressing and subject fields, and press send.\n \n+BASE TREE INFORMATION\n+---------------------\n+\n+The base tree information block is used for maintainers or third party\n+testers to know the exact state the patch series applies to. It consists\n+of the 'base commit', which is a well-known commit that is part of the\n+stable part of the project history everybody else works off of, and zero\n+or more 'prerequisite patches', which are well-known patches in flight\n+that is not yet part of the 'base commit' that need to be applied on top\n+of 'base commit' in topological order before the patches can be applied.\n+\n+The 'base commit' is shown as \"base-commit: \" followed by the 40-hex of\n+the commit object name.  A 'prerequisite patch' is shown as\n+\"prerequisite-patch-id: \" followed by the 40-hex 'patch id', which can\n+be obtained by passing the patch through the `git patch-id --stable`\n+command.\n+\n+Imagine that on top of the public commit P, you applied well-known\n+patches X, Y and Z from somebody else, and then built your three-patch\n+series A, B, C, the history would be like:\n+\n+................................................\n+---P---X---Y---Z---A---B---C\n+................................................\n+\n+With `git format-patch --base=P -3 C` (or variants thereof, e.g. with\n+`--cover-letter` of using `Z..C` instead of `-3 C` to specify the\n+range), the base tree information block is shown at the end of the\n+first message the command outputs (either the first patch, or the\n+cover letter), like this:\n+\n+------------\n+base-commit: P\n+prerequisite-patch-id: X\n+prerequisite-patch-id: Y\n+prerequisite-patch-id: Z\n+------------\n+\n+For non-linear topology, such as\n+\n+................................................\n+---P---X---A---M---C\n+    \\         /\n+     Y---Z---B\n+................................................\n+\n+You can also use `git format-patch --base=P -3 C` to generate patches\n+for A, B and C, and the identifiers for P, X, Y, Z are appended at the\n+end of the first message.\n \n EXAMPLES\n --------\ndiff --git a/builtin/log.c b/builtin/log.c\nindex dff3fbb..ee332ab 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1191,6 +1191,131 @@ static int from_callback(const struct option *opt, const char *arg, int unset)\n \treturn 0;\n }\n \n+struct base_tree_info {\n+\tstruct object_id base_commit;\n+\tint nr_patch_id, alloc_patch_id;\n+\tstruct object_id *patch_id;\n+};\n+\n+static struct commit *get_base_commit(const char *base_commit,\n+\t\t\t\t      struct commit **list,\n+\t\t\t\t      int total)\n+{\n+\tstruct commit *base = NULL;\n+\tstruct commit **rev;\n+\tint i = 0, rev_nr = 0;\n+\n+\tbase = lookup_commit_reference_by_name(base_commit);\n+\tif (!base)\n+\t\tdie(_(\"Unknown commit %s\"), base_commit);\n+\n+\tALLOC_ARRAY(rev, total);\n+\tfor (i = 0; i < total; i++)\n+\t\trev[i] = list[i];\n+\n+\trev_nr = total;\n+\t/*\n+\t * Get merge base through pair-wise computations\n+\t * and store it in rev[0].\n+\t */\n+\twhile (rev_nr > 1) {\n+\t\tfor (i = 0; i < rev_nr / 2; i++) {\n+\t\t\tstruct commit_list *merge_base;\n+\t\t\tmerge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);\n+\t\t\tif (!merge_base || merge_base->next)\n+\t\t\t\tdie(_(\"Failed to find exact merge base\"));\n+\n+\t\t\trev[i] = merge_base->item;\n+\t\t}\n+\n+\t\tif (rev_nr % 2)\n+\t\t\trev[i] = rev[2 * i];\n+\t\trev_nr = (rev_nr + 1) / 2;\n+\t}\n+\n+\tif (!in_merge_bases(base, rev[0]))\n+\t\tdie(_(\"base commit should be the ancestor of revision list\"));\n+\n+\tfor (i = 0; i < total; i++) {\n+\t\tif (base == list[i])\n+\t\t\tdie(_(\"base commit shouldn't be in revision list\"));\n+\t}\n+\n+\tfree(rev);\n+\treturn base;\n+}\n+\n+static void prepare_bases(struct base_tree_info *bases,\n+\t\t\t  struct commit *base,\n+\t\t\t  struct commit **list,\n+\t\t\t  int total)\n+{\n+\tstruct commit *commit;\n+\tstruct rev_info revs;\n+\tstruct diff_options diffopt;\n+\tint i;\n+\n+\tif (!base)\n+\t\treturn;\n+\n+\tdiff_setup(&diffopt);\n+\tDIFF_OPT_SET(&diffopt, RECURSIVE);\n+\tdiff_setup_done(&diffopt);\n+\n+\toidcpy(&bases->base_commit, &base->object.oid);\n+\n+\tinit_revisions(&revs, NULL);\n+\trevs.max_parents = 1;\n+\trevs.topo_order = 1;\n+\tfor (i = 0; i < total; i++) {\n+\t\tlist[i]->object.flags &= ~UNINTERESTING;\n+\t\tadd_pending_object(&revs, &list[i]->object, \"rev_list\");\n+\t\tlist[i]->util = (void *)1;\n+\t}\n+\tbase->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&revs, &base->object, \"base\");\n+\n+\tif (prepare_revision_walk(&revs))\n+\t\tdie(_(\"revision walk setup failed\"));\n+\t/*\n+\t * Traverse the commits list, get prerequisite patch ids\n+\t * and stuff them in bases structure.\n+\t */\n+\twhile ((commit = get_revision(&revs)) != NULL) {\n+\t\tunsigned char sha1[20];\n+\t\tstruct object_id *patch_id;\n+\t\tif (commit->util)\n+\t\t\tcontinue;\n+\t\tif (commit_patch_id(commit, &diffopt, sha1))\n+\t\t\tdie(_(\"cannot get patch id\"));\n+\t\tALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);\n+\t\tpatch_id = bases->patch_id + bases->nr_patch_id;\n+\t\thashcpy(patch_id->hash, sha1);\n+\t\tbases->nr_patch_id++;\n+\t}\n+}\n+\n+static void print_bases(struct base_tree_info *bases)\n+{\n+\tint i;\n+\n+\t/* Only do this once, either for the cover or for the first one */\n+\tif (is_null_oid(&bases->base_commit))\n+\t\treturn;\n+\n+\t/* Show the base commit */\n+\tprintf(\"base-commit: %s\\n\", oid_to_hex(&bases->base_commit));\n+\n+\t/* Show the prerequisite patches */\n+\tfor (i = bases->nr_patch_id - 1; i >= 0; i--)\n+\t\tprintf(\"prerequisite-patch-id: %s\\n\", oid_to_hex(&bases->patch_id[i]));\n+\n+\tfree(bases->patch_id);\n+\tbases->nr_patch_id = 0;\n+\tbases->alloc_patch_id = 0;\n+\toidclr(&bases->base_commit);\n+}\n+\n int cmd_format_patch(int argc, const char **argv, const char *prefix)\n {\n \tstruct commit *commit;\n@@ -1215,6 +1340,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tint reroll_count = -1;\n \tchar *branch_name = NULL;\n \tchar *from = NULL;\n+\tchar *base_commit = NULL;\n+\tstruct base_tree_info bases;\n+\n \tconst struct option builtin_format_patch_options[] = {\n \t\t{ OPTION_CALLBACK, 'n', \"numbered\", &numbered, NULL,\n \t\t\t    N_(\"use [PATCH n/m] even with a single patch\"),\n@@ -1277,6 +1405,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\t    PARSE_OPT_OPTARG, thread_callback },\n \t\tOPT_STRING(0, \"signature\", &signature, N_(\"signature\"),\n \t\t\t    N_(\"add a signature\")),\n+\t\tOPT_STRING(0, \"base\", &base_commit, N_(\"base-commit\"),\n+\t\t\t   N_(\"add prerequisite tree info to the patch series\")),\n \t\tOPT_FILENAME(0, \"signature-file\", &signature_file,\n \t\t\t\tN_(\"add a signature from a file\")),\n \t\tOPT__QUIET(&quiet, N_(\"don't print the patch filenames\")),\n@@ -1514,6 +1644,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tsignature = strbuf_detach(&buf, NULL);\n \t}\n \n+\tmemset(&bases, 0, sizeof(bases));\n+\tif (base_commit) {\n+\t\tstruct commit *base = get_base_commit(base_commit, list, nr);\n+\t\treset_revision_walk();\n+\t\tprepare_bases(&bases, base, list, nr);\n+\t}\n+\n \tif (in_reply_to || thread || cover_letter)\n \t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n \tif (in_reply_to) {\n@@ -1527,6 +1664,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, use_stdout,\n \t\t\t\t  origin, nr, list, branch_name, quiet);\n+\t\tprint_bases(&bases);\n \t\ttotal++;\n \t\tstart_number--;\n \t}\n@@ -1592,6 +1730,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\t\t       rev.mime_boundary);\n \t\t\telse\n \t\t\t\tprint_signature();\n+\t\t\tprint_bases(&bases);\n \t\t}\n \t\tif (!use_stdout)\n \t\t\tfclose(stdout);\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex eed2981..a6ce727 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1460,4 +1460,19 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n \ttest_path_is_dir patchset\n '\n \n+test_expect_success 'format-patch --base' '\n+\tgit checkout side &&\n+\tgit format-patch --stdout --base=HEAD~~~ -1 >patch &&\n+\tgrep -e \"^base-commit:\" -A3 patch >actual &&\n+\techo \"base-commit: $(git rev-parse HEAD~~~)\" >expected &&\n+\techo \"prerequisite-patch-id: $(git show --patch HEAD~~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n+\techo \"prerequisite-patch-id: $(git show --patch HEAD~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'format-patch --base error handling' '\n+\t! git format-patch --base=HEAD~ -2 &&\n+\t! git format-patch --base=HEAD~ -3\n+'\n+\n test_done\n-- \n2.8.1.221.ga4c6ba7\n"},{"id":"284094","messageId":"1461303756-25975-4-git-send-email-xiaolong.ye@intel.com","threadId":"42103","inReplyTo":"1461303756-25975-1-git-send-email-xiaolong.ye@intel.com","subject":"[PATCH v5 3/4] format-patch: introduce --base=auto option","fromName":"Xiaolong Ye","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-22T05:42:35Z","receivedAt":"2016-04-22T05:42:35Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"Introduce --base=auto to record the base commit info automatically, the\nbase_commit will be the merge base of tip commit of the upstream branch\nand revision-range specified in cmdline.\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nHelped-by: Wu Fengguang <fengguang.wu@intel.com>\nSigned-off-by: Xiaolong Ye <xiaolong.ye@intel.com>\n---\n Documentation/git-format-patch.txt |  6 ++++++\n builtin/log.c                      | 27 ++++++++++++++++++++++++---\n t/t4014-format-patch.sh            | 15 +++++++++++++++\n 3 files changed, 45 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt\nindex 1d790f1..bdeecd5 100644\n--- a/Documentation/git-format-patch.txt\n+++ b/Documentation/git-format-patch.txt\n@@ -575,6 +575,12 @@ You can also use `git format-patch --base=P -3 C` to generate patches\n for A, B and C, and the identifiers for P, X, Y, Z are appended at the\n end of the first message.\n \n+If set `--base=auto` in cmdline, it will track base commit automatically,\n+the base commit will be the merge base of tip commit of the remote-tracking\n+branch and revision-range specified in cmdline.\n+For a local branch, you need to track a remote branch by `git branch\n+--set-upstream-to` before using this option.\n+\n EXAMPLES\n --------\n \ndiff --git a/builtin/log.c b/builtin/log.c\nindex ee332ab..7851d20 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1205,9 +1205,30 @@ static struct commit *get_base_commit(const char *base_commit,\n \tstruct commit **rev;\n \tint i = 0, rev_nr = 0;\n \n-\tbase = lookup_commit_reference_by_name(base_commit);\n-\tif (!base)\n-\t\tdie(_(\"Unknown commit %s\"), base_commit);\n+\tif (!strcmp(base_commit, \"auto\")) {\n+\t\tstruct branch *curr_branch = branch_get(NULL);\n+\t\tconst char *upstream = branch_get_upstream(curr_branch, NULL);\n+\t\tif (upstream) {\n+\t\t\tunsigned char sha1[20];\n+\t\t\tif (get_sha1(upstream, sha1))\n+\t\t\t\tdie(_(\"Failed to resolve '%s' as a valid ref.\"), upstream);\n+\t\t\tstruct commit *commit = lookup_commit_or_die(sha1, \"upstream base\");\n+\t\t\tstruct commit_list *base_list = get_merge_bases_many(commit, total, list);\n+\t\t\t/* There should be one and only one merge base. */\n+\t\t\tif (!base_list || base_list->next)\n+\t\t\t\tdie(_(\"Could not find exact merge base.\"));\n+\t\t\tbase = base_list->item;\n+\t\t\tfree_commit_list(base_list);\n+\t\t} else {\n+\t\t\tdie(_(\"Failed to get upstream, if you want to record base commit automatically,\\n\"\n+\t\t\t      \"please use git branch --set-upstream-to to track a remote branch.\\n\"\n+\t\t\t      \"Or you could specify base commit by --base=<base-commit-id> manually.\"));\n+\t\t}\n+\t} else {\n+\t\tbase = lookup_commit_reference_by_name(base_commit);\n+\t\tif (!base)\n+\t\t\tdie(_(\"Unknown commit %s\"), base_commit);\n+\t}\n \n \tALLOC_ARRAY(rev, total);\n \tfor (i = 0; i < total; i++)\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex a6ce727..afcf8b8 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1475,4 +1475,19 @@ test_expect_success 'format-patch --base error handling' '\n \t! git format-patch --base=HEAD~ -3\n '\n \n+test_expect_success 'format-patch --base=auto' '\n+\tgit checkout -b new master &&\n+\tgit branch --set-upstream-to=master &&\n+\techo \"A\" >>file &&\n+\tgit add file &&\n+\tgit commit -m \"New change #A\" &&\n+\techo \"B\" >>file &&\n+\tgit add file &&\n+\tgit commit -m \"New change #B\" &&\n+\tgit format-patch --stdout --base=auto -2 >patch &&\n+\tgrep -e \"^base-commit:\" patch >actual &&\n+\techo \"base-commit: $(git rev-parse master)\" >expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.8.1.221.ga4c6ba7\n"},{"id":"284095","messageId":"1461303756-25975-5-git-send-email-xiaolong.ye@intel.com","threadId":"42103","inReplyTo":"1461303756-25975-1-git-send-email-xiaolong.ye@intel.com","subject":"[PATCH v5 4/4] format-patch: introduce format.useAutoBase configuration","fromName":"Xiaolong Ye","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-22T05:42:36Z","receivedAt":"2016-04-22T05:42:36Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"This allows to record the base commit automatically, it is equivalent\nto set --base=auto in cmdline.\n\nThe format.useAutoBase has lower priority than command line option,\nso if user set format.useAutoBase and pass the command line option in\nthe meantime, base_commit will be the one passed to command line\noption.\n\nSigned-off-by: Xiaolong Ye <xiaolong.ye@intel.com>\n---\n Documentation/config.txt |  5 +++++\n builtin/log.c            | 17 +++++++++++------\n t/t4014-format-patch.sh  | 18 ++++++++++++++++++\n 3 files changed, 34 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 42d2b50..1fe2a85 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1259,6 +1259,11 @@ format.outputDirectory::\n \tSet a custom directory to store the resulting files instead of the\n \tcurrent working directory.\n \n+format.useAutoBase::\n+\tA boolean value which lets you enable the `--base=auto` option of\n+\tformat-patch by default.\n+\n+\n filter.<driver>.clean::\n \tThe command which is used to convert the content of a worktree\n \tfile to a blob upon checkin.  See linkgit:gitattributes[5] for\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 7851d20..c3aeef8 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -702,6 +702,7 @@ static void add_header(const char *value)\n #define THREAD_DEEP 2\n static int thread;\n static int do_signoff;\n+static int base_auto;\n static const char *signature = git_version_string;\n static const char *signature_file;\n static int config_cover_letter;\n@@ -786,6 +787,10 @@ static int git_format_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!strcmp(var, \"format.outputdirectory\"))\n \t\treturn git_config_string(&config_output_directory, var, value);\n+\tif (!strcmp(var, \"format.useautobase\")) {\n+\t\tbase_auto = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \n \treturn git_log_config(var, value, cb);\n }\n@@ -1205,7 +1210,11 @@ static struct commit *get_base_commit(const char *base_commit,\n \tstruct commit **rev;\n \tint i = 0, rev_nr = 0;\n \n-\tif (!strcmp(base_commit, \"auto\")) {\n+\tif (base_commit && strcmp(base_commit, \"auto\")) {\n+\t\tbase = lookup_commit_reference_by_name(base_commit);\n+\t\tif (!base)\n+\t\t\tdie(_(\"Unknown commit %s\"), base_commit);\n+\t} else if ((base_commit && !strcmp(base_commit, \"auto\")) || base_auto) {\n \t\tstruct branch *curr_branch = branch_get(NULL);\n \t\tconst char *upstream = branch_get_upstream(curr_branch, NULL);\n \t\tif (upstream) {\n@@ -1224,10 +1233,6 @@ static struct commit *get_base_commit(const char *base_commit,\n \t\t\t      \"please use git branch --set-upstream-to to track a remote branch.\\n\"\n \t\t\t      \"Or you could specify base commit by --base=<base-commit-id> manually.\"));\n \t\t}\n-\t} else {\n-\t\tbase = lookup_commit_reference_by_name(base_commit);\n-\t\tif (!base)\n-\t\t\tdie(_(\"Unknown commit %s\"), base_commit);\n \t}\n \n \tALLOC_ARRAY(rev, total);\n@@ -1666,7 +1671,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t}\n \n \tmemset(&bases, 0, sizeof(bases));\n-\tif (base_commit) {\n+\tif (base_commit || base_auto) {\n \t\tstruct commit *base = get_base_commit(base_commit, list, nr);\n \t\treset_revision_walk();\n \t\tprepare_bases(&bases, base, list, nr);\ndiff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\nindex afcf8b8..dfee0b6 100755\n--- a/t/t4014-format-patch.sh\n+++ b/t/t4014-format-patch.sh\n@@ -1490,4 +1490,22 @@ test_expect_success 'format-patch --base=auto' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'format-patch format.base option' '\n+\ttest_when_finished \"git config --unset format.useAutoBase\" &&\n+\tgit config format.useAutoBase true &&\n+\tgit format-patch --stdout -1 >patch &&\n+\tgrep -e \"^base-commit:\" patch >actual &&\n+\techo \"base-commit: $(git rev-parse master)\" >expected &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'format-patch --base overrides format.base' '\n+\ttest_when_finished \"git config --unset format.useAutoBase\" &&\n+\tgit config format.useAutoBase true &&\n+\tgit format-patch --stdout --base=HEAD~ -1 >patch &&\n+\tgrep -e \"^base-commit:\" patch >actual &&\n+\techo \"base-commit: $(git rev-parse HEAD~)\" >expected &&\n+\ttest_cmp expected actual\n+'\n+\n test_done\n-- \n2.8.1.221.ga4c6ba7\n"},{"id":"284190","messageId":"xmqqshydnx0v.fsf@gitster.mtv.corp.google.com","threadId":"42103","inReplyTo":"1461303756-25975-3-git-send-email-xiaolong.ye@intel.com","subject":"Re: [PATCH v5 2/4] format-patch: add '--base' option to record base tree info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-22T21:39:28Z","receivedAt":"2016-04-22T21:39:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Xiaolong Ye <xiaolong.ye@intel.com> writes:\n\n> diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\n> index eed2981..a6ce727 100755\n> --- a/t/t4014-format-patch.sh\n> +++ b/t/t4014-format-patch.sh\n> @@ -1460,4 +1460,19 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n>  \ttest_path_is_dir patchset\n>  '\n>  \n> +test_expect_success 'format-patch --base' '\n> +\tgit checkout side &&\n> +\tgit format-patch --stdout --base=HEAD~~~ -1 >patch &&\n> +\tgrep -e \"^base-commit:\" -A3 patch >actual &&\n> +\techo \"base-commit: $(git rev-parse HEAD~~~)\" >expected &&\n\nHEAD~3 would be easier to read (and HEAD~2 is easier than HEAD~~).\n\n> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n> +\ttest_cmp expected actual\n> +'\n> +\n> +test_expect_success 'format-patch --base error handling' '\n> +\t! git format-patch --base=HEAD~ -2 &&\n> +\t! git format-patch --base=HEAD~ -3\n> +'\n\nWhen making sure that \"git\" exits with a failure in a controlled way\n(i.e. you want to consider \"git\" that segfaults as not passing the\ntest), do not use \"! git cmd\", but use \"test_must_fail git cmd\"\ninstead.\n\nYou now have a quite elaborate logic in base validation in this\nround.  Is the topology of the history used in this test still\ncomplex enough to make sure the logic is being tested?\n"},{"id":"284191","messageId":"xmqqoa91nwf9.fsf@gitster.mtv.corp.google.com","threadId":"42103","inReplyTo":"1461303756-25975-3-git-send-email-xiaolong.ye@intel.com","subject":"Re: [PATCH v5 2/4] format-patch: add '--base' option to record base tree info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-22T21:52:26Z","receivedAt":"2016-04-22T21:52:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Xiaolong Ye <xiaolong.ye@intel.com> writes:\n\n> +static struct commit *get_base_commit(const char *base_commit,\n> +\t\t\t\t      struct commit **list,\n> +\t\t\t\t      int total)\n> +{\n> +\tstruct commit *base = NULL;\n> +\tstruct commit **rev;\n> +\tint i = 0, rev_nr = 0;\n> +\n> +\tbase = lookup_commit_reference_by_name(base_commit);\n> +\tif (!base)\n> +\t\tdie(_(\"Unknown commit %s\"), base_commit);\n> +\n> +\tALLOC_ARRAY(rev, total);\n> +\tfor (i = 0; i < total; i++)\n> +\t\trev[i] = list[i];\n> +\n> +\trev_nr = total;\n> +\t/*\n> +\t * Get merge base through pair-wise computations\n> +\t * and store it in rev[0].\n> +\t */\n> +\twhile (rev_nr > 1) {\n> +\t\tfor (i = 0; i < rev_nr / 2; i++) {\n> +\t\t\tstruct commit_list *merge_base;\n> +\t\t\tmerge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);\n> +\t\t\tif (!merge_base || merge_base->next)\n> +\t\t\t\tdie(_(\"Failed to find exact merge base\"));\n> +\n> +\t\t\trev[i] = merge_base->item;\n> +\t\t}\n\nSo merge-base(0,1) is stored in rev[0], merge-base(2,3) is then\nstored in rev[1], etc. and the last item, if rev_nr is odd, is left\nin rev[rev_nr-1].  When the loop finishes, i is left as rev_nr/2\nand...\n\n> +\t\tif (rev_nr % 2)\n> +\t\t\trev[i] = rev[2 * i];\n\n... when rev_nr is odd, that left-over thing moved down here.\nE.g. if rev_nr == 5, the loop is left with i==2, rev[0] and rev[1]\nare filled with pairwise merge bases, and this moves rev[4] to\nrev[2], so that we can further process rev[0,1,2] with rev_nr set to\n3 (i.e. (rev_nr + 1) / 2 below).\n\nSounds correct.\n\n> +\t\trev_nr = (rev_nr + 1) / 2;\n> +\t}\n> +\n> +\tif (!in_merge_bases(base, rev[0]))\n> +\t\tdie(_(\"base commit should be the ancestor of revision list\"));\n> +\n> +\tfor (i = 0; i < total; i++) {\n> +\t\tif (base == list[i])\n> +\t\t\tdie(_(\"base commit shouldn't be in revision list\"));\n> +\t}\n> +\n> +\tfree(rev);\n> +\treturn base;\n> +}\n"},{"id":"284197","messageId":"xmqqk2jpnwc7.fsf@gitster.mtv.corp.google.com","threadId":"42103","inReplyTo":"1461303756-25975-4-git-send-email-xiaolong.ye@intel.com","subject":"Re: [PATCH v5 3/4] format-patch: introduce --base=auto option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-22T21:54:16Z","receivedAt":"2016-04-22T21:54:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Xiaolong Ye <xiaolong.ye@intel.com> writes:\n\n> +\t\tif (upstream) {\n> +\t\t\tunsigned char sha1[20];\n> +\t\t\tif (get_sha1(upstream, sha1))\n> +\t\t\t\tdie(_(\"Failed to resolve '%s' as a valid ref.\"), upstream);\n> +\t\t\tstruct commit *commit = lookup_commit_or_die(sha1, \"upstream base\");\n> +\t\t\tstruct commit_list *base_list = get_merge_bases_many(commit, total, list);\n\nThis introduces decl-after-statement.\n"},{"id":"284198","messageId":"xmqqfuudnw35.fsf@gitster.mtv.corp.google.com","threadId":"42103","inReplyTo":"1461303756-25975-3-git-send-email-xiaolong.ye@intel.com","subject":"Re: [PATCH v5 2/4] format-patch: add '--base' option to record base tree info","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-22T21:59:42Z","receivedAt":"2016-04-22T21:59:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Xiaolong Ye <xiaolong.ye@intel.com> writes:\n\n> +test_expect_success 'format-patch --base' '\n> +\tgit checkout side &&\n> +\tgit format-patch --stdout --base=HEAD~~~ -1 >patch &&\n> +\tgrep -e \"^base-commit:\" -A3 patch >actual &&\n\nThe -A3 is GNUism.  To do this portably, perhaps you can do\n\n\tsed -n -e \"/^base-commit:/,+3p\"\n\nor something like that.\n\nBut more importantly, grabbing 3 lines (and always 3 lines) will not\ncatch a future bug that somebody else may introduce to this code\nthat shows extra \"prerequisite-patch-id:\" after them.\n\n> +\techo \"base-commit: $(git rev-parse HEAD~~~)\" >expected &&\n> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n> +\ttest_cmp expected actual\n> +'\n"},{"id":"284232","messageId":"20160424030547.GA31188@yexl-desktop","threadId":"42103","inReplyTo":"xmqqshydnx0v.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 2/4] format-patch: add '--base' option to record base tree info","fromName":"Ye Xiaolong","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-24T03:05:47Z","receivedAt":"2016-04-24T03:05:47Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"On Fri, Apr 22, 2016 at 02:39:28PM -0700, Junio C Hamano wrote:\n>Xiaolong Ye <xiaolong.ye@intel.com> writes:\n>\n>> diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh\n>> index eed2981..a6ce727 100755\n>> --- a/t/t4014-format-patch.sh\n>> +++ b/t/t4014-format-patch.sh\n>> @@ -1460,4 +1460,19 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '\n>>  \ttest_path_is_dir patchset\n>>  '\n>>  \n>> +test_expect_success 'format-patch --base' '\n>> +\tgit checkout side &&\n>> +\tgit format-patch --stdout --base=HEAD~~~ -1 >patch &&\n>> +\tgrep -e \"^base-commit:\" -A3 patch >actual &&\n>> +\techo \"base-commit: $(git rev-parse HEAD~~~)\" >expected &&\n>\n>HEAD~3 would be easier to read (and HEAD~2 is easier than HEAD~~).\n>\n>> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n>> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n>> +\ttest_cmp expected actual\n>> +'\n>> +\n>> +test_expect_success 'format-patch --base error handling' '\n>> +\t! git format-patch --base=HEAD~ -2 &&\n>> +\t! git format-patch --base=HEAD~ -3\n>> +'\n>\n>When making sure that \"git\" exits with a failure in a controlled way\n>(i.e. you want to consider \"git\" that segfaults as not passing the\n>test), do not use \"! git cmd\", but use \"test_must_fail git cmd\"\n>instead.\n\nThanks for the reminder, I misunderstood the guide in t/README before.\n\n>\n>You now have a quite elaborate logic in base validation in this\n>rounhd.  Is the topology of the history used in this test still\n>complex enough to make sure the logic is being tested?\n\nI'll try to set up more complex topology of the history for the\ntestcase.\n\nThanks,\nXiaolong.\n"},{"id":"284235","messageId":"20160424043247.GB1258@yexl-desktop","threadId":"42103","inReplyTo":"xmqqk2jpnwc7.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 3/4] format-patch: introduce --base=auto option","fromName":"Ye Xiaolong","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-24T04:32:48Z","receivedAt":"2016-04-24T04:32:48Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"On Fri, Apr 22, 2016 at 02:54:16PM -0700, Junio C Hamano wrote:\n>Xiaolong Ye <xiaolong.ye@intel.com> writes:\n>\n>> +\t\tif (upstream) {\n>> +\t\t\tunsigned char sha1[20];\n>> +\t\t\tif (get_sha1(upstream, sha1))\n>> +\t\t\t\tdie(_(\"Failed to resolve '%s' as a valid ref.\"), upstream);\n>> +\t\t\tstruct commit *commit = lookup_commit_or_die(sha1, \"upstream base\");\n>> +\t\t\tstruct commit_list *base_list = get_merge_bases_many(commit, total, list);\n>\n>This introduces decl-after-statement.\n\nwill fix it.\n\nThanks,\nXiaolong.\n"},{"id":"284236","messageId":"20160424043614.GC1258@yexl-desktop","threadId":"42103","inReplyTo":"xmqqfuudnw35.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 2/4] format-patch: add '--base' option to record base tree info","fromName":"Ye Xiaolong","fromEmail":"xiaolong.ye@intel.com","sentAt":"2016-04-24T04:36:14Z","receivedAt":"2016-04-24T04:36:14Z","isPatch":true,"sender":{"key":"xiaolong.ye@intel.com","avatar":"https://avatars.githubusercontent.com/u/21098480?v=4"},"body":"On Fri, Apr 22, 2016 at 02:59:42PM -0700, Junio C Hamano wrote:\n>Xiaolong Ye <xiaolong.ye@intel.com> writes:\n>\n>> +test_expect_success 'format-patch --base' '\n>> +\tgit checkout side &&\n>> +\tgit format-patch --stdout --base=HEAD~~~ -1 >patch &&\n>> +\tgrep -e \"^base-commit:\" -A3 patch >actual &&\n>\n>The -A3 is GNUism.  To do this portably, perhaps you can do\n>\n>\tsed -n -e \"/^base-commit:/,+3p\"\n>\n>or something like that.\n>\n>But more importantly, grabbing 3 lines (and always 3 lines) will not\n>catch a future bug that somebody else may introduce to this code\n>that shows extra \"prerequisite-patch-id:\" after them.\n\nI'll try to improve the testcase to make it more sensible.\n\nThanks,\nXiaolong\n>\n>> +\techo \"base-commit: $(git rev-parse HEAD~~~)\" >expected &&\n>> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n>> +\techo \"prerequisite-patch-id: $(git show --patch HEAD~ | git patch-id --stable | awk \"{print \\$1}\")\" >>expected &&\n>> +\ttest_cmp expected actual\n>> +'\n"}]}