update-ref and tag: add --create-reflog arg

Allow the creation of a ref (e.g. stash) with a reflog already in place. For most refs (e.g. those under refs/heads), this happens automatically, but for others, we need this option. Currently, git does this by pre-creating the reflog, but alternate ref backends might store reflogs somewhere other than .git/logs. Code that now directly manipulates .git/logs should instead use git plumbing commands. I also added --create-reflog to git tag, just for completeness. In a moment, we will use this argument to make git stash work with alternate ref backends. Signed-off-by: David Turner <dturner@twopensource.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

David Turner committed Jul 21, 2015 at 17:04 UTC 144c76fa39254903cb1e8e2d6d159603077356a7
6 files changed +74 -7
Documentation/git-tag.txt
+4 -1
@@ -13,7 +13,7 @@ SYNOPSIS
13 <tagname> [<commit> | <object>]
14 'git tag' -d <tagname>...
15 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
16 - [--column[=<options>] | --no-column] [<pattern>...]
16 + [--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
17 [<pattern>...]
18 'git tag' -v <tagname>...
19
@@ -143,6 +143,9 @@ This option is only applicable when listing tags without annotation lines.
143 all, 'whitespace' removes just leading/trailing whitespace lines and
144 'strip' removes both whitespace and commentary.
145
146 +--create-reflog::
147 + Create a reflog for the tag.
148 +
149 <tagname>::
150 The name of the tag to create, delete, or describe.
151 The new tag name must pass all checks defined by
Documentation/git-update-ref.txt
+4 -1
@@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
8 SYNOPSIS
9 --------
10 [verse]
11 -'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
11 +'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])
12
13 DESCRIPTION
14 -----------
@@ -67,6 +67,9 @@ performs all modifications together. Specify commands of the form:
67 verify SP <ref> [SP <oldvalue>] LF
68 option SP <opt> LF
69
70 +With `--create-reflog`, update-ref will create a reflog for each ref
71 +even if one would not ordinarily be created.
72 +
73 Quote fields containing whitespace as if they were strings in C source
74 code; i.e., surrounded by double-quotes and with backslash escapes.
75 Use 40 "0" characters or the empty string to specify a zero value. To
builtin/tag.c
+4 -1
@@ -579,6 +579,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
579 struct create_tag_options opt;
580 char *cleanup_arg = NULL;
581 int annotate = 0, force = 0, lines = -1;
582 + int create_reflog = 0;
583 int cmdmode = 0;
584 const char *msgfile = NULL, *keyid = NULL;
585 struct msg_arg msg = { 0, STRBUF_INIT };
@@ -605,6 +606,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
606 OPT_STRING('u', "local-user", &keyid, N_("key-id"),
607 N_("use another key to sign the tag")),
608 OPT__FORCE(&force, N_("replace the tag if exists")),
609 + OPT_BOOL(0, "create-reflog", &create_reflog, N_("create_reflog")),
610
611 OPT_GROUP(N_("Tag listing options")),
612 OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
@@ -733,7 +735,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
735 transaction = ref_transaction_begin(&err);
736 if (!transaction ||
737 ref_transaction_update(transaction, ref.buf, object, prev,
736 - 0, NULL, &err) ||
738 + create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
739 + NULL, &err) ||
740 ref_transaction_commit(transaction, &err))
741 die("%s", err.buf);
742 ref_transaction_free(transaction);
builtin/update-ref.c
+11 -3
@@ -14,6 +14,7 @@ static const char * const git_update_ref_usage[] = {
14
15 static char line_termination = '\n';
16 static int update_flags;
17 +static unsigned create_reflog_flag;
18 static const char *msg;
19
20 /*
@@ -200,7 +201,8 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,
201
202 if (ref_transaction_update(transaction, refname,
203 new_sha1, have_old ? old_sha1 : NULL,
203 - update_flags, msg, &err))
204 + update_flags | create_reflog_flag,
205 + msg, &err))
206 die("%s", err.buf);
207
208 update_flags = 0;
@@ -231,7 +233,8 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
233 die("create %s: extra input: %s", refname, next);
234
235 if (ref_transaction_create(transaction, refname, new_sha1,
234 - update_flags, msg, &err))
236 + update_flags | create_reflog_flag,
237 + msg, &err))
238 die("%s", err.buf);
239
240 update_flags = 0;
@@ -354,6 +357,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
357 unsigned char sha1[20], oldsha1[20];
358 int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
359 unsigned int flags = 0;
360 + int create_reflog = 0;
361 struct option options[] = {
362 OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
363 OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
@@ -361,6 +365,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
365 N_("update <refname> not the one it points to")),
366 OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
367 OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
368 + OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create_reflog")),
369 OPT_END(),
370 };
371
@@ -370,6 +375,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
375 if (msg && !*msg)
376 die("Refusing to perform update with empty message.");
377
378 + create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0;
379 +
380 if (read_stdin) {
381 struct strbuf err = STRBUF_INIT;
382 struct ref_transaction *transaction;
@@ -418,5 +425,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
425 return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
426 else
427 return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
421 - flags, UPDATE_REFS_DIE_ON_ERR);
428 + flags | create_reflog_flag,
429 + UPDATE_REFS_DIE_ON_ERR);
430 }
t/t1400-update-ref.sh
+38
@@ -23,6 +23,7 @@ test_expect_success setup '
23 m=refs/heads/master
24 n_dir=refs/heads/gu
25 n=$n_dir/fixes
26 +outside=foo
27
28 test_expect_success \
29 "create $m" \
@@ -74,6 +75,24 @@ test_expect_success "delete $m (by HEAD)" '
75 '
76 rm -f .git/$m
77
78 +test_expect_success 'update-ref does not create reflogs by default' '
79 + test_when_finished "git update-ref -d $outside" &&
80 + git update-ref $outside $A &&
81 + git rev-parse $A >expect &&
82 + git rev-parse $outside >actual &&
83 + test_cmp expect actual &&
84 + test_must_fail git reflog exists $outside
85 +'
86 +
87 +test_expect_success 'update-ref creates reflogs with --create-reflog' '
88 + test_when_finished "git update-ref -d $outside" &&
89 + git update-ref --create-reflog $outside $A &&
90 + git rev-parse $A >expect &&
91 + git rev-parse $outside >actual &&
92 + test_cmp expect actual &&
93 + git reflog exists $outside
94 +'
95 +
96 test_expect_success \
97 "create $m (by HEAD)" \
98 "git update-ref HEAD $A &&
@@ -472,6 +491,25 @@ test_expect_success 'stdin create ref works' '
491 test_cmp expect actual
492 '
493
494 +test_expect_success 'stdin does not create reflogs by default' '
495 + test_when_finished "git update-ref -d $outside" &&
496 + echo "create $outside $m" >stdin &&
497 + git update-ref --stdin <stdin &&
498 + git rev-parse $m >expect &&
499 + git rev-parse $outside >actual &&
500 + test_cmp expect actual &&
501 + test_must_fail git reflog exists $outside
502 +'
503 +
504 +test_expect_success 'stdin creates reflogs with --create-reflog' '
505 + echo "create $outside $m" >stdin &&
506 + git update-ref --create-reflog --stdin <stdin &&
507 + git rev-parse $m >expect &&
508 + git rev-parse $outside >actual &&
509 + test_cmp expect actual &&
510 + git reflog exists $outside
511 +'
512 +
513 test_expect_success 'stdin succeeds with quoted argument' '
514 git update-ref -d $a &&
515 echo "create $a \"$m\"" >stdin &&
t/t7004-tag.sh
+13 -1
@@ -51,7 +51,19 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
51 echo foo >foo &&
52 git add foo &&
53 git commit -m Foo &&
54 - git tag mytag
54 + git tag mytag &&
55 + test_must_fail git reflog exists refs/tags/mytag
56 +'
57 +
58 +test_expect_success 'creating a tag with --create-reflog should create reflog' '
59 + test_when_finished "git tag -d tag_with_reflog" &&
60 + git tag --create-reflog tag_with_reflog &&
61 + git reflog exists refs/tags/tag_with_reflog
62 +'
63 +
64 +test_expect_success '--create-reflog does not create reflog on failure' '
65 + test_must_fail git tag --create-reflog mytag &&
66 + test_must_fail git reflog exists refs/tags/mytag
67 '
68
69 test_expect_success 'listing all tags if one exists should succeed' '