From: Cornelius Weig <cornelius.w...@tngtech.com>

When tags are created with `--create-reflog` or with the option
`core.logAllRefUpdates` set to 'always', a reflog is created for them.
So far, the description of reflog entries for tags was empty, making the
reflog hard to understand. For example:
"6e3a7b3 refs/tags/test@{0}:"

Now, a reflog message is generated when creating a tag, following the
pattern "<action>: <description>". Here, action is the command line with
all arguments, or the value of GIT_REFLOG_ACTION if it is set. The
description is the commit subject, if the tag points to a commit. For
example:
"6e3a7b3 refs/tags/test@{0}: tag --create-reflog test: Git 2.12-rc0"
If the tag points to a tree/blob/tag object, the following static
strings are taken as description:

 - "tree object"
 - "blob object"
 - "other tag object"

Signed-off-by: Cornelius Weig <cornelius.w...@tngtech.com>
---

Notes:
    *This patch supersedes the version submitted a few hours earlier.*
    
    Sorry for the messup, but I realized that the pattern for the reflog message
    from my first draft did not comply to standard git behavior.
    
    Please also note my remarks from v1 (repeated here):
    
    While playing around with tag reflogs I also found a bug that was present
    before this patch. It manifests itself when the sha1-ref in the reflog does 
not
    point to a commit object but something else.
    
    For example,
    
     - when the referenced sha1 is a tag object:
        $ git tag --create-reflog -f -m'annotated tag' tag_with_reflog
     - when the referenced sha1 is a blob object:
        $ git tag --create-reflog -f tag_with_reflog HEAD:<filename>
     - when the referenced sha1 is a tree object:
        $ git tag --create-reflog -f tag_with_reflog HEAD^{tree}
    
    In each case, a proper reflog entry is generated, but
        $ git reflog tag_with_reflog
    will sometimes segfault (if it does, it does so consistently), or only show 
the
    first few entries. The tree/blob cases are IMHO not so important, but the
    broken reflog for annotated tags I find quite severe.
    
    I guess it's because the reflog is funneled through the log.c code, where 
every
    reflog-entry is assumed to be a commit object? If this is the case, a fix 
would
    probably be quite involved.

 builtin/tag.c  | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 t/t7004-tag.sh | 16 +++++++++++++++-
 2 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/builtin/tag.c b/builtin/tag.c
index e40c4a9..3d9e105 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -30,6 +30,7 @@ static const char * const git_tag_usage[] = {
 
 static unsigned int colopts;
 static int force_sign_annotate;
+static struct strbuf default_rla = STRBUF_INIT;
 
 static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, 
const char *format)
 {
@@ -302,6 +303,48 @@ static void create_tag(const unsigned char *object, const 
char *tag,
        }
 }
 
+static void create_reflog_msg(const unsigned char *object, struct strbuf *sb)
+{
+       enum object_type type;
+       char *buf;
+       unsigned long size;
+       int subject_len = 0;
+       const char *subject_start;
+
+       char *rla = getenv("GIT_REFLOG_ACTION");
+       if (!rla)
+               rla = default_rla.buf;
+
+       strbuf_addf(sb, "%s: ", rla ? rla : default_rla.buf);
+
+       type = sha1_object_info(object, NULL);
+       switch (type) {
+       default:
+               strbuf_addstr(sb, "internal object");
+               break;
+       case OBJ_COMMIT:
+               buf = read_sha1_file(object, &type, &size);
+               if (buf) {
+                       subject_len = find_commit_subject(buf, &subject_start);
+                       strbuf_insert(sb, sb->len, subject_start, subject_len);
+                       free(buf);
+               } else {
+                       die("commit object %s could not be read",
+                               sha1_to_hex(object));
+               }
+               break;
+       case OBJ_TREE:
+               strbuf_addstr(sb, "tree object");
+               break;
+       case OBJ_BLOB:
+               strbuf_addstr(sb, "blob object");
+               break;
+       case OBJ_TAG:
+               strbuf_addstr(sb, "other tag object");
+               break;
+       }
+}
+
 struct msg_arg {
        int given;
        struct strbuf buf;
@@ -335,6 +378,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
        struct strbuf ref = STRBUF_INIT;
+       struct strbuf reflog_msg = STRBUF_INIT;
        unsigned char object[20], prev[20];
        const char *object_ref, *tag;
        struct create_tag_options opt;
@@ -349,7 +393,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct ref_filter filter;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
        const char *format = NULL;
-       int icase = 0;
+       int icase = 0, i;
        struct option options[] = {
                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
                { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -391,6 +435,11 @@ int cmd_tag(int argc, const char **argv, const char 
*prefix)
 
        git_config(git_tag_config, sorting_tail);
 
+       /* Record the command line for the reflog */
+       strbuf_addstr(&default_rla, "tag");
+       for (i = 1; i < argc; i++)
+               strbuf_addf(&default_rla, " %s", argv[i]);
+
        memset(&opt, 0, sizeof(opt));
        memset(&filter, 0, sizeof(filter));
        filter.lines = -1;
@@ -494,6 +543,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        else
                die(_("Invalid cleanup mode %s"), cleanup_arg);
 
+       create_reflog_msg(object, &reflog_msg);
+
        if (create_tag_object) {
                if (force_sign_annotate && !annotate)
                        opt.sign = 1;
@@ -504,7 +555,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (!transaction ||
            ref_transaction_update(transaction, ref.buf, object, prev,
                                   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
-                                  NULL, &err) ||
+                                  reflog_msg.buf, &err) ||
            ref_transaction_commit(transaction, &err))
                die("%s", err.buf);
        ref_transaction_free(transaction);
@@ -514,5 +565,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        strbuf_release(&err);
        strbuf_release(&buf);
        strbuf_release(&ref);
+       strbuf_release(&reflog_msg);
        return 0;
 }
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 072e6c6..9c80bc9 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -80,10 +80,24 @@ test_expect_success 'creating a tag using default HEAD 
should succeed' '
        test_must_fail git reflog exists refs/tags/mytag
 '
 
+git log -1 > expected \
+       --format="format:tag --create-reflog tag_with_reflog: %s%n"
 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
+       git reflog exists refs/tags/tag_with_reflog &&
+       sed -e "s/^.*\t//" .git/logs/refs/tags/tag_with_reflog > actual &&
+       test_cmp expected actual
+'
+
+git log -1 > expected \
+       --format='format:tag -m annotated tag --create-reflog tag_with_reflog: 
%s%n'
+test_expect_success 'annotated tag with --create-reflog has correct message' '
+       test_when_finished "git tag -d tag_with_reflog" &&
+       git tag -m "annotated tag" --create-reflog tag_with_reflog &&
+       git reflog exists refs/tags/tag_with_reflog &&
+       sed -e "s/^.*\t//" .git/logs/refs/tags/tag_with_reflog > actual &&
+       test_cmp expected actual
 '
 
 test_expect_success '--create-reflog does not create reflog on failure' '
-- 
2.10.2

Reply via email to