This can be useful e.g. in `filter-branch` when rewritting tags produced by
older versions of Git, such as v2.6.12-rc2..v2.6.13-rc3 in the Linux kernel
source tree:

        $ git cat-file tag v2.6.12-rc2
        object 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2
        type commit
        tag v2.6.12-rc2

        Linux v2.6.12-rc2 release
        -----BEGIN PGP SIGNATURE-----
        Version: GnuPG v1.2.4 (GNU/Linux)

        iD8DBQBCbW8ZF3YsRnbiHLsRAgFRAKCq/TkuDaEombFABkPqYgGCgWN2lQCcC0qc
        wznDbFU45A54dZC8RZ5JxyE=
        =ESRP
        -----END PGP SIGNATURE-----

        $ git cat-file tag v2.6.12-rc2 | git mktag
        error: char76: could not find "tagger "
        fatal: invalid tag signature file
        $ git cat-file tag v2.6.12-rc2 | git mktag --allow-missing-tagger
        9e734775f7c22d2f89943ad6c745571f1930105f

To that end, pass the new option to `mktag` in `filter-branch`.

Signed-off-by: Ian Campbell <[email protected]>
---
 Documentation/git-mktag.txt |   9 +++-
 builtin/mktag.c             | 100 +++++++++++++++++++++++++-------------------
 git-filter-branch.sh        |   2 +-
 t/t3800-mktag.sh            |  33 ++++++++++++++-
 4 files changed, 98 insertions(+), 46 deletions(-)

diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
index fa6a75612..c720c7419 100644
--- a/Documentation/git-mktag.txt
+++ b/Documentation/git-mktag.txt
@@ -9,7 +9,7 @@ git-mktag - Creates a tag object
 SYNOPSIS
 --------
 [verse]
-'git mktag'
+'git mktag' [--allow-missing-tagger]
 
 DESCRIPTION
 -----------
@@ -34,6 +34,13 @@ exists, is separated by a blank line from the header.  The
 message part may contain a signature that Git itself doesn't
 care about, but that can be verified with gpg.
 
+OPTIONS
+-------
+--allow-missing-tagger::
+       Allow the `tagger` line in the header to be omitted. This is
+       rarely desirable but may be useful in recreating tags created
+       by older Git.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 031b750f0..0f5dae8d5 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "parse-options.h"
 #include "tag.h"
 
 /*
@@ -15,6 +16,8 @@
  * the shortest possible tagger-line.
  */
 
+static int allow_missing_tagger;
+
 /*
  * We refuse to tag something we can't verify. Just because.
  */
@@ -41,8 +44,9 @@ static int verify_tag(char *buffer, unsigned long size)
        unsigned char sha1[20];
        const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb;
        size_t len;
+       const unsigned long min_size = allow_missing_tagger ? 71 : 84;
 
