format-patch: introduce --base=auto option

Introduce --base=auto to record the base commit info automatically, the base_commit will be the merge base of tip commit of the upstream branch and revision-range specified in cmdline. Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Xiaolong Ye <xiaolong.ye@intel.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Xiaolong Ye committed Apr 26, 2016 at 15:51 UTC 3de665175f3433ccd1dadd4d5d09fa9553948e55
3 files changed +72 -3
Documentation/git-format-patch.txt
+6
@@ -575,6 +575,12 @@ You can also use `git format-patch --base=P -3 C` to generate patches
575 for A, B and C, and the identifiers for P, X, Y, Z are appended at the
576 end of the first message.
577
578 +If set `--base=auto` in cmdline, it will track base commit automatically,
579 +the base commit will be the merge base of tip commit of the remote-tracking
580 +branch and revision-range specified in cmdline.
581 +For a local branch, you need to track a remote branch by `git branch
582 +--set-upstream-to` before using this option.
583 +
584 EXAMPLES
585 --------
586
builtin/log.c
+27 -3
@@ -1199,9 +1199,33 @@ static struct commit *get_base_commit(const char *base_commit,
1199 struct commit **rev;
1200 int i = 0, rev_nr = 0;
1201
1202 - base = lookup_commit_reference_by_name(base_commit);
1203 - if (!base)
1204 - die(_("Unknown commit %s"), base_commit);
1202 + if (!strcmp(base_commit, "auto")) {
1203 + struct branch *curr_branch = branch_get(NULL);
1204 + const char *upstream = branch_get_upstream(curr_branch, NULL);
1205 + if (upstream) {
1206 + struct commit_list *base_list;
1207 + struct commit *commit;
1208 + unsigned char sha1[20];
1209 +
1210 + if (get_sha1(upstream, sha1))
1211 + die(_("Failed to resolve '%s' as a valid ref."), upstream);
1212 + commit = lookup_commit_or_die(sha1, "upstream base");
1213 + base_list = get_merge_bases_many(commit, total, list);
1214 + /* There should be one and only one merge base. */
1215 + if (!base_list || base_list->next)
1216 + die(_("Could not find exact merge base."));
1217 + base = base_list->item;
1218 + free_commit_list(base_list);
1219 + } else {
1220 + die(_("Failed to get upstream, if you want to record base commit automatically,\n"
1221 + "please use git branch --set-upstream-to to track a remote branch.\n"
1222 + "Or you could specify base commit by --base=<base-commit-id> manually."));
1223 + }
1224 + } else {
1225 + base = lookup_commit_reference_by_name(base_commit);
1226 + if (!base)
1227 + die(_("Unknown commit %s"), base_commit);
1228 + }
1229
1230 ALLOC_ARRAY(rev, total);
1231 for (i = 0; i < total; i++)
t/t4014-format-patch.sh
+39
@@ -1507,4 +1507,43 @@ test_expect_success 'format-patch --base errors out when base commit is not ance
1507 test_cmp expected actual
1508 '
1509
1510 +test_expect_success 'format-patch --base=auto' '
1511 + git checkout -b upstream master &&
1512 + git checkout -b local upstream &&
1513 + git branch --set-upstream-to=upstream &&
1514 + test_commit N1 &&
1515 + test_commit N2 &&
1516 + git format-patch --stdout --base=auto -2 >patch &&
1517 + grep "^base-commit:" patch >actual &&
1518 + echo "base-commit: $(git rev-parse upstream)" >expected &&
1519 + test_cmp expected actual
1520 +'
1521 +
1522 +test_expect_success 'format-patch errors out when history involves criss-cross' '
1523 + # setup criss-cross history
1524 + #
1525 + # B---M1---D
1526 + # / \ /
1527 + # A X
1528 + # \ / \
1529 + # C---M2---E
1530 + #
1531 + git checkout master &&
1532 + test_commit A &&
1533 + git checkout -b xb master &&
1534 + test_commit B &&
1535 + git checkout -b xc master &&
1536 + test_commit C &&
1537 + git checkout -b xbc xb -- &&
1538 + git merge xc &&
1539 + git checkout -b xcb xc -- &&
1540 + git branch --set-upstream-to=xbc &&
1541 + git merge xb &&
1542 + git checkout xbc &&
1543 + test_commit D &&
1544 + git checkout xcb &&
1545 + test_commit E &&
1546 + test_must_fail git format-patch --base=auto -1
1547 +'
1548 +
1549 test_done