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>
maint
David Turner 2015-07-21 17:04:55 -04:00 committed by Junio C Hamano
parent 0f2a71d992
commit 144c76fa39
6 changed files with 74 additions and 7 deletions

View File

@ -13,7 +13,7 @@ SYNOPSIS
<tagname> [<commit> | <object>] <tagname> [<commit> | <object>]
'git tag' -d <tagname>... 'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>] 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
[--column[=<options>] | --no-column] [<pattern>...] [--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
[<pattern>...] [<pattern>...]
'git tag' -v <tagname>... 'git tag' -v <tagname>...


@ -143,6 +143,9 @@ This option is only applicable when listing tags without annotation lines.
all, 'whitespace' removes just leading/trailing whitespace lines and all, 'whitespace' removes just leading/trailing whitespace lines and
'strip' removes both whitespace and commentary. 'strip' removes both whitespace and commentary.


--create-reflog::
Create a reflog for the tag.

<tagname>:: <tagname>::
The name of the tag to create, delete, or describe. The name of the tag to create, delete, or describe.
The new tag name must pass all checks defined by The new tag name must pass all checks defined by

View File

@ -8,7 +8,7 @@ git-update-ref - Update the object name stored in a ref safely
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] <ref> <newvalue> [<oldvalue>] | --stdin [-z]) 'git update-ref' [-m <reason>] (-d <ref> [<oldvalue>] | [--no-deref] [--create-reflog] <ref> <newvalue> [<oldvalue>] | --stdin [-z])


DESCRIPTION DESCRIPTION
----------- -----------
@ -67,6 +67,9 @@ performs all modifications together. Specify commands of the form:
verify SP <ref> [SP <oldvalue>] LF verify SP <ref> [SP <oldvalue>] LF
option SP <opt> LF option SP <opt> LF


With `--create-reflog`, update-ref will create a reflog for each ref
even if one would not ordinarily be created.

Quote fields containing whitespace as if they were strings in C source Quote fields containing whitespace as if they were strings in C source
code; i.e., surrounded by double-quotes and with backslash escapes. code; i.e., surrounded by double-quotes and with backslash escapes.
Use 40 "0" characters or the empty string to specify a zero value. To Use 40 "0" characters or the empty string to specify a zero value. To

View File

@ -579,6 +579,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct create_tag_options opt; struct create_tag_options opt;
char *cleanup_arg = NULL; char *cleanup_arg = NULL;
int annotate = 0, force = 0, lines = -1; int annotate = 0, force = 0, lines = -1;
int create_reflog = 0;
int cmdmode = 0; int cmdmode = 0;
const char *msgfile = NULL, *keyid = NULL; const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT }; struct msg_arg msg = { 0, STRBUF_INIT };
@ -605,6 +606,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_STRING('u', "local-user", &keyid, N_("key-id"), OPT_STRING('u', "local-user", &keyid, N_("key-id"),
N_("use another key to sign the tag")), N_("use another key to sign the tag")),
OPT__FORCE(&force, N_("replace the tag if exists")), OPT__FORCE(&force, N_("replace the tag if exists")),
OPT_BOOL(0, "create-reflog", &create_reflog, N_("create_reflog")),


OPT_GROUP(N_("Tag listing options")), OPT_GROUP(N_("Tag listing options")),
OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), 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)
transaction = ref_transaction_begin(&err); transaction = ref_transaction_begin(&err);
if (!transaction || if (!transaction ||
ref_transaction_update(transaction, ref.buf, object, prev, ref_transaction_update(transaction, ref.buf, object, prev,
0, NULL, &err) || create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
NULL, &err) ||
ref_transaction_commit(transaction, &err)) ref_transaction_commit(transaction, &err))
die("%s", err.buf); die("%s", err.buf);
ref_transaction_free(transaction); ref_transaction_free(transaction);

View File

