revert: write REVERT_HEAD pseudoref during conflicted revert

When conflicts are encountered while reverting a commit, it can be handy to have the name of that commit easily available. For example, to produce a copy of the patch to refer to while resolving conflicts: $ git revert 2eceb2a8 error: could not revert 2eceb2a8... awesome, buggy feature $ git show -R REVERT_HEAD >the-patch $ edit $(git diff --name-only) Set a REVERT_HEAD pseudoref when "git revert" does not make a commit, for cases like this. This also makes it possible for scripts to distinguish between a revert that encountered conflicts and other sources of an unmerged index. After successfully committing, resetting with "git reset", or moving to another commit with "git checkout" or "git reset", the pseudoref is no longer useful, so remove it. Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Nieder committed Nov 22, 2011 at 05:17 UTC 82433cdf4d888639cecddeca162d619cf370417e
4 files changed +61 -3
branch.c
+1
@@ -241,6 +241,7 @@ void create_branch(const char *head,
241 void remove_branch_state(void)
242 {
243 unlink(git_path("CHERRY_PICK_HEAD"));
244 + unlink(git_path("REVERT_HEAD"));
245 unlink(git_path("MERGE_HEAD"));
246 unlink(git_path("MERGE_RR"));
247 unlink(git_path("MERGE_MSG"));
builtin/commit.c
+1
@@ -1514,6 +1514,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
1514 }
1515
1516 unlink(git_path("CHERRY_PICK_HEAD"));
1517 + unlink(git_path("REVERT_HEAD"));
1518 unlink(git_path("MERGE_HEAD"));
1519 unlink(git_path("MERGE_MSG"));
1520 unlink(git_path("MERGE_MODE"));
builtin/revert.c
+5 -3
@@ -289,7 +289,7 @@ static char *get_encoding(const char *message)
289 return NULL;
290 }
291
292 -static void write_cherry_pick_head(struct commit *commit)
292 +static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
293 {
294 const char *filename;
295 int fd;
@@ -297,7 +297,7 @@ static void write_cherry_pick_head(struct commit *commit)
297
298 strbuf_addf(&buf, "%s\n", sha1_to_hex(commit->object.sha1));
299
300 - filename = git_path("CHERRY_PICK_HEAD");
300 + filename = git_path(pseudoref);
301 fd = open(filename, O_WRONLY | O_CREAT, 0666);
302 if (fd < 0)
303 die_errno(_("Could not open '%s' for writing"), filename);
@@ -597,7 +597,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
597 * write it at all.
598 */
599 if (opts->action == CHERRY_PICK && !opts->no_commit && (res == 0 || res == 1))
600 - write_cherry_pick_head(commit);
600 + write_cherry_pick_head(commit, "CHERRY_PICK_HEAD");
601 + if (opts->action == REVERT && ((opts->no_commit && res == 0) || res == 1))
602 + write_cherry_pick_head(commit, "REVERT_HEAD");
603
604 if (res) {
605 error(opts->action == REVERT
t/t3507-cherry-pick-conflict.sh
+54
@@ -253,6 +253,60 @@ test_expect_success 'revert also handles conflicts sanely' '
253 test_cmp expected actual
254 '
255
256 +test_expect_success 'failed revert sets REVERT_HEAD' '
257 + pristine_detach initial &&
258 + test_must_fail git revert picked &&
259 + test_cmp_rev picked REVERT_HEAD
260 +'
261 +
262 +test_expect_success 'successful revert does not set REVERT_HEAD' '
263 + pristine_detach base &&
264 + git revert base &&
265 + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
266 + test_must_fail git rev-parse --verify REVERT_HEAD
267 +'
268 +
269 +test_expect_success 'revert --no-commit sets REVERT_HEAD' '
270 + pristine_detach base &&
271 + git revert --no-commit base &&
272 + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
273 + test_cmp_rev base REVERT_HEAD
274 +'
275 +
276 +test_expect_success 'revert w/dirty tree does not set REVERT_HEAD' '
277 + pristine_detach base &&
278 + echo foo > foo &&
279 + test_must_fail git revert base &&
280 + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
281 + test_must_fail git rev-parse --verify REVERT_HEAD
282 +'
283 +
284 +test_expect_success 'GIT_CHERRY_PICK_HELP does not suppress REVERT_HEAD' '
285 + pristine_detach initial &&
286 + (
287 + GIT_CHERRY_PICK_HELP="and then do something else" &&
288 + GIT_REVERT_HELP="and then do something else, again" &&
289 + export GIT_CHERRY_PICK_HELP GIT_REVERT_HELP &&
290 + test_must_fail git revert picked
291 + ) &&
292 + test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
293 + test_cmp_rev picked REVERT_HEAD
294 +'
295 +
296 +test_expect_success 'git reset clears REVERT_HEAD' '
297 + pristine_detach initial &&
298 + test_must_fail git revert picked &&
299 + git reset &&
300 + test_must_fail git rev-parse --verify REVERT_HEAD
301 +'
302 +
303 +test_expect_success 'failed commit does not clear REVERT_HEAD' '
304 + pristine_detach initial &&
305 + test_must_fail git revert picked &&
306 + test_must_fail git commit &&
307 + test_cmp_rev picked REVERT_HEAD
308 +'
309 +
310 test_expect_success 'revert conflict, diff3 -m style' '
311 pristine_detach initial &&
312 git config merge.conflictstyle diff3 &&