Skip to content

Commit 9ca488c

Browse files
committed
Merge branch 'nd/rebase-show-current-patch'
The new "--show-current-patch" option gives an end-user facing way to get the diff being applied when "git rebase" (and "git am") stops with a conflict. * nd/rebase-show-current-patch: rebase: introduce and use pseudo-ref REBASE_HEAD rebase: add --show-current-patch am: add --show-current-patch
2 parents 2cd91ec + fbd7a23 commit 9ca488c

File tree

12 files changed

+130
-11
lines changed

12 files changed

+130
-11
lines changed

Documentation/git-am.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ SYNOPSIS
1616
[--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
1717
[--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
1818
[(<mbox> | <Maildir>)...]
19-
'git am' (--continue | --skip | --abort | --quit)
19+
'git am' (--continue | --skip | --abort | --quit | --show-current-patch)
2020

2121
DESCRIPTION
2222
-----------
@@ -171,6 +171,10 @@ default. You can use `--no-utf8` to override this.
171171
Abort the patching operation but keep HEAD and the index
172172
untouched.
173173

174+
--show-current-patch::
175+
Show the patch being applied when "git am" is stopped because
176+
of conflicts.
177+
174178
DISCUSSION
175179
----------
176180

Documentation/git-rebase.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ SYNOPSIS
1212
[<upstream> [<branch>]]
1313
'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
1414
--root [<branch>]
15-
'git rebase' --continue | --skip | --abort | --quit | --edit-todo
15+
'git rebase' --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
1616

1717
DESCRIPTION
1818
-----------
@@ -255,6 +255,11 @@ leave out at most one of A and B, in which case it defaults to HEAD.
255255
--edit-todo::
256256
Edit the todo list during an interactive rebase.
257257

258+
--show-current-patch::
259+
Show the current patch in an interactive rebase or when rebase
260+
is stopped because of conflicts. This is the equivalent of
261+
`git show REBASE_HEAD`.
262+
258263
-m::
259264
--merge::
260265
Use merging strategies to rebase. When the recursive (default) merge

builtin/am.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
10111011

10121012
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
10131013
die_errno(_("failed to create directory '%s'"), state->dir);
1014+
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
10141015

10151016
if (split_mail(state, patch_format, paths, keep_cr) < 0) {
10161017
am_destroy(state);
@@ -1110,6 +1111,7 @@ static void am_next(struct am_state *state)
11101111

11111112
oidclr(&state->orig_commit);
11121113
unlink(am_path(state, "original-commit"));
1114+
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
11131115

11141116
if (!get_oid("HEAD", &head))
11151117
write_state_text(state, "abort-safety", oid_to_hex(&head));
@@ -1441,6 +1443,8 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
14411443

14421444
oidcpy(&state->orig_commit, &commit_oid);
14431445
write_state_text(state, "original-commit", oid_to_hex(&commit_oid));
1446+
update_ref("am", "REBASE_HEAD", &commit_oid,
1447+
NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
14441448

14451449
return 0;
14461450
}
@@ -1831,8 +1835,7 @@ static void am_run(struct am_state *state, int resume)
18311835
git_config_get_bool("advice.amworkdir", &advice_amworkdir);
18321836

18331837
if (advice_amworkdir)
1834-
printf_ln(_("The copy of the patch that failed is found in: %s"),
1835-
am_path(state, "patch"));
1838+
printf_ln(_("Use 'git am --show-current-patch' to see the failed patch"));
18361839

18371840
die_user_resolve(state);
18381841
}
@@ -2121,6 +2124,34 @@ static void am_abort(struct am_state *state)
21212124
am_destroy(state);
21222125
}
21232126

2127+
static int show_patch(struct am_state *state)
2128+
{
2129+
struct strbuf sb = STRBUF_INIT;
2130+
const char *patch_path;
2131+
int len;
2132+
2133+
if (!is_null_oid(&state->orig_commit)) {
2134+
const char *av[4] = { "show", NULL, "--", NULL };
2135+
char *new_oid_str;
2136+
int ret;
2137+
2138+
av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit));
2139+
ret = run_command_v_opt(av, RUN_GIT_CMD);
2140+
free(new_oid_str);
2141+
return ret;
2142+
}
2143+
2144+
patch_path = am_path(state, msgnum(state));
2145+
len = strbuf_read_file(&sb, patch_path, 0);
2146+
if (len < 0)
2147+
die_errno(_("failed to read '%s'"), patch_path);
2148+
2149+
setup_pager();
2150+
write_in_full(1, sb.buf, sb.len);
2151+
strbuf_release(&sb);
2152+
return 0;
2153+
}
2154+
21242155
/**
21252156
* parse_options() callback that validates and sets opt->value to the
21262157
* PATCH_FORMAT_* enum value corresponding to `arg`.
@@ -2150,7 +2181,8 @@ enum resume_mode {
21502181
RESUME_RESOLVED,
21512182
RESUME_SKIP,
21522183
RESUME_ABORT,
2153-
RESUME_QUIT
2184+
RESUME_QUIT,
2185+
RESUME_SHOW_PATCH
21542186
};
21552187

21562188
static int git_am_config(const char *k, const char *v, void *cb)
@@ -2172,6 +2204,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
21722204
int patch_format = PATCH_FORMAT_UNKNOWN;
21732205
enum resume_mode resume = RESUME_FALSE;
21742206
int in_progress;
2207+
int ret = 0;
21752208

21762209
const char * const usage[] = {
21772210
N_("git am [<options>] [(<mbox> | <Maildir>)...]"),
@@ -2253,6 +2286,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
22532286
OPT_CMDMODE(0, "quit", &resume,
22542287
N_("abort the patching operation but keep HEAD where it is."),
22552288
RESUME_QUIT),
2289+
OPT_CMDMODE(0, "show-current-patch", &resume,
2290+
N_("show the patch being applied."),
2291+
RESUME_SHOW_PATCH),
22562292
OPT_BOOL(0, "committer-date-is-author-date",
22572293
&state.committer_date_is_author_date,
22582294
N_("lie about committer date")),
@@ -2367,11 +2403,14 @@ int cmd_am(int argc, const char **argv, const char *prefix)
23672403
am_rerere_clear();
23682404
am_destroy(&state);
23692405
break;
2406+
case RESUME_SHOW_PATCH:
2407+
ret = show_patch(&state);
2408+
break;
23702409
default:
23712410
die("BUG: invalid resume value");
23722411
}
23732412

23742413
am_state_release(&state);
23752414

2376-
return 0;
2415+
return ret;
23772416
}

contrib/completion/git-completion.bash

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ __git_refs ()
439439
track=""
440440
;;
441441
*)
442-
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
442+
for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do
443443
case "$i" in
444444
$match*)
445445
if [ -e "$dir/$i" ]; then
@@ -1077,7 +1077,7 @@ _git_am ()
10771077
{
10781078
__git_find_repo_path
10791079
if [ -d "$__git_repo_path"/rebase-apply ]; then
1080-
__gitcomp "--skip --continue --resolved --abort --quit"
1080+
__gitcomp "--skip --continue --resolved --abort --quit --show-current-patch"
10811081
return
10821082
fi
10831083
case "$cur" in
@@ -1992,11 +1992,11 @@ _git_rebase ()
19921992
{
19931993
__git_find_repo_path
19941994
if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
1995-
__gitcomp "--continue --skip --abort --quit --edit-todo"
1995+
__gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch"
19961996
return
19971997
elif [ -d "$__git_repo_path"/rebase-apply ] || \
19981998
[ -d "$__git_repo_path"/rebase-merge ]; then
1999-
__gitcomp "--continue --skip --abort --quit"
1999+
__gitcomp "--continue --skip --abort --quit --show-current-patch"
20002000
return
20012001
fi
20022002
__git_complete_strategy && return

git-rebase--am.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ skip)
2727
move_to_original_branch
2828
return
2929
;;
30+
show-current-patch)
31+
exec git am --show-current-patch
32+
;;
3033
esac
3134

3235
if test -z "$rebase_root"

git-rebase--interactive.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,14 @@ make_patch () {
199199

200200
die_with_patch () {
201201
echo "$1" > "$state_dir"/stopped-sha
202+
git update-ref REBASE_HEAD "$1"
202203
make_patch "$1"
203204
die "$2"
204205
}
205206

206207
exit_with_patch () {
207208
echo "$1" > "$state_dir"/stopped-sha
209+
git update-ref REBASE_HEAD "$1"
208210
make_patch $1
209211
git rev-parse --verify HEAD > "$amend"
210212
gpg_sign_opt_quoted=${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")}
@@ -844,6 +846,9 @@ To continue rebase after editing, run:
844846

845847
exit
846848
;;
849+
show-current-patch)
850+
exec git show REBASE_HEAD --
851+
;;
847852
esac
848853

849854
comment_for_reflog start
@@ -859,6 +864,7 @@ fi
859864

860865
orig_head=$(git rev-parse --verify HEAD) || die "$(gettext "No HEAD?")"
861866
mkdir -p "$state_dir" || die "$(eval_gettext "Could not create temporary \$state_dir")"
867+
rm -f "$(git rev-parse --git-path REBASE_HEAD)"
862868

863869
: > "$state_dir"/interactive || die "$(gettext "Could not mark as interactive")"
864870
write_basic_state

git-rebase--merge.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ call_merge () {
5858
echo "$msgnum" >"$state_dir/msgnum"
5959
cmt="$(cat "$state_dir/cmt.$msgnum")"
6060
echo "$cmt" > "$state_dir/current"
61+
git update-ref REBASE_HEAD "$cmt"
6162
hd=$(git rev-parse --verify HEAD)
6263
cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
6364
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
@@ -138,11 +139,15 @@ skip)
138139
finish_rb_merge
139140
return
140141
;;
142+
show-current-patch)
143+
exec git show REBASE_HEAD --
144+
;;
141145
esac
142146

143147
mkdir -p "$state_dir"
144148
echo "$onto_name" > "$state_dir/onto_name"
145149
write_basic_state
150+
rm -f "$(git rev-parse --git-path REBASE_HEAD)"
146151

147152
msgnum=0
148153
for cmt in $(git rev-list --reverse --no-merges "$revisions")

git-rebase.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ abort! abort and check out the original branch
4646
skip! skip current patch and continue
4747
edit-todo! edit the todo list during an interactive rebase
4848
quit! abort but keep HEAD where it is
49+
show-current-patch! show the patch file being applied or merged
4950
"
5051
. git-sh-setup
5152
set_reflog_action rebase
@@ -183,6 +184,7 @@ You can run "git stash pop" or "git stash drop" at any time.
183184
}
184185

185186
finish_rebase () {
187+
rm -f "$(git rev-parse --git-path REBASE_HEAD)"
186188
apply_autostash &&
187189
{ git gc --auto || true; } &&
188190
rm -rf "$state_dir"
@@ -247,7 +249,7 @@ do
247249
--verify)
248250
ok_to_skip_pre_rebase=
249251
;;
250-
--continue|--skip|--abort|--quit|--edit-todo)
252+
--continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
251253
test $total_argc -eq 2 || usage
252254
action=${1##--}
253255
;;
@@ -417,6 +419,10 @@ quit)
417419
edit-todo)
418420
run_specific_rebase
419421
;;
422+
show-current-patch)
423+
run_specific_rebase
424+
die "BUG: run_specific_rebase is not supposed to return here"
425+
;;
420426
esac
421427

422428
# Make sure no rebase is in progress

sequencer.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,6 +2314,9 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
23142314
p = short_commit_name(commit);
23152315
if (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)
23162316
return -1;
2317+
if (update_ref("rebase", "REBASE_HEAD", &commit->object.oid,
2318+
NULL, REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR))
2319+
res |= error(_("could not update %s"), "REBASE_HEAD");
23172320

23182321
strbuf_addf(&buf, "%s/patch", get_dir(opts));
23192322
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
@@ -2565,6 +2568,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
25652568
unlink(rebase_path_author_script());
25662569
unlink(rebase_path_stopped_sha());
25672570
unlink(rebase_path_amend());
2571+
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
25682572
}
25692573
if (item->command <= TODO_SQUASH) {
25702574
if (is_rebase_i(opts))

t/t3400-rebase.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,38 @@ EOF
277277
test_cmp From_.msg out
278278
'
279279

280+
test_expect_success 'rebase--am.sh and --show-current-patch' '
281+
test_create_repo conflict-apply &&
282+
(
283+
cd conflict-apply &&
284+
test_commit init &&
285+
echo one >>init.t &&
286+
git commit -a -m one &&
287+
echo two >>init.t &&
288+
git commit -a -m two &&
289+
git tag two &&
290+
test_must_fail git rebase --onto init HEAD^ &&
291+
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
292+
grep "show.*$(git rev-parse two)" stderr
293+
)
294+
'
295+
296+
test_expect_success 'rebase--merge.sh and --show-current-patch' '
297+
test_create_repo conflict-merge &&
298+
(
299+
cd conflict-merge &&
300+
test_commit init &&
301+
echo one >>init.t &&
302+
git commit -a -m one &&
303+
echo two >>init.t &&
304+
git commit -a -m two &&
305+
git tag two &&
306+
test_must_fail git rebase --merge --onto init HEAD^ &&
307+
git rebase --show-current-patch >actual.patch &&
308+
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
309+
grep "show.*REBASE_HEAD" stderr &&
310+
test "$(git rev-parse REBASE_HEAD)" = "$(git rev-parse two)"
311+
)
312+
'
313+
280314
test_done

0 commit comments

Comments
 (0)