@ -14,6 +14,7 @@ static const char * const git_update_ref_usage[] = {


static char line_termination = '\n'; static char line_termination = '\n';
static int update_flags; static int update_flags;
static unsigned create_reflog_flag;
static const char *msg; static const char *msg;


/* /*
@ -200,7 +201,8 @@ static const char *parse_cmd_update(struct ref_transaction *transaction,


if (ref_transaction_update(transaction, refname, if (ref_transaction_update(transaction, refname,
new_sha1, have_old ? old_sha1 : NULL, new_sha1, have_old ? old_sha1 : NULL,
update_flags, msg, &err)) update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf); die("%s", err.buf);


update_flags = 0; update_flags = 0;
@ -231,7 +233,8 @@ static const char *parse_cmd_create(struct ref_transaction *transaction,
die("create %s: extra input: %s", refname, next); die("create %s: extra input: %s", refname, next);


if (ref_transaction_create(transaction, refname, new_sha1, if (ref_transaction_create(transaction, refname, new_sha1,
update_flags, msg, &err)) update_flags | create_reflog_flag,
msg, &err))
die("%s", err.buf); die("%s", err.buf);


update_flags = 0; update_flags = 0;
@ -354,6 +357,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
unsigned char sha1[20], oldsha1[20]; unsigned char sha1[20], oldsha1[20];
int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0; int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0;
unsigned int flags = 0; unsigned int flags = 0;
int create_reflog = 0;
struct option options[] = { struct option options[] = {
OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
OPT_BOOL('d', NULL, &delete, N_("delete the reference")), 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)
N_("update <refname> not the one it points to")), N_("update <refname> not the one it points to")),
OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")), OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")), OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create_reflog")),
OPT_END(), OPT_END(),
}; };


@ -370,6 +375,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (msg && !*msg) if (msg && !*msg)
die("Refusing to perform update with empty message."); die("Refusing to perform update with empty message.");


create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0;

if (read_stdin) { if (read_stdin) {
struct strbuf err = STRBUF_INIT; struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction; struct ref_transaction *transaction;
@ -418,5 +425,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
return delete_ref(refname, oldval ? oldsha1 : NULL, flags); return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
else else
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
flags, UPDATE_REFS_DIE_ON_ERR); flags | create_reflog_flag,
UPDATE_REFS_DIE_ON_ERR);
} }

View File

@ -23,6 +23,7 @@ test_expect_success setup '
m=refs/heads/master m=refs/heads/master
n_dir=refs/heads/gu n_dir=refs/heads/gu
n=$n_dir/fixes n=$n_dir/fixes
outside=foo


test_expect_success \ test_expect_success \
"create $m" \ "create $m" \
@ -74,6 +75,24 @@ test_expect_success "delete $m (by HEAD)" '
' '
rm -f .git/$m rm -f .git/$m


test_expect_success 'update-ref does not create reflogs by default' '
test_when_finished "git update-ref -d $outside" &&
git update-ref $outside $A &&
git rev-parse $A >expect &&
git rev-parse $outside >actual &&
test_cmp expect actual &&
test_must_fail git reflog exists $outside
'

test_expect_success 'update-ref creates reflogs with --create-reflog' '
test_when_finished "git update-ref -d $outside" &&
git update-ref --create-reflog $outside $A &&
git rev-parse $A >expect &&
git rev-parse $outside >actual &&
test_cmp expect actual &&
git reflog exists $outside
'

test_expect_success \ test_expect_success \
"create $m (by HEAD)" \ "create $m (by HEAD)" \
"git update-ref HEAD $A && "git update-ref HEAD $A &&
@ -472,6 +491,25 @@ test_expect_success 'stdin create ref works' '
test_cmp expect actual test_cmp expect actual
' '


test_expect_success 'stdin does not create reflogs by default' '
test_when_finished "git update-ref -d $outside" &&
echo "create $outside $m" >stdin &&
git update-ref --stdin <stdin &&
git rev-parse $m >expect &&
git rev-parse $outside >actual &&
test_cmp expect actual &&
test_must_fail git reflog exists $outside
'

test_expect_success 'stdin creates reflogs with --create-reflog' '
echo "create $outside $m" >stdin &&
git update-ref --create-reflog --stdin <stdin &&
git rev-parse $m >expect &&
git rev-parse $outside >actual &&
test_cmp expect actual &&
git reflog exists $outside
'

test_expect_success 'stdin succeeds with quoted argument' ' test_expect_success 'stdin succeeds with quoted argument' '
git update-ref -d $a && git update-ref -d $a &&
echo "create $a \"$m\"" >stdin && echo "create $a \"$m\"" >stdin &&

View File

@ -51,7 +51,19 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
echo foo >foo && echo foo >foo &&
git add foo && git add foo &&
git commit -m Foo && git commit -m Foo &&
git tag mytag git tag mytag &&
test_must_fail git reflog exists refs/tags/mytag
'

test_expect_success 'creating a tag with --create-reflog should create reflog' '
test_when_finished "git tag -d tag_with_reflog" &&
git tag --create-reflog tag_with_reflog &&
git reflog exists refs/tags/tag_with_reflog
'

test_expect_success '--create-reflog does not create reflog on failure' '
test_must_fail git tag --create-reflog mytag &&
test_must_fail git reflog exists refs/tags/mytag
' '


test_expect_success 'listing all tags if one exists should succeed' ' test_expect_success 'listing all tags if one exists should succeed' '