Skip to content

Commit 5f8c70a

Browse files
committed
Merge branch 'jk/format-auto-base-when-able'
"git format-patch" learns to take "whenAble" as a possible value for the format.useAutoBase configuration variable to become no-op when the automatically computed base does not make sense. * jk/format-auto-base-when-able: format-patch: teach format.useAutoBase "whenAble" option
2 parents 7da656f + 7efba5f commit 5f8c70a

File tree

3 files changed

+130
-26
lines changed

3 files changed

+130
-26
lines changed

Documentation/config/format.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ format.outputDirectory::
9696

9797
format.useAutoBase::
9898
A boolean value which lets you enable the `--base=auto` option of
99-
format-patch by default.
99+
format-patch by default. Can also be set to "whenAble" to allow
100+
enabling `--base=auto` if a suitable base is available, but to skip
101+
adding base info otherwise without the format dying.
100102

101103
format.notes::
102104
Provides the default value for the `--notes` option to

builtin/log.c

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -805,9 +805,15 @@ enum cover_from_description {
805805
COVER_FROM_AUTO
806806
};
807807

808+
enum auto_base_setting {
809+
AUTO_BASE_NEVER,
810+
AUTO_BASE_ALWAYS,
811+
AUTO_BASE_WHEN_ABLE
812+
};
813+
808814
static enum thread_level thread;
809815
static int do_signoff;
810-
static int base_auto;
816+
static enum auto_base_setting auto_base;
811817
static char *from;
812818
static const char *signature = git_version_string;
813819
static const char *signature_file;
@@ -906,7 +912,11 @@ static int git_format_config(const char *var, const char *value, void *cb)
906912
if (!strcmp(var, "format.outputdirectory"))
907913
return git_config_string(&config_output_directory, var, value);
908914
if (!strcmp(var, "format.useautobase")) {
909-
base_auto = git_config_bool(var, value);
915+
if (value && !strcasecmp(value, "whenAble")) {
916+
auto_base = AUTO_BASE_WHEN_ABLE;
917+
return 0;
918+
}
919+
auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER;
910920
return 0;
911921
}
912922
if (!strcmp(var, "format.from")) {
@@ -1425,6 +1435,23 @@ static int from_callback(const struct option *opt, const char *arg, int unset)
14251435
return 0;
14261436
}
14271437