-       if (size < 84)
+       if (size < min_size)
                return error("wanna fool me ? you obviously got the size wrong 
!");
 
        buffer[size] = 0;
@@ -98,46 +102,46 @@ static int verify_tag(char *buffer, unsigned long size)
        /* Verify the tagger line */
        tagger_line = tag_line;
 
-       if (memcmp(tagger_line, "tagger ", 7))
+       if (!memcmp(tagger_line, "tagger ", 7)) {
+               /*
+                * Check for correct form for name and email
+                * i.e. " <" followed by "> " on _this_ line
+                * No angle brackets within the name or email address fields.
+                * No spaces within the email address field.
+                */
+               tagger_line += 7;
+               if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> 
")) ||
+                       strpbrk(tagger_line, "<>\n") != lb+1 ||
+                       strpbrk(lb+2, "><\n ") != rb)
+                       return error("char%"PRIuMAX": malformed tagger field",
+                               (uintmax_t) (tagger_line - buffer));
+
+               /* Check for author name, at least one character, space is 
acceptable */
+               if (lb == tagger_line)
+                       return error("char%"PRIuMAX": missing tagger name",
+                               (uintmax_t) (tagger_line - buffer));
+
+               /* timestamp, 1 or more digits followed by space */
+               tagger_line = rb + 2;
+               if (!(len = strspn(tagger_line, "0123456789")))
+                       return error("char%"PRIuMAX": missing tag timestamp",
+                               (uintmax_t) (tagger_line - buffer));
+               tagger_line += len;
+               if (*tagger_line != ' ')
+                       return error("char%"PRIuMAX": malformed tag timestamp",
+                               (uintmax_t) (tagger_line - buffer));
+               tagger_line++;
+
+               /* timezone, 5 digits [+-]hhmm, max. 1400 */
+               if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
+                     strspn(tagger_line+1, "0123456789") == 4 &&
+                     tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
+                       return error("char%"PRIuMAX": malformed tag timezone",
+                               (uintmax_t) (tagger_line - buffer));
+               tagger_line += 6;
+       } else if (!allow_missing_tagger)
                return error("char%"PRIuMAX": could not find \"tagger \"",
-                       (uintmax_t) (tagger_line - buffer));
-
-       /*
-        * Check for correct form for name and email
-        * i.e. " <" followed by "> " on _this_ line
-        * No angle brackets within the name or email address fields.
-        * No spaces within the email address field.
-        */
-       tagger_line += 7;
-       if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
-               strpbrk(tagger_line, "<>\n") != lb+1 ||
-               strpbrk(lb+2, "><\n ") != rb)
-               return error("char%"PRIuMAX": malformed tagger field",
-                       (uintmax_t) (tagger_line - buffer));
-
-       /* Check for author name, at least one character, space is acceptable */
-       if (lb == tagger_line)
-               return error("char%"PRIuMAX": missing tagger name",
-                       (uintmax_t) (tagger_line - buffer));
-
-       /* timestamp, 1 or more digits followed by space */
-       tagger_line = rb + 2;
-       if (!(len = strspn(tagger_line, "0123456789")))
-               return error("char%"PRIuMAX": missing tag timestamp",
-                       (uintmax_t) (tagger_line - buffer));
-       tagger_line += len;
-       if (*tagger_line != ' ')
-               return error("char%"PRIuMAX": malformed tag timestamp",
-                       (uintmax_t) (tagger_line - buffer));
-       tagger_line++;
-
-       /* timezone, 5 digits [+-]hhmm, max. 1400 */
-       if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
-             strspn(tagger_line+1, "0123456789") == 4 &&
-             tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
-               return error("char%"PRIuMAX": malformed tag timezone",
-                       (uintmax_t) (tagger_line - buffer));
-       tagger_line += 6;
+                            (uintmax_t) (tagger_line - buffer));
 
        /* Verify the blank line separating the header from the body */
        if (*tagger_line != '\n')
@@ -148,13 +152,25 @@ static int verify_tag(char *buffer, unsigned long size)
        return 0;
 }
 
+static char const * const mktag_usage[] = {
+       N_("git mktag [<options>]"),
+       NULL
+};
+
+static struct option mktag_opts[] = {
+       OPT_BOOL(0, "allow-missing-tagger", &allow_missing_tagger, N_("allow 
the tagger field to be omitted")),
+       OPT_END(),
+};
+
 int cmd_mktag(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
        unsigned char result_sha1[20];
 
-       if (argc != 1)
-               usage("git mktag");
+       argc = parse_options(argc, argv, prefix, mktag_opts, mktag_usage, 0);
+
+       if (argc != 0)
+               usage_with_options(mktag_usage, mktag_opts);
 
        if (strbuf_read(&buf, 0, 4096) < 0) {
                die_errno("could not read from stdin");
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 3a74602ef..05645064a 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -530,7 +530,7 @@ if [ "$filter_tag_name" ]; then
                                        }' \
                                    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
                                    -e 'p' ) |
-                               git mktag) ||
+                               git mktag --allow-missing-tagger) ||
                                die "Could not create new tag object for $ref"
                        if git cat-file tag "$ref" | \
                           sane_grep '^-----BEGIN PGP SIGNATURE-----' 
>/dev/null 2>&1
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
index 8eb47942e..3a77a26c8 100755
--- a/t/t3800-mktag.sh
+++ b/t/t3800-mktag.sh
@@ -340,7 +340,36 @@ check_verify_failure 'detect invalid header entry' \
        '^error: char124: trailing garbage in tag header$'
 
 ############################################################
-# 24. create valid tag
+# 24. missing tagger ok with --allow-missing-tagger
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+
+EOF
+
+test_expect_success \
+    'missing tagger with --allow-missing-tagger' \
+    'git mktag --allow-missing-tagger <tag.sig >.git/refs/tags/mytag 2>message'
+
+############################################################
+# 25. detect invalid header entry with --allow-missing-tagger
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+this line should not be here
+EOF
+
+test_expect_success \
+    'detect invalid header entry with --allow-missing-tagger' \
+    '( test_must_fail git mktag --allow-missing-tagger <tag.sig 2>message ) &&
+       grep "^error: char70: trailing garbage in tag header$" message'
+
+############################################################
+# 26. create valid tag
 
 cat >tag.sig <<EOF
 object $head
@@ -355,7 +384,7 @@ test_expect_success \
     'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
 
 ############################################################
-# 25. check mytag
+# 27. check mytag
 
 test_expect_success \
     'check mytag' \
-- 
2.11.0

Reply via email to