am: support --allow-empty to record specific empty patches
This option helps to record specific empty patches in the middle of an am session, which does create empty commits only when: 1. the index has not changed 2. lacking a branch When the index has changed, "--allow-empty" will create a non-empty commit like passing "--continue" or "--resolved". Signed-off-by: 徐沛文 (Aleen) <aleen42@vip.qq.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
徐沛文 (Aleen) committed
Dec 9, 2021 at 07:25 UTC
9e7e41bf19ed10469f9adb60669b1699c81b8ea6
5 files changed
+95
-10
Documentation/git-am.txt
+6
-1
@@ -18,7 +18,7 @@ SYNOPSIS
18
[--quoted-cr=<action>]
19
[--empty=(stop|drop|keep)]
20
[(<mbox> | <Maildir>)...]
21
-'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)])
21
+'git am' (--continue | --skip | --abort | --quit | --show-current-patch[=(diff|raw)] | --allow-empty)
22
23
DESCRIPTION
24
-----------
@@ -200,6 +200,11 @@ default. You can use `--no-utf8` to override this.
200
the e-mail message; if `diff`, show the diff portion only.
201
Defaults to `raw`.
202
203
+--allow-empty::
204
+ After a patch failure on an input e-mail message lacking a patch,
205
+ create an empty commit with the contents of the e-mail message
206
+ as its log message.
207
+
208
DISCUSSION
209
----------
210
builtin/am.c
+24
-8
@@ -1152,6 +1152,12 @@ static void NORETURN die_user_resolve(const struct am_state *state)
1152
1153
printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline);
1154
printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline);
1155
+
1156
+ if (advice_enabled(ADVICE_AM_WORK_DIR) &&
1157
+ is_empty_or_missing_file(am_path(state, "patch")) &&
1158
+ !repo_index_has_changes(the_repository, NULL, NULL))
1159
+ printf_ln(_("To record the empty patch as an empty commit, run \"%s --allow-empty\"."), cmdline);
1160
+
1161
printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline);
1162
}
1163
@@ -1900,19 +1906,24 @@ next:
1906
/**
1907
* Resume the current am session after patch application failure. The user did
1908
* all the hard work, and we do not have to do any patch application. Just
1903
- * trust and commit what the user has in the index and working tree.
1909
+ * trust and commit what the user has in the index and working tree. If `allow_empty`
1910
+ * is true, commit as an empty commit when index has not changed and lacking a patch.
1911
*/
1905
-static void am_resolve(struct am_state *state)
1912
+static void am_resolve(struct am_state *state, int allow_empty)
1913
{
1914
validate_resume_state(state);
1915
1916
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
1917
1918
if (!repo_index_has_changes(the_repository, NULL, NULL)) {
1912
- printf_ln(_("No changes - did you forget to use 'git add'?\n"
1913
- "If there is nothing left to stage, chances are that something else\n"
1914
- "already introduced the same changes; you might want to skip this patch."));
1915
- die_user_resolve(state);
1919
+ if (allow_empty && is_empty_or_missing_file(am_path(state, "patch"))) {
1920
+ printf_ln(_("No changes - recorded it as an empty commit."));
1921
+ } else {
1922
+ printf_ln(_("No changes - did you forget to use 'git add'?\n"
1923
+ "If there is nothing left to stage, chances are that something else\n"
1924
+ "already introduced the same changes; you might want to skip this patch."));
1925
+ die_user_resolve(state);
1926
+ }
1927
}
1928
1929
if (unmerged_cache()) {
@@ -2239,7 +2250,8 @@ enum resume_type {
2250
RESUME_SKIP,
2251
RESUME_ABORT,
2252
RESUME_QUIT,
2242
- RESUME_SHOW_PATCH
2253
+ RESUME_SHOW_PATCH,
2254
+ RESUME_ALLOW_EMPTY,
2255
};
2256
2257
struct resume_mode {
@@ -2392,6 +2404,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
2404
N_("show the patch being applied"),
2405
PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
2406
parse_opt_show_current_patch, RESUME_SHOW_PATCH },
2407
+ OPT_CMDMODE(0, "allow-empty", &resume.mode,
2408
+ N_("record the empty patch as an empty commit"),
2409
+ RESUME_ALLOW_EMPTY),
2410
OPT_BOOL(0, "committer-date-is-author-date",
2411
&state.committer_date_is_author_date,
2412
N_("lie about committer date")),
@@ -2500,7 +2515,8 @@ int cmd_am(int argc, const char **argv, const char *prefix)
2515
am_run(&state, 1);
2516
break;
2517
case RESUME_RESOLVED:
2503
- am_resolve(&state);
2518
+ case RESUME_ALLOW_EMPTY:
2519
+ am_resolve(&state, resume.mode == RESUME_ALLOW_EMPTY ? 1 : 0);
2520
break;
2521
case RESUME_SKIP:
2522
am_skip(&state);
t/t4150-am.sh
+57
@@ -1202,4 +1202,61 @@ test_expect_success 'record as an empty commit when meeting e-mail message that
1202
grep "Creating an empty commit: empty commit" output
1203
'
1204
1205
+test_expect_success 'skip an empty patch in the middle of an am session' '
1206
+ git checkout empty-commit^ &&
1207
+ test_must_fail git am empty-commit.patch >err &&
1208
+ grep "Patch is empty." err &&
1209
+ grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
1210
+ git am --skip &&
1211
+ test_path_is_missing .git/rebase-apply &&
1212
+ git rev-parse empty-commit^ >expected &&
1213
+ git rev-parse HEAD >actual &&
1214
+ test_cmp expected actual
1215
+'
1216
+
1217
+test_expect_success 'record an empty patch as an empty commit in the middle of an am session' '
1218
+ git checkout empty-commit^ &&
1219
+ test_must_fail git am empty-commit.patch >err &&
1220
+ grep "Patch is empty." err &&
1221
+ grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
1222
+ git am --allow-empty >output &&
1223
+ grep "No changes - recorded it as an empty commit." output &&
1224
+ test_path_is_missing .git/rebase-apply &&
1225
+ git show empty-commit --format="%B" >expected &&
1226
+ git show HEAD --format="%B" >actual &&
1227
+ grep -f actual expected
1228
+'
1229
+
1230
+test_expect_success 'create an non-empty commit when the index IS changed though "--allow-empty" is given' '
1231
+ git checkout empty-commit^ &&
1232
+ test_must_fail git am empty-commit.patch >err &&
1233
+ : >empty-file &&
1234
+ git add empty-file &&
1235
+ git am --allow-empty &&
1236
+ git show empty-commit --format="%B" >expected &&
1237
+ git show HEAD --format="%B" >actual &&
1238
+ grep -f actual expected &&
1239
+ git diff HEAD^..HEAD --name-only
1240
+'
1241
+
1242
+test_expect_success 'cannot create empty commits when there is a clean index due to merge conflicts' '
1243
+ test_when_finished "git am --abort || :" &&
1244
+ git rev-parse HEAD >expected &&
1245
+ test_must_fail git am seq.patch &&
1246
+ test_must_fail git am --allow-empty >err &&
1247
+ ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
1248
+ git rev-parse HEAD >actual &&
1249
+ test_cmp actual expected
1250
+'
1251
+
1252
+test_expect_success 'cannot create empty commits when there is unmerged index due to merge conflicts' '
1253
+ test_when_finished "git am --abort || :" &&
1254
+ git rev-parse HEAD >expected &&
1255
+ test_must_fail git am -3 seq.patch &&
1256
+ test_must_fail git am --allow-empty >err &&
1257
+ ! grep "To record the empty patch as an empty commit, run \"git am --allow-empty\"." err &&
1258
+ git rev-parse HEAD >actual &&
1259
+ test_cmp actual expected
1260
+'
1261
+
1262
test_done
t/t7512-status-help.sh
+1
@@ -659,6 +659,7 @@ On branch am_empty
659
You are in the middle of an am session.
660
The current patch is empty.
661
(use "git am --skip" to skip this patch)
662
+ (use "git am --allow-empty" to record this patch as an empty commit)
663
(use "git am --abort" to restore the original branch)
664
665
nothing to commit (use -u to show untracked files)
wt-status.c
+7
-1
@@ -1212,17 +1212,23 @@ static void show_merge_in_progress(struct wt_status *s,
1212
static void show_am_in_progress(struct wt_status *s,
1213
const char *color)
1214
{
1215
+ int am_empty_patch;
1216
+
1217
status_printf_ln(s, color,
1218
_("You are in the middle of an am session."));
1219
if (s->state.am_empty_patch)
1220
status_printf_ln(s, color,
1221
_("The current patch is empty."));
1222
if (s->hints) {
1221
- if (!s->state.am_empty_patch)
1223
+ am_empty_patch = s->state.am_empty_patch;
1224
+ if (!am_empty_patch)
1225
status_printf_ln(s, color,
1226
_(" (fix conflicts and then run \"git am --continue\")"));
1227
status_printf_ln(s, color,
1228
_(" (use \"git am --skip\" to skip this patch)"));
1229
+ if (am_empty_patch)
1230
+ status_printf_ln(s, color,
1231
+ _(" (use \"git am --allow-empty\" to record this patch as an empty commit)"));
1232
status_printf_ln(s, color,
1233
_(" (use \"git am --abort\" to restore the original branch)"));
1234
}