1438+
static int base_callback(const struct option *opt, const char *arg, int unset)
1439+
{
1440+
const char **base_commit = opt->value;
1441+
1442+
if (unset) {
1443+
auto_base = AUTO_BASE_NEVER;
1444+
*base_commit = NULL;
1445+
} else if (!strcmp(arg, "auto")) {
1446+
auto_base = AUTO_BASE_ALWAYS;
1447+
*base_commit = NULL;
1448+
} else {
1449+
auto_base = AUTO_BASE_NEVER;
1450+
*base_commit = arg;
1451+
}
1452+
return 0;
1453+
}
1454+
14281455
struct base_tree_info {
14291456
struct object_id base_commit;
14301457
int nr_patch_id, alloc_patch_id;
@@ -1437,33 +1464,69 @@ static struct commit *get_base_commit(const char *base_commit,
14371464
{
14381465
struct commit *base = NULL;
14391466
struct commit **rev;
1440-
int i = 0, rev_nr = 0;
1467+
int i = 0, rev_nr = 0, auto_select, die_on_failure;
14411468

1442-
if (base_commit && strcmp(base_commit, "auto")) {
1469+
switch (auto_base) {
1470+
case AUTO_BASE_NEVER:
1471+
if (base_commit) {
1472+
auto_select = 0;
1473+
die_on_failure = 1;
1474+
} else {
1475+
/* no base information is requested */
1476+
return NULL;
1477+
}
1478+
break;
1479+
case AUTO_BASE_ALWAYS:
1480+
case AUTO_BASE_WHEN_ABLE:
1481+
if (base_commit) {
1482+
BUG("requested automatic base selection but a commit was provided");
1483+
} else {
1484+
auto_select = 1;
1485+
die_on_failure = auto_base == AUTO_BASE_ALWAYS;
1486+
}
1487+
break;
1488+
default:
1489+
BUG("unexpected automatic base selection method");
1490+
}
1491+
1492+
if (!auto_select) {
14431493
base = lookup_commit_reference_by_name(base_commit);
14441494
if (!base)
14451495
die(_("unknown commit %s"), base_commit);
1446-
} else if ((base_commit && !strcmp(base_commit, "auto"))) {
1496+
} else {
14471497
struct branch *curr_branch = branch_get(NULL);
14481498
const char *upstream = branch_get_upstream(curr_branch, NULL);
14491499
if (upstream) {
14501500
struct commit_list *base_list;
14511501
struct commit *commit;
14521502
struct object_id oid;
14531503

1454-
if (get_oid(upstream, &oid))
1455-
die(_("failed to resolve '%s' as a valid ref"), upstream);
1504+
if (get_oid(upstream, &oid)) {
1505+
if (die_on_failure)
1506+
die(_("failed to resolve '%s' as a valid ref"), upstream);
1507+
else
1508+
return NULL;
1509+
}
14561510
commit = lookup_commit_or_die(&oid, "upstream base");
14571511
base_list = get_merge_bases_many(commit, total, list);
14581512
/* There should be one and only one merge base. */
1459-
if (!base_list || base_list->next)
1460-
die(_("could not find exact merge base"));
1513+
if (!base_list || base_list->next) {
1514+
if (die_on_failure) {
1515+
die(_("could not find exact merge base"));
1516+
} else {
1517+
free_commit_list(base_list);
1518+
return NULL;
1519+
}
1520+
}
14611521
base = base_list->item;
14621522
free_commit_list(base_list);
14631523
} else {
1464-
die(_("failed to get upstream, if you want to record base commit automatically,\n"
1465-
"please use git branch --set-upstream-to to track a remote branch.\n"
1466-
"Or you could specify base commit by --base=<base-commit-id> manually"));
1524+
if (die_on_failure)
1525+
die(_("failed to get upstream, if you want to record base commit automatically,\n"
1526+
"please use git branch --set-upstream-to to track a remote branch.\n"
1527+
"Or you could specify base commit by --base=<base-commit-id> manually"));
1528+
else
1529+
return NULL;
14671530
}
14681531
}
14691532

@@ -1480,8 +1543,14 @@ static struct commit *get_base_commit(const char *base_commit,
14801543
for (i = 0; i < rev_nr / 2; i++) {
14811544
struct commit_list *merge_base;
14821545
merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
1483-
if (!merge_base || merge_base->next)
1484-
die(_("failed to find exact merge base"));
1546+
if (!merge_base || merge_base->next) {
1547+
if (die_on_failure) {
1548+
die(_("failed to find exact merge base"));
1549+
} else {
1550+
free(rev);
1551+
return NULL;
1552+
}
1553+
}
14851554

14861555
rev[i] = merge_base->item;
14871556
}
@@ -1491,12 +1560,24 @@ static struct commit *get_base_commit(const char *base_commit,
14911560
rev_nr = DIV_ROUND_UP(rev_nr, 2);
14921561
}
14931562

1494-
if (!in_merge_bases(base, rev[0]))
1495-
die(_("base commit should be the ancestor of revision list"));
1563+
if (!in_merge_bases(base, rev[0])) {
1564+
if (die_on_failure) {
1565+
die(_("base commit should be the ancestor of revision list"));
1566+
} else {
1567+
free(rev);
1568+
return NULL;
1569+
}
1570+
}
14961571

14971572
for (i = 0; i < total; i++) {
1498-
if (base == list[i])
1499-
die(_("base commit shouldn't be in revision list"));
1573+
if (base == list[i]) {
1574+
if (die_on_failure) {
1575+
die(_("base commit shouldn't be in revision list"));
1576+
} else {
1577+
free(rev);
1578+
return NULL;
1579+
}
1580+
}
15001581
}
15011582

15021583
free(rev);
@@ -1639,6 +1720,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
16391720
char *branch_name = NULL;
16401721
char *base_commit = NULL;
16411722
struct base_tree_info bases;
1723+
struct commit *base;
16421724
int show_progress = 0;
16431725
struct progress *progress = NULL;
16441726
struct oid_array idiff_prev = OID_ARRAY_INIT;
@@ -1715,8 +1797,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
17151797
PARSE_OPT_OPTARG, thread_callback),
17161798
OPT_STRING(0, "signature", &signature, N_("signature"),
17171799
N_("add a signature")),
1718-
OPT_STRING(0, "base", &base_commit, N_("base-commit"),
1719-
N_("add prerequisite tree info to the patch series")),
1800+
OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"),
1801+
N_("add prerequisite tree info to the patch series"),
1802+
0, base_callback),
17201803
OPT_FILENAME(0, "signature-file", &signature_file,
17211804
N_("add a signature from a file")),
17221805
OPT__QUIET(&quiet, N_("don't print the patch filenames")),
@@ -1753,9 +1836,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
17531836
s_r_opt.def = "HEAD";
17541837
s_r_opt.revarg_opt = REVARG_COMMITTISH;
17551838

1756-
if (base_auto)
1757-
base_commit = "auto";
1758-
17591839
if (default_attach) {
17601840
rev.mime_boundary = default_attach;
17611841
rev.no_inline = 1;
@@ -2019,8 +2099,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
20192099
}
20202100

20212101
memset(&bases, 0, sizeof(bases));
2022-
if (base_commit) {
2023-
struct commit *base = get_base_commit(base_commit, list, nr);
2102+
base = get_base_commit(base_commit, list, nr);
2103+
if (base) {
20242104
reset_revision_walk();
20252105
clear_object_flags(UNINTERESTING);
20262106
prepare_bases(&bases, base, list, nr);

t/t4014-format-patch.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,6 +2037,12 @@ test_expect_success 'format-patch errors out when history involves criss-cross'
20372037
test_must_fail git format-patch --base=auto -1
20382038
'
20392039

2040+
test_expect_success 'format-patch format.useAutoBase whenAble history involves criss-cross' '
2041+
test_config format.useAutoBase whenAble &&
2042+
git format-patch -1 >patch &&
2043+
! grep "^base-commit:" patch
2044+
'
2045+
20402046
test_expect_success 'format-patch format.useAutoBase option' '
20412047
git checkout local &&
20422048
test_config format.useAutoBase true &&
@@ -2047,6 +2053,16 @@ test_expect_success 'format-patch format.useAutoBase option' '
20472053
test_cmp expect actual
20482054
'
20492055

2056+
test_expect_success 'format-patch format.useAutoBase option with whenAble' '
2057+
git checkout local &&
2058+
test_config format.useAutoBase whenAble &&
2059+
git format-patch --stdout -1 >patch &&
2060+
grep "^base-commit:" patch >actual &&
2061+
git rev-parse upstream >commit-id-base &&
2062+
echo "base-commit: $(cat commit-id-base)" >expect &&
2063+
test_cmp expect actual
2064+
'
2065+
20502066
test_expect_success 'format-patch --base overrides format.useAutoBase' '
20512067
test_config format.useAutoBase true &&
20522068
git format-patch --stdout --base=HEAD~1 -1 >patch &&
@@ -2062,6 +2078,12 @@ test_expect_success 'format-patch --no-base overrides format.useAutoBase' '
20622078
! grep "^base-commit:" patch
20632079
'
20642080

2081+
test_expect_success 'format-patch --no-base overrides format.useAutoBase whenAble' '
2082+
test_config format.useAutoBase whenAble &&
2083+
git format-patch --stdout --no-base -1 >patch &&
2084+
! grep "^base-commit:" patch
2085+
'
2086+
20652087
test_expect_success 'format-patch --base with --attach' '
20662088
git format-patch --attach=mimemime --stdout --base=HEAD~ -1 >patch &&
20672089
sed -n -e "/^base-commit:/s/.*/1/p" -e "/^---*mimemime--$/s/.*/2/p" \

0 commit comments

Comments
 (0)