From: Lucas Gabriel Vuotto Subject: Introduce one-line mode for got tag -l To: gameoftrees@openbsd.org Date: Sun, 22 Dec 2024 18:49:07 +0000 As the subject reads. There are 2 commits in here: - moving tag printing into its own function. I believe I did it in a error-compatible way, but an extra pair of eyes are more than welcome, especially because the new function signature is ugly. - adding the actual functionality, lifted from 'log -s'. And here, I have 2 bikesheds I'd like to remodel: - I added a shortened tag hash to the output, but idk if it's actually useful, or if the corresponding commit hash is useful (if it exists? I'm unsure if that's always the case). Or we can go with only 'date tagname first-line-of-message'. - given that -s is taken, I used -S for the sake of the patch. But I'd prefer using -s here too, to keep it in-sync with log. Can '-s signer' be changed to '-S signer'? And I also have a question: I believe -V doesn't make much sense a short output. Should I make -S conflict with -V? Or should we still go ahead and verify tags, and add a simple indicator for whether the signature is valid or not? Lucas ----------------------------------------------- commit 8daa7bca200234e24a0e77d03e589d5ff53d698b from: Lucas Gabriel Vuotto date: Sun Dec 22 18:29:15 2024 UTC got tag: move tag printing into its own function in prepartion for a oneline output mode M got/got.c diff 589b5832ea0e12318af1af49fb76e2887629e2b6 8daa7bca200234e24a0e77d03e589d5ff53d698b commit - 589b5832ea0e12318af1af49fb76e2887629e2b6 commit + 8daa7bca200234e24a0e77d03e589d5ff53d698b blob - 36655f1f322184d063373730e92b7dbf08b373a4 blob + a24af714d90145206eec64eed5b494e9d1436a82 --- got/got.c +++ got/got.c @@ -7554,6 +7554,77 @@ get_tag_refname(char **refname, const char *tag_name) } static const struct got_error * +print_tag(struct got_tag_object *tag, struct got_commit_object *commit, + const char *refname, const char *refstr, const char *tagger, + time_t tagger_time, const char *id_str, const char *ssh_sig, + const char *allowed_signers, const char *revoked_signers, int verbosity, + int *bad_sigs) +{ + static const struct got_error *err = NULL; + char datebuf[26]; + char *sig_msg = NULL, *tagmsg0 = NULL, *tagmsg, *line, *datestr; + + printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr); + printf("from: %s\n", tagger); + datestr = get_datestr(&tagger_time, datebuf); + if (datestr) + printf("date: %s UTC\n", datestr); + if (commit) + printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str); + else { + switch (got_object_tag_get_object_type(tag)) { + case GOT_OBJ_TYPE_BLOB: + printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB, id_str); + break; + case GOT_OBJ_TYPE_TREE: + printf("object: %s %s\n", GOT_OBJ_LABEL_TREE, id_str); + break; + case GOT_OBJ_TYPE_COMMIT: + printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str); + break; + case GOT_OBJ_TYPE_TAG: + printf("object: %s %s\n", GOT_OBJ_LABEL_TAG, id_str); + break; + default: + break; + } + } + + if (ssh_sig) { + err = got_sigs_verify_tag_ssh(&sig_msg, tag, ssh_sig, + allowed_signers, revoked_signers, verbosity); + if (err && err->code == GOT_ERR_BAD_TAG_SIGNATURE) + *bad_sigs = 1; + else if (err) + goto out; + printf("signature: %s", sig_msg); + free(sig_msg); + } + + if (commit) { + err = got_object_commit_get_logmsg(&tagmsg0, commit); + if (err) + goto out; + got_object_commit_close(commit); + } else { + tagmsg0 = strdup(got_object_tag_get_message(tag)); + got_object_tag_close(tag); + if (tagmsg0 == NULL) { + err = got_error_from_errno("strdup"); + goto out; + } + } + + tagmsg = tagmsg0; + while ((line = strsep(&tagmsg, "\n")) != NULL) + printf(" %s\n", line); + + out: + free(tagmsg0); + return err; +} + +static const struct got_error * list_tags(struct got_repository *repo, const char *tag_name, int verify_tags, const char *allowed_signers, const char *revoked_signers, int verbosity) { @@ -7583,10 +7654,8 @@ list_tags(struct got_repository *repo, const char *tag TAILQ_FOREACH(re, &refs, entry) { const char *refname; - char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr; - char datebuf[26]; + char *refstr, *id_str; const char *tagger, *ssh_sig = NULL; - char *sig_msg = NULL; time_t tagger_time; struct got_object_id *id; struct got_tag_object *tag; @@ -7648,71 +7717,15 @@ list_tags(struct got_repository *repo, const char *tag } } - printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr); - free(refstr); - printf("from: %s\n", tagger); - datestr = get_datestr(&tagger_time, datebuf); - if (datestr) - printf("date: %s UTC\n", datestr); - if (commit) - printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, id_str); - else { - switch (got_object_tag_get_object_type(tag)) { - case GOT_OBJ_TYPE_BLOB: - printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB, - id_str); - break; - case GOT_OBJ_TYPE_TREE: - printf("object: %s %s\n", GOT_OBJ_LABEL_TREE, - id_str); - break; - case GOT_OBJ_TYPE_COMMIT: - printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT, - id_str); - break; - case GOT_OBJ_TYPE_TAG: - printf("object: %s %s\n", GOT_OBJ_LABEL_TAG, - id_str); - break; - default: - break; - } - } + err = print_tag(tag, commit, refname, refstr, tagger, + tagger_time, id_str, ssh_sig, allowed_signers, + revoked_signers, verbosity, &bad_sigs); + free(id_str); + free(refstr); - if (ssh_sig) { - err = got_sigs_verify_tag_ssh(&sig_msg, tag, ssh_sig, - allowed_signers, revoked_signers, verbosity); - if (err && err->code == GOT_ERR_BAD_TAG_SIGNATURE) - bad_sigs = 1; - else if (err) - break; - printf("signature: %s", sig_msg); - free(sig_msg); - sig_msg = NULL; - } - - if (commit) { - err = got_object_commit_get_logmsg(&tagmsg0, commit); - if (err) - break; - got_object_commit_close(commit); - } else { - tagmsg0 = strdup(got_object_tag_get_message(tag)); - got_object_tag_close(tag); - if (tagmsg0 == NULL) { - err = got_error_from_errno("strdup"); - break; - } - } - - tagmsg = tagmsg0; - do { - line = strsep(&tagmsg, "\n"); - if (line) - printf(" %s\n", line); - } while (line); - free(tagmsg0); + if (err != NULL) + break; } done: got_ref_list_free(&refs); ----------------------------------------------- commit 3ce9f89cf4b06724a377239d761d90a1f3aee83e (tag-short) from: Lucas Gabriel Vuotto date: Sun Dec 22 18:29:15 2024 UTC got tag: provide one-line output mode M got/got.1 M got/got.c M regress/cmdline/tag.sh diff 8daa7bca200234e24a0e77d03e589d5ff53d698b 3ce9f89cf4b06724a377239d761d90a1f3aee83e commit - 8daa7bca200234e24a0e77d03e589d5ff53d698b commit + 3ce9f89cf4b06724a377239d761d90a1f3aee83e blob - e653af513ca5618a8e7dcf9459524bbb2f483e8d blob + b0e1bb7bcc396fdb25eabf4910c07d7bdb08d85f --- got/got.1 +++ got/got.1 @@ -1725,7 +1725,7 @@ option to be used as well. .El .It Xo .Cm tag -.Op Fl lVv +.Op Fl lSVv .Op Fl c Ar commit .Op Fl m Ar message .Op Fl r Ar repository-path @@ -1821,6 +1821,12 @@ working directory. If this directory is a .Nm work tree, use the repository path associated with this work tree. +.It Fl S +Display a short one-line summary of each tag, instead of the default +history format. +Can only be used with the +.Fl l +option. .It Fl s Ar signer-id While creating a new tag, sign this tag with the identity given in .Ar signer-id . blob - a24af714d90145206eec64eed5b494e9d1436a82 blob + 421c4578e51ad094ff09fb718aecf10be5d5d7b0 --- got/got.c +++ got/got.c @@ -7473,7 +7473,7 @@ done: __dead static void usage_tag(void) { - fprintf(stderr, "usage: %s tag [-lVv] [-c commit] [-m message] " + fprintf(stderr, "usage: %s tag [-lSVv] [-c commit] [-m message] " "[-r repository-path] [-s signer-id] name\n", getprogname()); exit(1); } @@ -7554,6 +7554,46 @@ get_tag_refname(char **refname, const char *tag_name) } static const struct got_error * +print_tag_oneline(struct got_tag_object *tag, struct got_commit_object *commit, + const char *refname, const char *refstr, time_t tagger_time) +{ + static const struct got_error *err = NULL; + struct tm tm; + char *tagmsg0 = NULL, *tagmsg, *nl; + char datebuf[11]; + + if (gmtime_r(&tagger_time, &tm) == NULL) + return got_error_from_errno("gmtime_r"); + if (strftime(datebuf, sizeof(datebuf), "%F", &tm) == 0) + return got_error(GOT_ERR_NO_SPACE); + + if (commit) { + err = got_object_commit_get_logmsg(&tagmsg0, commit); + if (err) + return err; + got_object_commit_close(commit); + } else { + tagmsg0 = strdup(got_object_tag_get_message(tag)); + got_object_tag_close(tag); + if (tagmsg0 == NULL) + return got_error_from_errno("strdup"); + } + + tagmsg = tagmsg0; + while (isspace((unsigned char)*tagmsg)) + tagmsg++; + nl = strchr(tagmsg, '\n'); + if (nl) + *nl = '\0'; + + printf("%s %s %.7s %s\n", datebuf, refname, refstr, tagmsg); + + free(tagmsg0); + + return err; +} + +static const struct got_error * print_tag(struct got_tag_object *tag, struct got_commit_object *commit, const char *refname, const char *refstr, const char *tagger, time_t tagger_time, const char *id_str, const char *ssh_sig, @@ -7626,7 +7666,8 @@ print_tag(struct got_tag_object *tag, struct got_commi static const struct got_error * list_tags(struct got_repository *repo, const char *tag_name, int verify_tags, - const char *allowed_signers, const char *revoked_signers, int verbosity) + const char *allowed_signers, const char *revoked_signers, int verbosity, + int oneline) { static const struct got_error *err = NULL; struct got_reflist_head refs; @@ -7717,9 +7758,13 @@ list_tags(struct got_repository *repo, const char *tag } } - err = print_tag(tag, commit, refname, refstr, tagger, - tagger_time, id_str, ssh_sig, allowed_signers, - revoked_signers, verbosity, &bad_sigs); + if (oneline) + err = print_tag_oneline(tag, commit, refname, refstr, + tagger_time); + else + err = print_tag(tag, commit, refname, refstr, tagger, + tagger_time, id_str, ssh_sig, allowed_signers, + revoked_signers, verbosity, &bad_sigs); free(id_str); free(refstr); @@ -7908,7 +7953,7 @@ cmd_tag(int argc, char *argv[]) char *allowed_signers = NULL, *revoked_signers = NULL, *editor = NULL; const char *signer_id = NULL; const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL; - int ch, do_list = 0, verify_tags = 0, verbosity = 0; + int ch, do_list = 0, oneline = 0, verify_tags = 0, verbosity = 0; int *pack_fds = NULL; #ifndef PROFILE @@ -7917,7 +7962,7 @@ cmd_tag(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "c:lm:r:s:Vv")) != -1) { + while ((ch = getopt(argc, argv, "c:lm:r:Ss:Vv")) != -1) { switch (ch) { case 'c': commit_id_arg = optarg; @@ -7937,6 +7982,9 @@ cmd_tag(int argc, char *argv[]) } got_path_strip_trailing_slashes(repo_path); break; + case 'S': + oneline = 1; + break; case 's': signer_id = optarg; break; @@ -7958,7 +8006,9 @@ cmd_tag(int argc, char *argv[]) argc -= optind; argv += optind; - if (do_list || verify_tags) { + if (oneline && !do_list) + errx(1, "-S option con only be used when listing tags"); + else if (do_list || verify_tags) { if (commit_id_arg != NULL) errx(1, "-c option can only be used when creating a tag"); @@ -8048,7 +8098,7 @@ cmd_tag(int argc, char *argv[]) if (error) goto done; error = list_tags(repo, tag_name, verify_tags, allowed_signers, - revoked_signers, verbosity); + revoked_signers, verbosity, oneline); } else { error = get_gitconfig_path(&gitconfig_path); if (error) blob - 005a6304020f8cb434ecb6fb450d1b44d9bcc66e blob + 8b1676b2da3337a7015d4d611342f241a374b52a --- regress/cmdline/tag.sh +++ regress/cmdline/tag.sh @@ -223,6 +223,60 @@ test_tag_list() { test_done "$testroot" "$ret" } +test_tag_list_oneline() { + local testroot=`test_init tag_list` + local commit_id=`git_show_head $testroot/repo` + local tag=1.0.0 + local tag2=2.0.0 + + # create tag with Git + git -C $testroot/repo tag -a -m 'test' $tag + # create tag with Got + (cd $testroot/repo && got tag -m 'test' $tag2 > /dev/null) + + tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + local tagger_time=`git_show_tagger_time $testroot/repo $tag` + d1=`date -u -r $tagger_time +"%F"` + tag_id2=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag2" | tr -d ' ' | cut -d: -f2` + local tagger_time2=`git_show_tagger_time $testroot/repo $tag2` + d2=`date -u -r $tagger_time2 +"%F"` + + got tag -r $testroot/repo -lS > $testroot/stdout + + echo "$d2 $tag2 $(trim_obj_id 7 "$tag_id2") test" > $testroot/stdout.expected + echo "$d1 $tag $(trim_obj_id 7 "$tag_id") test" >> $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got tag -r $testroot/repo -lS $tag > $testroot/stdout + + echo "$d1 $tag $(trim_obj_id 7 "$tag_id") test" > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got tag -r $testroot/repo -lS $tag2 > $testroot/stdout + + echo "$d2 $tag2 $(trim_obj_id 7 "$tag_id2") test" > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + test_tag_list_lightweight() { local testroot=`test_init tag_list_lightweight` local commit_id=`git_show_head $testroot/repo` @@ -573,6 +627,7 @@ test_parseargs "$@" run_test test_tag_create run_test test_tag_list run_test test_tag_list_lightweight +run_test test_tag_list_oneline run_test test_tag_create_ssh_signed run_test test_tag_create_ssh_signed_missing_key run_test test_tag_commit_keywords