Make git-tag a builtin.
PATCH 2/2 Make git-tag a builtin.
This replaces the script "git-tag.sh" with "builtin-tag.c". It is based in a previous work on it from Kristian Høgsberg.
There are some minor changes in the behaviour of "git tag" here:
"git tag -v" now can get more than one tag to verify, like "git tag -d" did, "git tag" with no arguments prints all tags, more like "git branch" does, and the template for the edited message adds an empty line, like in "git commit".
The program is now calling to the script "git verify-tag" for verify, something that should be changed porting it to C and calling its functions directly from builtin-tag.c.
---
As said, "git verify-tag" should be replaced with "git tag" for verify tags, since both do the same job. The builtin "git mktag" is another candidate to be replaced with "git tag", however it is now an essential part of the "plumbing" family doing a neat thing not easily replaceable.
The function launch_editor is copied and modified from the initial work from Kristian on builtin-commit.c, so we expect share that code on both builtins when it is finnished.
Makefile | 3 +- builtin-tag.c | 430 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git-tag.sh | 205 --------------------------- git.c | 1 + 5 files changed, 434 insertions(+), 206 deletions(-) create mode 100644 builtin-tag.c delete mode 100755 git-tag.sh
Makefile +2 -1
@@ -207,7 +207,7 @@ SCRIPT_SH = \ git-pull.sh git-rebase.sh git-rebase--interactive.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ git-sh-setup.sh \ - git-tag.sh git-verify-tag.sh \ + git-verify-tag.sh \ git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh \
@@ -376,6 +376,7 @@ BUILTIN_OBJS = \ builtin-show-branch.o \ builtin-stripspace.o \ builtin-symbolic-ref.o \ + builtin-tag.o \ builtin-tar-tree.o \ builtin-unpack-objects.o \ builtin-update-index.o \
builtin-tag.c new file +430 -0
430 lines not on this page · raw message
builtin.h +1 -0
@@ -70,6 +70,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);extern int cmd_show_branch (int argc,const char **argv,const char *prefix);extern int cmd_stripspace (int argc,const char **argv,const char *prefix);extern int cmd_symbolic_ref (int argc,const char **argv,const char *prefix); +extern int cmd_tag (int argc,const char **argv,const char *prefix);extern int cmd_tar_tree (int argc,const char **argv,const char *prefix);extern int cmd_unpack_objects (int argc,const char **argv,const char *prefix);extern int cmd_update_index (int argc,const char **argv,const char *prefix);
git-tag.sh deleted +0 -205
205 lines not on this page · raw message
git.c +1 -0
@@ -363,6 +363,7 @@ static void handle_internal_command(int argc, const char **argv) { "show", cmd_show, RUN_SETUP | USE_PAGER }, { "stripspace", cmd_stripspace }, { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP }, + { "tag", cmd_tag, RUN_SETUP }, { "tar-tree", cmd_tar_tree }, { "unpack-objects", cmd_unpack_objects, RUN_SETUP }, { "update-index", cmd_update_index, RUN_SETUP },
Signature · 1 line
1.5.0
Quoted 2 lines · Carlos Rica
Mime-Version: 1.0
Content-Type: text/plain; charset=windows-1252
Hmmmmmm.....
Quoted 7 lines
This replaces the script "
git-tag.sh" with "builtin-tag.c". It is based in a previous work on it from Kristian Høgsberg.There are some minor changes in the behaviour of "git tag" here:
"git tag -v" now can get more than one tag to verify, like "git tag -d" did, "git tag" with no arguments prints all tags, more like "git branch" does, and the template for the edited message adds an empty line, like in "git commit".
These probably are good changes (except that "no argument" case might be a bit annoying, but I guess it's Ok). Is there any need for documentation updates with these changes?
builtin-tag.c line 4
Quoted 10 lines
builtin-tag.c new file +4
-0@@ -0,0 +1,430 @@ +/* +* Builtin "git tag" +* +* Copyright (c) 2007 Kristian H??gsberg <[email protected]>,
I do not know how the above would come out, but it surely was not Høgsberg in the copy I received...
We probably should make sure our sources are in UTF-8; I suspect builtin-branch.c is not.
Quoted 24 lines
builtin-tag.c new file +24
-0+* Carlos Rica <[email protected]> +* Based on git-tag.sh and mktag.c by Linus Torvalds. +*/ + +#include "cache.h" +#include "builtin.h" +#include "refs.h" +#include "tag.h" +#include "run-command.h" + +static const char builtin_tag_usage[] = +"git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]" ; + +static char signingkey[1000 ]; +static int lines; + +static void launch_editor (const char *path,char **buffer,unsigned long *len) +{ +const char *editor, *terminal; +struct child_process child; +const char *args[3 ]; +int fd; + + editor =getenv ("VISUAL" );
builtin-tag.c line 38
Quoted 10 lines
builtin-tag.c new file +10
-0+if (!editor) + editor =getenv ("EDITOR" ); + + terminal =getenv ("TERM" ); +if (!editor && (!terminal || !strcmp (terminal,"dumb" ))) { +fprintf (stderr, +"Terminal is dumb but no VISUAL nor EDITOR defined.\n" +"Please supply the message using either -m or -F option.\n" ); +exit (1 ); + }
Ah, this is taken from git-commit.sh ;-) Does your "git tag" support the -F option (builtin_tag_usage[] does not seem to mention it)? I wonder what happens when this function migrates to editor.c and a new program, other than git-tag and git-commit, that is without -F nor -m options wants to call this.
builtin-tag.c line 50
Quoted 11 lines
builtin-tag.c new file +11
-0+if (!editor) + editor ="vi" ; + +memset (&child,0 ,sizeof (child)); + child.argv = args; + args[0 ] = editor; + args[1 ] = path; + args[2 ] =NULL ; + +if (run_command (&child)) +die ("could not launch editor %s." , editor);
This message is not quite true, isn't it? The editor might have been launched but exited with non-zero status.
builtin-tag.c line 54
Quoted 3 lines
builtin-tag.c new file +3
-0+ fd =open (path,O_RDONLY ); +if (fd ==-1 ) +die ("could not read %s." , path);
And this is "could not open", with probably strerror(errno) to be helpful.
builtin-tag.c line 78
Quoted 13 lines
builtin-tag.c new file +12
-0+if (read_pipe (fd, buffer, len)) +die ("could not read message file '%s': %s" , + path,strerror (errno)); +close (fd); +} + +#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" + +static int show_reference (const char *refname,const unsigned char *sha1, +int flag,void *cb_data) +{ +}
Ok.
builtin-tag.c line 124
Quoted 4 lines
builtin-tag.c new file +3
-0+static int list_tags (const char *pattern) +{ +}
Ok.
builtin-tag.c line 136
Quoted 10 lines
builtin-tag.c new file +10
-0+static int delete_tags (const char **argv) +{ +const char **p; +char ref[PATH_MAX ]; +int had_error =0 ; +unsigned char sha1[20 ]; + +for (p = argv; *p; p++) { +if (snprintf (ref,sizeof ref,"refs/tags/%s" , *p) >sizeof ref) +die ("tag name '%s' too long." , *p);
"sizeof foo" may be proper ANSI C, but somehow many people seem to find "sizeof(foo)" much easier to read, including me.
I think the overflow check is wrong. snprintf() does not write more than sizeof(ref) including the training NUL, but when its output is truncated, it returns the number of bytes that would have been written to the buffer, excluding the NUL termination. What this means is that if it returns sizeof(ref), it also is a case of overflow. Further, if C library is not C99, e.g. older glibc (say 2.0.6), it could return negative status indicating an error condition upon truncated output.
I suspect you inherited these problems from builtin-branch.c, but we'd better fix them there as well.
builtin-tag.c line 141
Quoted 5 lines
builtin-tag.c new file +5
-0+if (!resolve_ref (ref, sha1,1 ,NULL )) { +fprintf (stderr,"tag '%s' not found.\n" , *p); + had_error =1 ; +continue ; + }
We might want to call error("tag '%s' not found.") instead for later libification...
Quoted 6 lines
...
builtin-tag.c new file +5
-0+} + +static int run_verify_tag_command (unsigned char *sha1) +{ +int ret;
builtin-tag.c line 164
Quoted 10 lines
builtin-tag.c new file +10
-0+const char *argv_verify_tag[] = {"git-verify-tag" , +"-v" ,"SHA1_HEX" ,NULL }; + argv_verify_tag[2 ] =sha1_to_hex (sha1); + + ret =run_command_v_opt (argv_verify_tag,0 ); + +if (ret <=-10000 ) +die ("unable to run %s\n" , argv_verify_tag[0 ]); +return -ret; +}
I wonder why you need to differentiate between ERR_RUN_COMMAND_* and non-zero exit status... Also do you need to "return -ret", instead of not negating? In C programs error returns are often negative and finish_command() follows that convention.
builtin-tag.c line 181
Quoted 4 lines
builtin-tag.c new file +3
-0+static int verify_tags (const char **argv) +{ +}
Ok.
builtin-tag.c line 201
Quoted 13 lines
builtin-tag.c new file +13
-0+static int do_sign (char *buffer,size_t size,size_t max) +{ +struct child_process gpg; +const char *args[4 ]; +char *bracket; +int len; + +if (signingkey[0 ] =='\0' ) { +strlcpy (signingkey,git_committer_info (1 ),sizeof signingkey); + bracket =strchr (signingkey,'>' ); +if (bracket) + bracket[1 ] ='\0' ; + }
Hmph. Silently truncate with strlcpy instead of erroring out on insanely long committer info?
Quoted 6 lines
builtin-tag.c new file +6
-0+memset (&gpg,0 ,sizeof (gpg)); + gpg.argv = args; + gpg.in =-1 ; + gpg.out =-1 ; + args[0 ] ="gpg" ; + args[1 ] ="-bsau" ;
builtin-tag.c line 218
Quoted 10 lines
builtin-tag.c new file +10
-0+ args[2 ] = signingkey; + args[3 ] =NULL ; + +if (start_command (&gpg)) +die ("could not run gpg." ); + +write_or_die (gpg.in, buffer, size); +close (gpg.in); + gpg.close_in =0 ; + len =read_in_full (gpg.out, buffer + size, max - size);
Bi-di pipes makes me nervous, but this case should be Ok. It could try writing "---BEGIN PGP SIGNATURE---" before reading from you and get stuck because you are not reading from it but that is only in theory.
I wonder why our read_in_full and write_in_full do not return ssize_t although that's how they count things internally. At least you could make do_sign() to return ssize_t, and change the die() to "return error()" to have the caller deal with it, which would be easier for people other than "git tag -s" to call this if they later want to.
What happens when gpg gave more than you expect? We get truncated signature and I do not see any code to notice the breakage...
Quoted 6 lines
builtin-tag.c new file +6
-0+ +finish_command (&gpg); + +return size + len; +} +
builtin-tag.c line 238
Quoted 10 lines
builtin-tag.c new file +9
-0+static const char tag_template[] = +"\n" +"#\n" +"# Write a tag message\n" +"#\n" ; + +static int git_tag_config (const char *var,const char *value) +{ +}
Ok, except for the same strlcpy comment above.
Quoted 16 lines
builtin-tag.c new file +16
-0+#define MAX_SIGNATURE_LENGTH 1024 +/* message must be NULL or allocated, it will be reallocated and freed */ +static void create_tag (const unsigned char *object,const char *tag, +char *message,int sign,unsigned char *result) +{ +enum object_type type; +char header_buf[1024 ], *buffer; +int header_len, max_size; +unsigned long size; + + type =sha1_object_info (object,NULL ); +if (type <=0 ) +die ("bad object type." ); + + header_len =snprintf (header_buf,sizeof header_buf, + "object %s\n"
builtin-tag.c line 268
Quoted 10 lines
builtin-tag.c new file +10
-0+"type %s\n" +"tag %s\n" +"tagger %s\n\n" , +sha1_to_hex (object), +typename (type), + tag, + git_committer_info(1)); + +if (header_len >=sizeof header_buf) +die ("tag header too big." );
This comparison '>=' is correct (i.e. not '>'), as opposed to the one I commented way above.
builtin-tag.c line 295
Quoted 12 lines
builtin-tag.c new file +11
-0+if (!message) { + } +else { + buffer = message; + size =strlen (message); + } + + size =stripspace (buffer, size,1 ); + +if (!message && !size) +die ("no tag message?" );
Why check 'message' here?
builtin-tag.c
Quoted 3 lines
...
builtin-tag.c new file +2
-0+free (buffer); +}
The rest is Ok.
builtin-tag.c line 370
Quoted 13 lines
builtin-tag.c new file +12
-0+int cmd_tag (int argc,const char **argv,const char *prefix) +{ +if (!strcmp (arg,"-F" )) { +unsigned long len; + annotate =1 ; + i++; +if (i == argc) +die ("option -F needs an argument." ); + + fd =open (argv[i],O_RDONLY ); +if (fd <0 ) +die ("cannot open %s" , argv[1 ]);
The shell script version relies on the magic "cat -" to read from standard input upon "git tag -F -". It is understandable that both of you and Dscho missed it, though.
builtin-tag.c line 379
Quoted 8 lines
builtin-tag.c new file +8
-0+ len =1024 ; + message =xmalloc (len); +if (read_pipe (fd, &message, &len)) +die ("cannot read %s" , argv[1 ]); + message =xrealloc (message, len +1 ); + message[len] ='\0' ; +continue ; + }
We might be better off if read_pipe() is renamed to read_fd() and made internally always NUL-terminate the buffer (but not count that NUL as part of length). I dunno.
builtin-tag.c line 387
Quoted 8 lines
builtin-tag.c new file +8
-0+if (!strcmp (arg,"-u" )) { + annotate =1 ; + sign =1 ; + i++; +if (i == argc) +die ("option -u needs an argument." ); +strlcpy (signingkey, argv[i],sizeof signingkey); +continue ;
Extra space "if (expr". Return from strlcpy() not checked?
builtin-tag.c line 411
Quoted 2 lines
builtin-tag.c new file +2
-0+if (snprintf (ref,sizeof ref,"refs/tags/%s" , tag) >sizeof ref) +die ("tag '%s' too long." , tag);
This use of snprintf() and checking the overflow as error is much nicer, don't you think, instead of silently ignoring truncation?
Quoted 2 lines
...
+}
The remainder of cmd_tag() looks Ok.
Thank you for your review, Junio, I'm studing all of your suggestions.
2007/7/12, Junio C Hamano <[email protected]>:
Quoted 7 lines
Quoted 2 lines · Carlos Rica
Mime-Version: 1.0
Content-Type: text/plain; charset=windows-1252Hmmmmmm.....
Quoted 13 lines
builtin-tag.c line 4
Quoted 10 lines
builtin-tag.c new file +4
-0@@ -0,0 +1,430 @@ +/* +* Builtin "git tag" +* +* Copyright (c) 2007 Kristian H??gsberg <[email protected]>, I do not know how the above would come out, but it surely was not Høgsberg in the copy I received...
I think it is my fault. My original builtin-tag.c file has the name encoded in UTF-8, and the patch too, except because I also added his name in the commit's message in Latin-1 encoding, and then, programs cannot recode or recognize both. Sorry.
Quoted 34 lines
Quoted 34 lines
builtin-tag.c new file +33
-0+* Carlos Rica <[email protected]> +* Based on git-tag.sh and mktag.c by Linus Torvalds. +*/ + +#include "cache.h" +#include "builtin.h" +#include "refs.h" +#include "tag.h" +#include "run-command.h" + +static const char builtin_tag_usage[] = +"git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]" ; + +static char signingkey[1000 ]; +static int lines; + +static void launch_editor (const char *path,char **buffer,unsigned long *len) +{ +const char *editor, *terminal; +struct child_process child; +const char *args[3 ]; +int fd; + + editor =getenv ("VISUAL" ); +if (!editor) + editor =getenv ("EDITOR" ); + + terminal =getenv ("TERM" ); +if (!editor && (!terminal || !strcmp (terminal,"dumb" ))) { +fprintf (stderr, +"Terminal is dumb but no VISUAL nor EDITOR defined.\n" +"Please supply the message using either -m or -F option.\n" ); +exit (1 );
Quoted 8 lines
builtin-tag.c line 38
Quoted 1 line
builtin-tag.c new file +1
-0+ }Ah, this is taken from
git-commit.sh;-) Does your "git tag" support the -F option (builtin_tag_usage[] does not seem to mention it)? I wonder what happens when this function migrates toeditor.cand a new program, other than git-tag and git-commit, that is without -F nor -m options wants to call this.
I will include that -F option in the builtin_tag_usage and I will also find if documentation already has it.
As I said above, launch_editor is copied and modified from the builtin-commit.c that Kristian is currently writing. Indeed, I removed every mention on it related
specifically to commits, so it will be difficult to share the code between both versions in the current state. Perhaps a raw function not printing out errors (and returning error codes instead) could be the only option to reuse it. I need to ask Kristian for this.
Quoted 10 lines
Quoted 10 lines
builtin-tag.c new file +10
-0+static int run_verify_tag_command (unsigned char *sha1) +{ +int ret; +const char *argv_verify_tag[] = {"git-verify-tag" , +"-v" ,"SHA1_HEX" ,NULL }; + argv_verify_tag[2 ] =sha1_to_hex (sha1); + + ret =run_command_v_opt (argv_verify_tag,0 ); + +if (ret <=-10000 )
Quoted 8 lines
builtin-tag.c line 164
Quoted 3 lines
builtin-tag.c new file +3
-0+die ("unable to run %s\n" , argv_verify_tag[0 ]); +return -ret; +}I wonder why you need to differentiate between ERR_RUN_COMMAND_* and non-zero exit status... Also do you need to "return -ret", instead of not negating? In C programs error returns are often negative and
finish_command()follows that convention.
The point here was to mimic the behaviour of git-tag.sh, returning the exit status returned by the called script: "git-verify-tag.sh". However, I didn't realize that git-verify tag.sh was exiting only with 1 or 0, so "return ret;" will also work since the call to the function is:
if (run_verify_tag_command(sha1))
had_error = 1;
When die is called (because of an ERR_RUN_COMMAND_* error condition), it will return 128 to the system, and won't do more processing to other tags. Do yo mean that it would be better to remove this "die" call here?
Quoted 15 lines
builtin-tag.c line 295
Quoted 13 lines
builtin-tag.c new file +11
-0+if (!message) { + } +else { + buffer = message; + size =strlen (message); + } + + size =stripspace (buffer, size,1 ); + +if (!message && !size) +die ("no tag message?" );Why check 'message' here?
Because the behaviour of git-tag.sh here is not allow empty messages when the editor is executed, but allow them when -m or -F options are given (message then will be not NULL).
Quoted 9 lines
Quoted 9 lines
builtin-tag.c new file +8
-0+int cmd_tag (int argc,const char **argv,const char *prefix) +{ +if (!strcmp (arg,"-F" )) { +unsigned long len; + annotate =1 ; + i++; +if (i == argc) +die ("option -F needs an argument." );
Quoted 8 lines
builtin-tag.c line 370
Quoted 4 lines
builtin-tag.c new file +4
-0+ + fd =open (argv[i],O_RDONLY ); +if (fd <0 ) +die ("cannot open %s" , argv[1 ]);The shell script version relies on the magic "cat -" to read from standard input upon "git tag -F -". It is understandable that both of you and Dscho missed it, though.
It should be easy to implement, I will do it.
Quoted 9 lines
builtin-tag.c line 379
Quoted 8 lines
builtin-tag.c new file +8
-0+ len =1024 ; + message =xmalloc (len); +if (read_pipe (fd, &message, &len)) +die ("cannot read %s" , argv[1 ]); + message =xrealloc (message, len +1 ); + message[len] ='\0' ; +continue ; + }
Quoted 3 lines
We might be better off if
read_pipe()is renamed toread_fd()and made internally always NUL-terminate the buffer (but not count that NUL as part of length). I dunno.
I saw an implementation for read_pipe named read_fd from Kristian:
http://article.gmane.org/gmane.comp.version-control.git/51366
but it is only focused on allowing NULL and 0 in pointer and size parameters. It should be harmless NUL-terminating the buffer since the routine is already allocating more memory than needed for efficiency purposes.