From: "Kristian Høgsberg" <krh@redhat.com>
To: git@vger.kernel.org
Cc: "Kristian Høgsberg" <krh@redhat.com>
Subject: [PATCH] Port git-tag.sh to C.
Date: Fri, 8 Jun 2007 18:45:59 -0400 [thread overview]
Message-ID: <11813427591137-git-send-email-krh@redhat.com> (raw)
From: Kristian Høgsberg <krh@redhat.com>
A more or less straight-forward port of git-tag.sh to C.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Cc: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
Ok, here's an updated version that passes the test suite. Johannes, I
leave it to you and jasam to merge the bits you find useful, but as
far as I see it, this conversion is complete, and there's enough other
shell scripts to port. My port doesn't pass jasam's test suite, it
looks like he is expecting the -l glob to be a regexp, but the
git-tag.sh I started from used shell globs.
Anyways, it'd be nice if you or jasam could keep the list a little
more in the loop with the SoC changes, it is where most of the
development happens, after all. What's next on your list?
cheers,
Kristian
Makefile | 3 +-
builtin-tag.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
builtin.h | 1 +
git-tag.sh | 183 ----------------------------
git.c | 1 +
5 files changed, 375 insertions(+), 184 deletions(-)
create mode 100644 builtin-tag.c
delete mode 100755 git-tag.sh
diff --git a/Makefile b/Makefile
index 0f75955..bb1bed1 100644
--- a/Makefile
+++ b/Makefile
@@ -205,7 +205,7 @@ SCRIPT_SH = \
git-pull.sh git-rebase.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 \
@@ -372,6 +372,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 \
diff --git a/builtin-tag.c b/builtin-tag.c
new file mode 100644
index 0000000..26035f5
--- /dev/null
+++ b/builtin-tag.c
@@ -0,0 +1,371 @@
+/*
+ * Builtin "git tag"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "refs.h"
+#include "commit.h"
+#include "builtin.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 launch_editor(const char *path, const char *template,
+ char *buffer, size_t size)
+{
+ struct child_process child;
+ const char *editor;
+ const char *args[3];
+ char *eol;
+ int len, fd, blank_lines, i, j;
+
+ fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd < 0)
+ die("could not create file %s.", path);
+
+ len = strlen(template);
+ write_or_die(fd, template, len);
+ close(fd);
+
+ editor = getenv("VISUAL");
+ if (!editor)
+ editor = getenv("EDITOR");
+ 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);
+
+ fd = open(path, O_RDONLY, 0644);
+ if (fd == -1)
+ die("could not read %s.", path);
+ len = read_in_full(fd, buffer, size);
+ if (len < 0)
+ die("failed to read '%s', %m", path);
+ close(fd);
+
+ blank_lines = 1;
+ for (i = 0, j = 0; i < len; i++) {
+ if (blank_lines > 0 && buffer[i] == '#') {
+ eol = strchr(buffer + i, '\n');
+ if (!eol)
+ break;
+
+ i = eol - buffer;
+ continue;
+ }
+
+ if (buffer[i] == '\n') {
+ blank_lines++;
+ if (blank_lines > 1)
+ continue;
+ } else {
+ if (blank_lines > 2)
+ buffer[j++] = '\n';
+ blank_lines = 0;
+ }
+
+ buffer[j++] = buffer[i];
+ }
+
+ if (buffer[j - 1] != '\n')
+ buffer[j++] = '\n';
+
+ unlink(path);
+
+ return j;
+}
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+ int flag, void *cb_data)
+{
+ const char *pattern = cb_data;
+
+ if (pattern == NULL || !fnmatch(pattern, refname, 0))
+ printf("%s\n", refname);
+
+ return 0;
+}
+
+static int list_tags(const char *pattern)
+{
+ for_each_tag_ref(show_reference, (void *) pattern);
+
+ return 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);
+ if (!resolve_ref(ref, sha1, 1, NULL)) {
+ fprintf(stderr, "tag '%s' not found.\n", *p);
+ had_error = 1;
+ continue;
+ }
+
+ if (!delete_ref(ref, sha1))
+ printf("Deleted tag '%s'\n", *p);
+ }
+
+ return had_error;
+}
+
+static int verify_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);
+
+ if (!resolve_ref(ref, sha1, 1, NULL)) {
+ fprintf(stderr, "tag '%s' not found.\n", *p);
+ had_error = 1;
+ continue;
+ }
+
+ printf("FIXME: verify tag '%s'\n", *p);
+ }
+
+ return had_error;
+}
+
+static int do_sign(char *buffer, size_t size, size_t max)
+{
+ struct child_process gpg;
+ const char *args[5];
+ char *bracket;
+ int len;
+
+ if (signingkey[0] == '\0') {
+ strlcpy(signingkey, git_committer_info(1), sizeof signingkey);
+ bracket = strchr(signingkey, '>');
+ if (bracket)
+ bracket[1] = '\0';
+ }
+
+ memset(&gpg, 0, sizeof(gpg));
+ gpg.argv = args;
+ gpg.in = -1;
+ gpg.out = -1;
+ args[0] = "gpg";
+ args[1] = "-bsa";
+ args[2] = "-u";
+ args[3] = signingkey;
+ args[4] = 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);
+
+ finish_command(&gpg);
+
+ return size + len;
+}
+
+static const char tag_template[] =
+ "\n"
+ "#\n"
+ "# Write a tag message\n"
+ "#\n";
+
+static int git_tag_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "user.signingkey")) {
+ if (!value)
+ die("user.signingkey without value");
+ strlcpy(signingkey, value, sizeof signingkey);
+ return 0;
+ }
+
+ return git_default_config(var, value);
+}
+
+static void create_tag(const unsigned char *object, const char *tag,
+ const char *message, int sign, unsigned char *result)
+{
+ enum object_type type;
+ char buffer[4096];
+ int header, body, total;
+
+ type = sha1_object_info(object, NULL);
+ if (type <= 0)
+ die("bad object type.");
+
+ header = snprintf(buffer, sizeof buffer,
+ "object %s\n"
+ "type %s\n"
+ "tag %s\n"
+ "tagger %s\n\n",
+ sha1_to_hex(object),
+ typename(type),
+ tag,
+ git_committer_info(1));
+
+ if (message == NULL)
+ body = launch_editor(git_path("TAGMSG"), tag_template,
+ buffer + header, sizeof buffer - header);
+ else
+ body = snprintf(buffer + header, sizeof buffer - header,
+ "%s\n", message);
+
+ if (body == 0)
+ die("no tag message?");
+
+ if (header + body > sizeof buffer)
+ die("tag message too big.");
+
+ if (sign)
+ total = do_sign(buffer, header + body, sizeof buffer);
+ else
+ total = header + body;
+
+ if (write_sha1_file(buffer, total, tag_type, result) < 0)
+ die("unable to write tag file");
+}
+
+int cmd_tag(int argc, const char **argv, const char *prefix)
+{
+ unsigned char object[20], prev[20], result[20];
+ int annotate = 0, sign = 0, force = 0, lines = 0;
+ const char *message = NULL;
+ char ref[PATH_MAX];
+ const char *object_ref, *tag;
+ int i, fd;
+ struct ref_lock *lock;
+
+ git_config(git_tag_config);
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-')
+ break;
+ if (!strcmp(arg, "-a")) {
+ annotate = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-s")) {
+ annotate = 1;
+ sign = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-f")) {
+ force = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-n")) {
+ if (i + 1 == argc || *argv[i + 1] == '-')
+ /* no argument */
+ lines = 1;
+ else
+ /* FIXME, fallback to 1 on invalid integer */
+ lines = atoi(argv[i + 1]);
+ continue;
+ }
+ if (!strcmp(arg, "-m")) {
+ annotate = 1;
+ i++;
+ if (i == argc)
+ die("option -m needs an argument.");
+ message = argv[i];
+ continue;
+ }
+ if (!strcmp(arg, "-F")) {
+ 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]);
+
+ message = xmalloc(4096);
+ if (read_in_full(fd, (char *) message, 4096) < 0)
+ die("cannot read %s", argv[1]);
+ continue;
+ }
+ 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;
+ }
+ if (!strcmp(arg, "-l")) {
+ return list_tags(argv[i + 1]);
+ }
+ if (!strcmp(arg, "-d")) {
+ return delete_tags(argv + i + 1);
+ }
+ if (!strcmp(arg, "-v")) {
+ return verify_tags(argv + i + 1);
+ }
+ usage(builtin_tag_usage);
+ }
+
+ if (i == argc)
+ return list_tags(NULL);
+ tag = argv[i++];
+
+ if (i < argc)
+ object_ref = argv[i];
+ else
+ object_ref = "HEAD";
+
+ if (get_sha1(object_ref, object))
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
+
+ if (snprintf(ref, sizeof ref, "refs/tags/%s", tag) > sizeof ref)
+ die("tag '%s' too long.", tag);
+ if (check_ref_format(ref))
+ die("'%s' is not a valid tag name.", tag);
+ if (resolve_ref(ref, prev, 1, NULL)) {
+ if (!force)
+ die("tag '%s' already exists", tag);
+ } else {
+ hashclr(prev);
+ }
+
+ if (annotate)
+ create_tag(object, tag, message, sign, object);
+
+ lock = lock_any_ref_for_update(ref, prev, 0);
+ if (!lock)
+ die("%s: cannot lock the ref", ref);
+ if (write_ref_sha1(lock, object, NULL) < 0)
+ die("%s: cannot update the ref", ref);
+
+ return 0;
+}
diff --git a/builtin.h b/builtin.h
index 39290d1..91166e1 100644
--- a/builtin.h
+++ b/builtin.h
@@ -72,6 +72,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);
diff --git a/git-tag.sh b/git-tag.sh
deleted file mode 100755
index 37cee97..0000000
--- a/git-tag.sh
+++ /dev/null
@@ -1,183 +0,0 @@
-#!/bin/sh
-# Copyright (c) 2005 Linus Torvalds
-
-USAGE='[-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
-SUBDIRECTORY_OK='Yes'
-. git-sh-setup
-
-message_given=
-annotate=
-signed=
-force=
-message=
-username=
-list=
-verify=
-LINES=0
-while case "$#" in 0) break ;; esac
-do
- case "$1" in
- -a)
- annotate=1
- ;;
- -s)
- annotate=1
- signed=1
- ;;
- -f)
- force=1
- ;;
- -n)
- case $2 in
- -*) LINES=1 # no argument
- ;;
- *) shift
- LINES=$(expr "$1" : '\([0-9]*\)')
- [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used
- ;;
- esac
- ;;
- -l)
- list=1
- shift
- PATTERN="$1" # select tags by shell pattern, not re
- git rev-parse --symbolic --tags | sort |
- while read TAG
- do
- case "$TAG" in
- *$PATTERN*) ;;
- *) continue ;;
- esac
- [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;}
- OBJTYPE=$(git cat-file -t "$TAG")
- case $OBJTYPE in
- tag) ANNOTATION=$(git cat-file tag "$TAG" |
- sed -e '1,/^$/d' \
- -e '/^-----BEGIN PGP SIGNATURE-----$/Q' )
- printf "%-15s %s\n" "$TAG" "$ANNOTATION" |
- sed -e '2,$s/^/ /' \
- -e "${LINES}q"
- ;;
- *) echo "$TAG"
- ;;
- esac
- done
- ;;
- -m)
- annotate=1
- shift
- message="$1"
- if test "$#" = "0"; then
- die "error: option -m needs an argument"
- else
- message_given=1
- fi
- ;;
- -F)
- annotate=1
- shift
- if test "$#" = "0"; then
- die "error: option -F needs an argument"
- else
- message="$(cat "$1")"
- message_given=1
- fi
- ;;
- -u)
- annotate=1
- signed=1
- shift
- username="$1"
- ;;
- -d)
- shift
- had_error=0
- for tag
- do
- cur=$(git-show-ref --verify --hash -- "refs/tags/$tag") || {
- echo >&2 "Seriously, what tag are you talking about?"
- had_error=1
- continue
- }
- git-update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || {
- had_error=1
- continue
- }
- echo "Deleted tag $tag."
- done
- exit $had_error
- ;;
- -v)
- shift
- tag_name="$1"
- tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") ||
- die "Seriously, what tag are you talking about?"
- git-verify-tag -v "$tag"
- exit $?
- ;;
- -*)
- usage
- ;;
- *)
- break
- ;;
- esac
- shift
-done
-
-[ -n "$list" ] && exit 0
-
-name="$1"
-[ "$name" ] || usage
-prev=0000000000000000000000000000000000000000
-if git-show-ref --verify --quiet -- "refs/tags/$name"
-then
- test -n "$force" || die "tag '$name' already exists"
- prev=`git rev-parse "refs/tags/$name"`
-fi
-shift
-git-check-ref-format "tags/$name" ||
- die "we do not like '$name' as a tag name."
-
-object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
-type=$(git-cat-file -t $object) || exit 1
-tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
-
-test -n "$username" ||
- username=$(git-repo-config user.signingkey) ||
- username=$(expr "z$tagger" : 'z\(.*>\)')
-
-trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0
-
-if [ "$annotate" ]; then
- if [ -z "$message_given" ]; then
- ( echo "#"
- echo "# Write a tag message"
- echo "#" ) > "$GIT_DIR"/TAG_EDITMSG
- ${VISUAL:-${EDITOR:-vi}} "$GIT_DIR"/TAG_EDITMSG || exit
- else
- printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG
- fi
-
- grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG |
- git-stripspace >"$GIT_DIR"/TAG_FINALMSG
-
- [ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || {
- echo >&2 "No tag message?"
- exit 1
- }
-
- ( printf 'object %s\ntype %s\ntag %s\ntagger %s\n\n' \
- "$object" "$type" "$name" "$tagger";
- cat "$GIT_DIR"/TAG_FINALMSG ) >"$GIT_DIR"/TAG_TMP
- rm -f "$GIT_DIR"/TAG_TMP.asc "$GIT_DIR"/TAG_FINALMSG
- if [ "$signed" ]; then
- gpg -bsa -u "$username" "$GIT_DIR"/TAG_TMP &&
- cat "$GIT_DIR"/TAG_TMP.asc >>"$GIT_DIR"/TAG_TMP ||
- die "failed to sign the tag with GPG."
- fi
- object=$(git-mktag < "$GIT_DIR"/TAG_TMP)
-fi
-
-git update-ref "refs/tags/$name" "$object" "$prev"
-
diff --git a/git.c b/git.c
index 29b55a1..c9c20fb 100644
--- a/git.c
+++ b/git.c
@@ -285,6 +285,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "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 },
--
1.5.0.6
next reply other threads:[~2007-06-08 22:46 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-06-08 22:45 Kristian Høgsberg [this message]
2007-06-09 1:58 ` [PATCH] Port git-tag.sh to C Carlos Rica
2007-06-12 3:28 ` Daniel Barkalow
2007-06-12 12:41 ` git-fetch, was " Johannes Schindelin
2007-06-12 13:29 ` Julian Phillips
2007-06-12 16:51 ` Daniel Barkalow
2007-06-12 23:55 ` Daniel Barkalow
2007-06-13 0:27 ` Johannes Schindelin
2007-06-09 18:26 ` Junio C Hamano
2007-06-09 23:27 ` Kristian Høgsberg
2007-06-10 12:37 ` Robin Rosenberg
2007-06-10 21:36 ` Junio C Hamano
2007-06-10 22:13 ` Jeff King
2007-06-10 22:19 ` Junio C Hamano
2007-06-10 22:22 ` Jeff King
-- strict thread matches above, loose matches on Subject: below --
2007-06-08 21:38 Kristian Høgsberg
2007-06-08 21:51 ` Johannes Schindelin
2007-06-08 22:05 ` Kristian Høgsberg
2007-06-08 22:07 ` Johannes Schindelin
2007-06-08 22:36 ` Carlos Rica
2007-06-08 22:39 ` Matthijs Melchior
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=11813427591137-git-send-email-krh@redhat.com \
--to=krh@redhat.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.