{"thread":{"id":"5576","subject":"Add \"git show-ref\" builtin command","startedAt":"2006-09-15T18:19:32Z","lastAt":"2006-09-16T09:33:39Z","messageCount":7,"participants":["Linus Torvalds","Junio C Hamano","Jakub Narebski"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"26981","messageId":"Pine.LNX.4.64.0609151108560.4388@g5.osdl.org","threadId":"5576","inReplyTo":null,"subject":"Add \"git show-ref\" builtin command","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-15T18:19:32Z","receivedAt":"2006-09-15T18:19:32Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nIt's kind of like \"git peek-remote\", but works only locally (and thus \navoids the whole overhead of git_connect()) and has some extra \nverification features.\n\nFor example, it allows you to filter the results, and to choose whether \nyou want the tag dereferencing or not. You can also use it to just test \nwhether a particular ref exists.\n\nFor example:\n\n\tgit show-ref master\n\nwill show all references called \"master\", whether tags or heads or \nanything else, and regardless of how deep in the reference naming \nhierarchy they are (so it would show \"refs/heads/master\" but also \n\"refs/remote/other-repo/master\").\n\nWhen using the \"--verify\" flag, the command requires an exact ref path:\n\n\tgit show-ref --verify refs/heads/master\n\nwill only match the exact branch called \"master\".\n\nIf nothing matches, show-ref will return an error code of 1, and in the \ncase of verification, it will show an error message.\n\nFor scripting, you can ask it to be quiet with the \"--quiet\" flag, which \nallows you to do things like\n\n\tgit-show-ref --quiet --verify -- \"refs/heads/$headname\" ||\n\t\techo \"$headname is not a valid branch\"\n\nto check whether a particular branch exists or not (notice how we don't \nactually want to show any results, and we want to use the full refname for \nit in order to not trigger the problem with ambiguous partial matches).\n\nTo show only tags, or only proper branch heads, use \"--tags\" and/or \n\"--heads\" respectively (using both means that it shows tags _and_ heads, \nbut not other random references under the refs/ subdirectory).\n\nTo do automatic tag object dereferencing, use the \"-d\" or \"--dereference\" \nflag, so you can do\n\n\tgit show-ref --tags --dereference\n\nto get a listing of all tags together with what they dereference.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nNOTE! This is against the current master branch, and does _not_ depend on \nmy other ref-work. In fact, I'd argue that this is much better to merge \nfirst and independently of the packed references branch, because it allows \nus to much more easily update the scripts etc to be totally unaware of how \nthe refs are actually stored.\n\nI've given it some basic testing, and it all seems obvious enough.\n\ndiff --git a/Makefile b/Makefile\nindex 7b3114f..c365138 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -295,7 +295,8 @@ BUILTIN_OBJS = \\\n \tbuiltin-upload-tar.o \\\n \tbuiltin-verify-pack.o \\\n \tbuiltin-write-tree.o \\\n-\tbuiltin-zip-tree.o\n+\tbuiltin-zip-tree.o \\\n+\tbuiltin-show-ref.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-show-ref.c b/builtin-show-ref.c\nnew file mode 100644\nindex 0000000..161b236\n--- /dev/null\n+++ b/builtin-show-ref.c\n@@ -0,0 +1,112 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"object.h\"\n+#include \"tag.h\"\n+\n+static const char show_ref_usage[] = \"git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--deref] [--tags] [--heads] [--] [pattern*]\";\n+\n+static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0, found_match = 0, verify = 0, quiet = 0;\n+static const char **pattern;\n+\n+static int show_ref(const char *refname, const unsigned char *sha1)\n+{\n+\tstruct object *obj;\n+\n+\tif (tags_only || heads_only) {\n+\t\tint match;\n+\n+\t\tmatch = heads_only && !strncmp(refname, \"refs/heads/\", 11);\n+\t\tmatch |= tags_only && !strncmp(refname, \"refs/tags/\", 10);\n+\t\tif (!match)\n+\t\t\treturn 0;\n+\t}\n+\tif (pattern) {\n+\t\tint reflen = strlen(refname);\n+\t\tconst char **p = pattern, *m;\n+\t\twhile ((m = *p++) != NULL) {\n+\t\t\tint len = strlen(m);\n+\t\t\tif (len > reflen)\n+\t\t\t\tcontinue;\n+\t\t\tif (memcmp(m, refname + reflen - len, len))\n+\t\t\t\tcontinue;\n+\t\t\tif (len == reflen)\n+\t\t\t\tgoto match;\n+\t\t\t/* \"--verify\" requires an exact match */\n+\t\t\tif (verify)\n+\t\t\t\tcontinue;\n+\t\t\tif (refname[reflen - len - 1] == '/')\n+\t\t\t\tgoto match;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+match:\n+\tfound_match++;\n+\tobj = parse_object(sha1);\n+\tif (!obj) {\n+\t\tif (quiet)\n+\t\t\treturn 0;\n+\t\tdie(\"git-show-ref: bad ref %s (%s)\", refname, sha1_to_hex(sha1));\n+\t}\n+\tif (quiet)\n+\t\treturn 0;\n+\tprintf(\"%s %s\\n\", sha1_to_hex(sha1), refname);\n+\tif (deref_tags && obj->type == OBJ_TAG) {\n+\t\tobj = deref_tag(obj, refname, 0);\n+\t\tprintf(\"%s %s^{}\\n\", sha1_to_hex(obj->sha1), refname);\n+\t}\n+\treturn 0;\n+}\n+\n+int cmd_show_ref(int argc, const char **argv, const char *prefix)\n+{\n+\tint i;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tif (*arg != '-') {\n+\t\t\tpattern = argv + i;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\tpattern = argv + i + 1;\n+\t\t\tif (!*pattern)\n+\t\t\t\tpattern = NULL;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-q\") || !strcmp(arg, \"--quiet\")) {\n+\t\t\tquiet = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-h\") || !strcmp(arg, \"--head\")) {\n+\t\t\tshow_head = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-d\") || !strcmp(arg, \"--dereference\")) {\n+\t\t\tderef_tags = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--verify\")) {\n+\t\t\tverify = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--tags\")) {\n+\t\t\ttags_only = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--heads\")) {\n+\t\t\theads_only = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tusage(show_ref_usage);\n+\t}\n+\tif (show_head)\n+\t\thead_ref(show_ref);\n+\tfor_each_ref(show_ref);\n+\tif (!found_match) {\n+\t\tif (verify && !quiet)\n+\t\t\tdie(\"No match\");\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 25431d7..bca5f79 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -61,5 +61,6 @@ extern int cmd_version(int argc, const c\n extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);\n extern int cmd_write_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n+extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n \n #endif\ndiff --git a/git.c b/git.c\nindex 8c182a5..fedd536 100644\n--- a/git.c\n+++ b/git.c\n@@ -266,6 +266,7 @@ static void handle_internal_command(int \n \t\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | USE_PAGER },\n \t\t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n \t\t{ \"verify-pack\", cmd_verify_pack },\n+\t\t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t};\n \tint i;\n \n"},{"id":"26984","messageId":"7vmz90g80m.fsf@assigned-by-dhcp.cox.net","threadId":"5576","inReplyTo":"Pine.LNX.4.64.0609151108560.4388@g5.osdl.org","subject":"Re: Add \"git show-ref\" builtin command","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-15T21:00:25Z","receivedAt":"2006-09-15T21:00:25Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> It's kind of like \"git peek-remote\", but works only locally (and thus \n> avoids the whole overhead of git_connect()) and has some extra \n> verification features.\n>...\n> NOTE! This is against the current master branch, and does _not_ depend on \n> my other ref-work. In fact, I'd argue that this is much better to merge \n> first and independently of the packed references branch, because it allows \n> us to much more easily update the scripts etc to be totally unaware of how \n> the refs are actually stored.\n\nLooks sane.  Thanks.\n\nHaving said that....\n\nGaaah.  I had been doing a \"show-refs\" that does something quite\ndifferent from this X-<.\n\nI'll rename it; I think the new name makes more sense anyway.\n\n-- >8 --\nFrom: Junio C Hamano <junkio@cox.net>\nDate: Fri, 15 Sep 2006 13:30:02 -0700\nSubject: [PATCH] Add git-for-each-ref: helper for language bindings\n\nThis adds a new command, git-for-each-ref.  You can have it iterate\nover refs and have it output various aspects of the objects they\nrefer to.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n .gitignore                         |    1 \n Documentation/git-for-each-ref.txt |  164 +++++++\n Makefile                           |    1 \n builtin-for-each-ref.c             |  845 ++++++++++++++++++++++++++++++++++++\n builtin.h                          |    3 \n git.c                              |    1 \n object.c                           |   68 ++-\n object.h                           |    6 \n quote.c                            |   38 ++\n quote.h                            |    4 \n 10 files changed, 1103 insertions(+), 28 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 0d608fe..0b08f37 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -36,6 +36,7 @@ git-fetch\n git-fetch-pack\n git-findtags\n git-fmt-merge-msg\n+git-for-each-ref\n git-format-patch\n git-fsck-objects\n git-get-tar-commit-id\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nnew file mode 100644\nindex 0000000..74dd735\n--- /dev/null\n+++ b/Documentation/git-for-each-ref.txt\n@@ -0,0 +1,164 @@\n+git-for-each-ref(1)\n+===================\n+\n+NAME\n+----\n+git-for-each-ref - Output information on each ref\n+\n+SYNOPSIS\n+--------\n+'git-for-each-ref' [--count=<count>]* [--shell|--perl|--python] [--sort=<key>]* [--format=<format>] [<pattern>]\n+\n+DESCRIPTION\n+-----------\n+\n+Iterate over all refs that match `<pattern>` and show them\n+according to the given `<format>`, after sorting them according\n+to the given `<sortkeys>`.  If `<max>` is given, stop after\n+showing that many refs.  The interporated values in `<format>`\n+can optionally be quoted as string literals in the specified\n+host language.\n+\n+OPTIONS\n+-------\n+<count>::\n+\tBy default the command shows all refs that match\n+\t`<pattern>`.  This option makes it stop after showing\n+\tthat many refs.\n+\n+<key>::\n+\tA field name to sort on.  Prefix `-` to sort in\n+\tdescending order of the value.  When unspecified,\n+\t`refname` is used.  More than one sort keys can be\n+\tgiven.\n+\n+<format>::\n+\tA string that interpolates `%(fieldname)` from the\n+\tobject pointed at by a ref being shown.  If `fieldname`\n+\tis prefixed with an asterisk (`*`) and the ref points\n+\tat a tag object, the value for the field in the object\n+\ttag refers is used.  When unspecified, defaults to\n+\t`%(refname)`.\n+\n+<pattern>::\n+\tIf given, the name of the ref is matched against this\n+\tusing fnmatch(3).  Refs that do not match the pattern\n+\tare not shown.\n+\n+--shell, --perl, --python::\n+\tIf given, strings that substitute `%(fieldname)`\n+\tplaceholders are quoted as string literals suitable for\n+\tthe specified host language.  This is meant to produce\n+\ta scriptlet that can directly be `eval`ed.\n+\n+\n+FIELD NAMES\n+-----------\n+\n+Various values from structured fields in referenced objects can\n+be used to interpolate into the resulting output, or as sort\n+keys.\n+\n+For all objects, the following names can be used:\n+\n+refname::\n+\tThe name of the ref (the part after $GIT_DIR/refs/).\n+\n+objecttype::\n+\tThe type of the object (`blob`, `tree`, `commit`, `tag`).\n+\n+objectsize::\n+\tThe size of the object (the same as `git-cat-file -s` reports).\n+\n+objectname::\n+\tThe object name (aka SHA-1).\n+\n+In addition to the above, for commit and tag objects, the header\n+field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n+be used to specify the value in the header field.\n+\n+Fields that have name-email-date tuple as its value (`author`,\n+`committer`, and `tagger`) can be suffixed with `name`, `email`,\n+and `date` to extract the named component.\n+\n+The first line of the message in a commit and tag object is\n+`subject`, the remaining lines are `body`.  The whole message\n+is `contents`.\n+\n+For sorting purposes, fields with numeric values sort in numeric\n+order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).\n+All other fields are used to sort in their byte-value order.\n+\n+In any case, a field name that refers to a field inapplicable to\n+the object referred by the ref does not cause an error.  It\n+returns an empty string instead.\n+\n+\n+EXAMPLES\n+--------\n+\n+Show the most recent 3 tagged commits::\n+\n+------------\n+#!/bin/sh\n+\n+git-for-each-ref --count=3 --sort='-*authordate' \\\n+--format='From: %(*authorname) %(*authoremail)\n+Subject: %(*subject)\n+Date: %(*authordate)\n+Ref: %(*refname)\n+\n+%(*body)\n+' 'refs/tags'\n+------------\n+\n+A bit more elaborate report on tags::\n+------------\n+#!/bin/sh\n+\n+fmt='\n+\tr=%(refname)\n+\tt=%(*objecttype)\n+\tT=${r#refs/tags/}\n+\n+\to=%(*objectname)\n+\tn=%(*authorname)\n+\te=%(*authoremail)\n+\ts=%(*subject)\n+\td=%(*authordate)\n+\tb=%(*body)\n+\n+\tkind=Tag\n+\tif test \"z$t\" = z\n+\tthen\n+\t\t# could be a lightweight tag\n+\t\tt=%(objecttype)\n+\t\tkind=\"Lightweight tag\"\n+\t\to=%(objectname)\n+\t\tn=%(authorname)\n+\t\te=%(authoremail)\n+\t\ts=%(subject)\n+\t\td=%(authordate)\n+\t\tb=%(body)\n+\tfi\n+\techo \"$kind $T points at a $t object $o\"\n+\tif test \"z$t\" = zcommit\n+\tthen\n+\t\techo \"The commit was authored by $n $e\n+at $d, and titled\n+\n+    $s\n+\n+Its message reads as:\n+\"\n+\t\techo \"$b\" | sed -e \"s/^/    /\"\n+\t\techo\n+\tfi\n+'\n+\n+eval=`git-for-each-ref -s --format=\"$fmt\" \\\n+\t--sort='*objecttype' \\\n+\t--sort=-taggerdate \\\n+\trefs/tags`\n+eval \"$eval\"\n+------------\ndiff --git a/Makefile b/Makefile\nindex 7b3114f..f0e2e51 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -267,6 +267,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-diff-stages.o \\\n \tbuiltin-diff-tree.o \\\n \tbuiltin-fmt-merge-msg.o \\\n+\tbuiltin-for-each-ref.o \\\n \tbuiltin-grep.o \\\n \tbuiltin-init-db.o \\\n \tbuiltin-log.o \\\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nnew file mode 100644\nindex 0000000..f064e7e\n--- /dev/null\n+++ b/builtin-for-each-ref.c\n@@ -0,0 +1,845 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"object.h\"\n+#include \"tag.h\"\n+#include \"commit.h\"\n+#include \"tree.h\"\n+#include \"blob.h\"\n+#include \"quote.h\"\n+#include <fnmatch.h>\n+\n+/* Quoting styles */\n+#define QUOTE_NONE 0\n+#define QUOTE_SHELL 1\n+#define QUOTE_PERL 2\n+#define QUOTE_PYTHON 3\n+\n+typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n+\n+struct atom_value {\n+\tconst char *s;\n+\tunsigned long ul; /* used for sorting when not FIELD_STR */\n+};\n+\n+struct ref_sort {\n+\tstruct ref_sort *next;\n+\tint atom; /* index into used_atom array */\n+\tunsigned reverse : 1;\n+};\n+\n+struct refinfo {\n+\tchar *refname;\n+\tunsigned char objectname[20];\n+\tstruct atom_value *value;\n+};\n+\n+static struct {\n+\tconst char *name;\n+\tcmp_type cmp_type;\n+} valid_atom[] = {\n+\t{ \"refname\" },\n+\t{ \"objecttype\" },\n+\t{ \"objectsize\", FIELD_ULONG },\n+\t{ \"objectname\" },\n+\t{ \"tree\" },\n+\t{ \"parent\" }, /* NEEDSWORK: how to address 2nd and later parents? */\n+\t{ \"numparent\", FIELD_ULONG },\n+\t{ \"object\" },\n+\t{ \"type\" },\n+\t{ \"tag\" },\n+\t{ \"author\" },\n+\t{ \"authorname\" },\n+\t{ \"authoremail\" },\n+\t{ \"authordate\", FIELD_TIME },\n+\t{ \"committer\" },\n+\t{ \"committername\" },\n+\t{ \"committeremail\" },\n+\t{ \"committerdate\", FIELD_TIME },\n+\t{ \"tagger\" },\n+\t{ \"taggername\" },\n+\t{ \"taggeremail\" },\n+\t{ \"taggerdate\", FIELD_TIME },\n+\t{ \"subject\" },\n+\t{ \"body\" },\n+\t{ \"contents\" },\n+};\n+\n+/*\n+ * An atom is a valid field atom listed above, possibly prefixed with\n+ * a \"*\" to denote deref_tag().\n+ *\n+ * We parse given format string and sort specifiers, and make a list\n+ * of properties that we need to extract out of objects.  refinfo\n+ * structure will hold an array of values extracted that can be\n+ * indexed with the \"atom number\", which is an index into this\n+ * array.\n+ */\n+static const char **used_atom;\n+static cmp_type *used_atom_type;\n+static int used_atom_cnt, sort_atom_limit, need_tagged;\n+\n+/*\n+ * Used to parse format string and sort specifiers\n+ */\n+static int parse_atom(const char *atom, const char *ep)\n+{\n+\tconst char *sp;\n+\tchar *n;\n+\tint i, at;\n+\n+\tsp = atom;\n+\tif (*sp == '*' && sp < ep)\n+\t\tsp++; /* deref */\n+\tif (ep <= sp)\n+\t\tdie(\"malformed field name: %.*s\", (int)(ep-atom), atom);\n+\n+\t/* Do we have the atom already used elsewhere? */\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tint len = strlen(used_atom[i]);\n+\t\tif (len == ep - atom && !memcmp(used_atom[i], atom, len))\n+\t\t\treturn i;\n+\t}\n+\n+\t/* Is the atom a valid one? */\n+\tfor (i = 0; i < ARRAY_SIZE(valid_atom); i++) {\n+\t\tint len = strlen(valid_atom[i].name);\n+\t\tif (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (ARRAY_SIZE(valid_atom) <= i)\n+\t\tdie(\"unknown field name: %.*s\", (int)(ep-atom), atom);\n+\n+\t/* Add it in, including the deref prefix */\n+\tat = used_atom_cnt;\n+\tused_atom_cnt++;\n+\tused_atom = xrealloc(used_atom,\n+\t\t\t     (sizeof *used_atom) * used_atom_cnt);\n+\tused_atom_type = xrealloc(used_atom_type,\n+\t\t\t\t  (sizeof(*used_atom_type) * used_atom_cnt));\n+\tn = xmalloc(ep - atom + 1);\n+\tmemcpy(n, atom, ep - atom);\n+\tn[ep-atom] = 0;\n+\tused_atom[at] = n;\n+\tused_atom_type[at] = valid_atom[i].cmp_type;\n+\treturn at;\n+}\n+\n+/*\n+ * In a format string, find the next occurrence of %(atom).\n+ */\n+static const char *find_next(const char *cp)\n+{\n+\twhile (*cp) {\n+\t\tif (*cp == '%') {\n+\t\t\t/* %( is the start of an atom;\n+\t\t\t * %% is a quoteed per-cent.\n+\t\t\t */\n+\t\t\tif (cp[1] == '(')\n+\t\t\t\treturn cp;\n+\t\t\telse if (cp[1] == '%')\n+\t\t\t\tcp++; /* skip over two % */\n+\t\t\t/* otherwise this is a singleton, literal % */\n+\t\t}\n+\t\tcp++;\n+\t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Make sure the format string is well formed, and parse out\n+ * the used atoms.\n+ */\n+static void verify_format(const char *format)\n+{\n+\tconst char *cp, *sp;\n+\tfor (cp = format; *cp && (sp = find_next(cp)); ) {\n+\t\tconst char *ep = strchr(sp, ')');\n+\t\tif (!ep)\n+\t\t\tdie(\"malformatted format string %s\", sp);\n+\t\t/* sp points at \"%(\" and ep points at the closing \")\" */\n+\t\tparse_atom(sp + 2, ep);\n+\t\tcp = ep + 1;\n+\t}\n+}\n+\n+/*\n+ * Given an object name, read the object data and size, and return a\n+ * \"struct object\".  If the object data we are returning is also borrowed\n+ * by the \"struct object\" representation, set *eaten as well---it is a\n+ * signal from parse_object_buffer to us not to free the buffer.\n+ */\n+static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)\n+{\n+\tchar type[20];\n+\tvoid *buf = read_sha1_file(sha1, type, sz);\n+\n+\tif (buf)\n+\t\t*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);\n+\telse\n+\t\t*obj = NULL;\n+\treturn buf;\n+}\n+\n+/* See grab_values */\n+static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"objecttype\"))\n+\t\t\tv->s = type_names[obj->type];\n+\t\telse if (!strcmp(name, \"objectsize\")) {\n+\t\t\tchar *s = xmalloc(40);\n+\t\t\tsprintf(s, \"%lu\", sz);\n+\t\t\tv->ul = sz;\n+\t\t\tv->s = s;\n+\t\t}\n+\t\telse if (!strcmp(name, \"objectname\")) {\n+\t\t\tchar *s = xmalloc(41);\n+\t\t\tstrcpy(s, sha1_to_hex(obj->sha1));\n+\t\t\tv->s = s;\n+\t\t}\n+\t}\n+}\n+\n+/* See grab_values */\n+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tstruct tag *tag = (struct tag *) obj;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"tag\"))\n+\t\t\tv->s = tag->tag;\n+\t}\n+}\n+\n+static int num_parents(struct commit *commit)\n+{\n+\tstruct commit_list *parents;\n+\tint i;\n+\n+\tfor (i = 0, parents = commit->parents;\n+\t     parents;\n+\t     parents = parents->next)\n+\t\ti++;\n+\treturn i;\n+}\n+\n+/* See grab_values */\n+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tstruct commit *commit = (struct commit *) obj;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (!strcmp(name, \"tree\")) {\n+\t\t\tchar *s = xmalloc(41);\n+\t\t\tstrcpy(s, sha1_to_hex(commit->tree->object.sha1));\n+\t\t\tv->s = s;\n+\t\t}\n+\t\tif (!strcmp(name, \"numparent\")) {\n+\t\t\tchar *s = xmalloc(40);\n+\t\t\tsprintf(s, \"%lu\", v->ul);\n+\t\t\tv->s = s;\n+\t\t\tv->ul = num_parents(commit);\n+\t\t}\n+\t\telse if (!strcmp(name, \"parent\")) {\n+\t\t\tint num = num_parents(commit);\n+\t\t\tint i;\n+\t\t\tstruct commit_list *parents;\n+\t\t\tchar *s = xmalloc(42 * num);\n+\t\t\tv->s = s;\n+\t\t\tfor (i = 0, parents = commit->parents;\n+\t\t\t     parents;\n+\t\t\t     parents = parents->next, i = i + 42) {\n+\t\t\t\tstruct commit *parent = parents->item;\n+\t\t\t\tstrcpy(s+i, sha1_to_hex(parent->object.sha1));\n+\t\t\t\tif (parents->next)\n+\t\t\t\t\ts[i+40] = ' ';\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)\n+{\n+\tconst char *eol;\n+\twhile (*buf) {\n+\t\tif (!strncmp(buf, who, wholen) &&\n+\t\t    buf[wholen] == ' ')\n+\t\t\treturn buf + wholen + 1;\n+\t\teol = strchr(buf, '\\n');\n+\t\tif (!eol)\n+\t\t\treturn \"\";\n+\t\teol++;\n+\t\tif (eol[1] == '\\n')\n+\t\t\treturn \"\"; /* end of header */\n+\t\tbuf = eol;\n+\t}\n+\treturn \"\";\n+}\n+\n+static char *copy_line(const char *buf)\n+{\n+\tconst char *eol = strchr(buf, '\\n');\n+\tchar *line;\n+\tint len;\n+\tif (!eol)\n+\t\treturn \"\";\n+\tlen = eol - buf;\n+\tline = xmalloc(len + 1);\n+\tmemcpy(line, buf, len);\n+\tline[len] = 0;\n+\treturn line;\n+}\n+\n+static char *copy_name(const char *buf)\n+{\n+\tconst char *eol = strchr(buf, '\\n');\n+\tconst char *eoname = strstr(buf, \" <\");\n+\tchar *line;\n+\tint len;\n+\tif (!(eoname && eol && eoname < eol))\n+\t\treturn \"\";\n+\tlen = eoname - buf;\n+\tline = xmalloc(len + 1);\n+\tmemcpy(line, buf, len);\n+\tline[len] = 0;\n+\treturn line;\n+}\n+\n+static char *copy_email(const char *buf)\n+{\n+\tconst char *email = strchr(buf, '<');\n+\tconst char *eoemail = strchr(email, '>');\n+\tchar *line;\n+\tint len;\n+\tif (!email || !eoemail)\n+\t\treturn \"\";\n+\teoemail++;\n+\tlen = eoemail - email;\n+\tline = xmalloc(len + 1);\n+\tmemcpy(line, email, len);\n+\tline[len] = 0;\n+\treturn line;\n+}\n+\n+static void grab_date(const char *buf, struct atom_value *v)\n+{\n+\tconst char *eoemail = strstr(buf, \"> \");\n+\tchar *zone;\n+\tunsigned long timestamp;\n+\tlong tz;\n+\n+\tif (!eoemail)\n+\t\tgoto bad;\n+\ttimestamp = strtoul(eoemail + 2, &zone, 10);\n+\tif (timestamp == ULONG_MAX)\n+\t\tgoto bad;\n+\ttz = strtol(zone, NULL, 10);\n+\tif ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)\n+\t\tgoto bad;\n+\tv->s = xstrdup(show_date(timestamp, tz, 0));\n+\tv->ul = timestamp;\n+\treturn;\n+ bad:\n+\tv->s = \"\";\n+\tv->ul = 0;\n+}\n+\n+/* See grab_values */\n+static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tint wholen = strlen(who);\n+\tconst char *wholine = NULL;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (strncmp(who, name, wholen))\n+\t\t\tcontinue;\n+\t\tif (name[wholen] != 0 &&\n+\t\t    strcmp(name + wholen, \"name\") &&\n+\t\t    strcmp(name + wholen, \"email\") &&\n+\t\t    strcmp(name + wholen, \"date\"))\n+\t\t\tcontinue;\n+\t\tif (!wholine)\n+\t\t\twholine = find_wholine(who, wholen, buf, sz);\n+\t\tif (!wholine)\n+\t\t\treturn; /* no point looking for it */\n+\t\tif (name[wholen] == 0)\n+\t\t\tv->s = copy_line(wholine);\n+\t\telse if (!strcmp(name + wholen, \"name\"))\n+\t\t\tv->s = copy_name(wholine);\n+\t\telse if (!strcmp(name + wholen, \"email\"))\n+\t\t\tv->s = copy_email(wholine);\n+\t\telse if (!strcmp(name + wholen, \"date\"))\n+\t\t\tgrab_date(wholine, v);\n+\t}\n+}\n+\n+static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)\n+{\n+\twhile (*buf) {\n+\t\tconst char *eol = strchr(buf, '\\n');\n+\t\tif (!eol)\n+\t\t\treturn;\n+\t\tif (eol[1] == '\\n') {\n+\t\t\tbuf = eol + 1;\n+\t\t\tbreak; /* found end of header */\n+\t\t}\n+\t\tbuf = eol + 1;\n+\t}\n+\twhile (*buf == '\\n')\n+\t\tbuf++;\n+\tif (!*buf)\n+\t\treturn;\n+\t*sub = buf; /* first non-empty line */\n+\tbuf = strchr(buf, '\\n');\n+\tif (!buf)\n+\t\treturn; /* no body */\n+\twhile (*buf == '\\n')\n+\t\tbuf++; /* skip blank between subject and body */\n+\t*body = buf;\n+}\n+\n+/* See grab_values */\n+static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tint i;\n+\tconst char *subpos = NULL, *bodypos = NULL;\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (!!deref != (*name == '*'))\n+\t\t\tcontinue;\n+\t\tif (deref)\n+\t\t\tname++;\n+\t\tif (strcmp(name, \"subject\") &&\n+\t\t    strcmp(name, \"body\") &&\n+\t\t    strcmp(name, \"contents\"))\n+\t\t\tcontinue;\n+\t\tif (!subpos)\n+\t\t\tfind_subpos(buf, sz, &subpos, &bodypos);\n+\t\tif (!subpos)\n+\t\t\treturn;\n+\n+\t\tif (!strcmp(name, \"subject\"))\n+\t\t\tv->s = copy_line(subpos);\n+\t\telse if (!strcmp(name, \"body\"))\n+\t\t\tv->s = bodypos;\n+\t\telse if (!strcmp(name, \"contents\"))\n+\t\t\tv->s = subpos;\n+\t}\n+}\n+\n+/* We want to have empty print-string for field requests\n+ * that do not apply (e.g. \"authordate\" for a tag object)\n+ */\n+static void fill_missing_values(struct atom_value *val)\n+{\n+\tint i;\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tstruct atom_value *v = &val[i];\n+\t\tif (v->s == NULL)\n+\t\t\tv->s = \"\";\n+\t}\n+}\n+\n+/*\n+ * val is a list of atom_value to hold returned values.  Extract\n+ * the values for atoms in used_atom array out of (obj, buf, sz).\n+ * when deref is false, (obj, buf, sz) is the object that is\n+ * pointed at by the ref itself; otherwise it is the object the\n+ * ref (which is a tag) refers to.\n+ */\n+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)\n+{\n+\tgrab_common_values(val, deref, obj, buf, sz);\n+\tswitch (obj->type) {\n+\tcase OBJ_TAG:\n+\t\tgrab_tag_values(val, deref, obj, buf, sz);\n+\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n+\t\tgrab_person(\"tagger\", val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_COMMIT:\n+\t\tgrab_commit_values(val, deref, obj, buf, sz);\n+\t\tgrab_sub_body_contents(val, deref, obj, buf, sz);\n+\t\tgrab_person(\"author\", val, deref, obj, buf, sz);\n+\t\tgrab_person(\"committer\", val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_TREE:\n+\t\t// grab_tree_values(val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tcase OBJ_BLOB:\n+\t\t// grab_blob_values(val, deref, obj, buf, sz);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"Eh?  Object of type %d?\", obj->type);\n+\t}\n+}\n+\n+/*\n+ * Parse the object referred by ref, and grab needed value.\n+ */\n+static void populate_value(struct refinfo *ref)\n+{\n+\tvoid *buf;\n+\tstruct object *obj;\n+\tint eaten, i;\n+\tunsigned long size;\n+\tconst unsigned char *tagged;\n+\n+\tref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);\n+\n+\tbuf = get_obj(ref->objectname, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(ref->objectname), ref->refname);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(ref->objectname), ref->refname);\n+\n+\t/* Fill in specials first */\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tconst char *name = used_atom[i];\n+\t\tstruct atom_value *v = &ref->value[i];\n+\t\tif (!strcmp(name, \"refname\"))\n+\t\t\tv->s = ref->refname;\n+\t\telse if (!strcmp(name, \"*refname\")) {\n+\t\t\tint len = strlen(ref->refname);\n+\t\t\tchar *s = xmalloc(len + 4);\n+\t\t\tsprintf(s, \"%s^{}\", ref->refname);\n+\t\t\tv->s = s;\n+\t\t}\n+\t}\n+\n+\tgrab_values(ref->value, 0, obj, buf, size);\n+\tif (!eaten)\n+\t\tfree(buf);\n+\n+\t/* If there is no atom that wants to know about tagged\n+\t * object, we are done.\n+\t */\n+\tif (!need_tagged || (obj->type != OBJ_TAG))\n+\t\treturn;\n+\n+\t/* If it is a tag object, see if we use a value that derefs\n+\t * the object, and if we do grab the object it refers to.\n+\t */\n+\ttagged = ((struct tag *)obj)->tagged->sha1;\n+\n+\t/* NEEDSWORK: This derefs tag only once, which\n+\t * is good to deal with chains of trust, but\n+\t * is not consistent with what deref_tag() does\n+\t * which peels the onion to the core.\n+\t */\n+\tbuf = get_obj(tagged, &obj, &size, &eaten);\n+\tif (!buf)\n+\t\tdie(\"missing object %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->refname);\n+\tif (!obj)\n+\t\tdie(\"parse_object_buffer failed on %s for %s\",\n+\t\t    sha1_to_hex(tagged), ref->refname);\n+\tgrab_values(ref->value, 1, obj, buf, size);\n+\tif (!eaten)\n+\t\tfree(buf);\n+}\n+\n+/*\n+ * Given a ref, return the value for the atom.  This lazily gets value\n+ * out of the object by calling populate value.\n+ */\n+static void get_value(struct refinfo *ref, int atom, struct atom_value **v)\n+{\n+\tif (!ref->value) {\n+\t\tpopulate_value(ref);\n+\t\tfill_missing_values(ref->value);\n+\t}\n+\t*v = &ref->value[atom];\n+}\n+\n+static struct refinfo **grab_array;\n+static const char **grab_pattern;\n+static int *grab_cnt;\n+\n+/*\n+ * A call-back given to for_each_ref().  It is unfortunate that we\n+ * need to use global variables to pass extra information to this\n+ * function.\n+ */\n+static int grab_single_ref(const char *refname, const unsigned char *sha1)\n+{\n+\tstruct refinfo *ref;\n+\tint cnt;\n+\n+\tif (*grab_pattern) {\n+\t\tconst char **pattern;\n+\t\tint namelen = strlen(refname);\n+\t\tfor (pattern = grab_pattern; *pattern; pattern++) {\n+\t\t\tconst char *p = *pattern;\n+\t\t\tint plen = strlen(p);\n+\n+\t\t\tif ((plen <= namelen) &&\n+\t\t\t    !strncmp(refname, p, plen) &&\n+\t\t\t    (refname[plen] == '\\0' ||\n+\t\t\t     refname[plen] == '/'))\n+\t\t\t\tbreak;\n+\t\t\tif (!fnmatch(p, refname, FNM_PATHNAME))\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (!*pattern)\n+\t\t\treturn 0;\n+\t}\n+\n+\t/* We do not open the object yet; sort may only need refname\n+\t * to do its job and the resulting list may yet to be pruned\n+\t * by maxcount logic.\n+\t */\n+\tref = xcalloc(1, sizeof(*ref));\n+\tref->refname = xstrdup(refname);\n+\thashcpy(ref->objectname, sha1);\n+\n+\tcnt = *grab_cnt;\n+\tgrab_array = xrealloc(grab_array, sizeof(*grab_array) * (cnt + 1));\n+\tgrab_array[cnt++] = ref;\n+\t*grab_cnt = cnt;\n+\treturn 0;\n+}\n+\n+static struct refinfo **grab_refs(const char **pattern, int *cnt)\n+{\n+\t/* Sheesh, we really should make for-each-ref to take\n+\t * callback data.\n+\t */\n+\t*cnt = 0;\n+\tgrab_pattern = pattern;\n+\tgrab_cnt = cnt;\n+\tfor_each_ref(grab_single_ref);\n+\treturn grab_array;\n+}\n+\n+static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)\n+{\n+\tstruct atom_value *va, *vb;\n+\tint cmp;\n+\tcmp_type cmp_type = used_atom_type[s->atom];\n+\n+\tget_value(a, s->atom, &va);\n+\tget_value(b, s->atom, &vb);\n+\tswitch (cmp_type) {\n+\tcase FIELD_STR:\n+\t\tcmp = strcmp(va->s, vb->s);\n+\t\tbreak;\n+\tdefault:\n+\t\tif (va->ul < vb->ul)\n+\t\t\tcmp = -1;\n+\t\telse if (va->ul == vb->ul)\n+\t\t\tcmp = 0;\n+\t\telse\n+\t\t\tcmp = 1;\n+\t\tbreak;\n+\t}\n+\treturn (s->reverse) ? -cmp : cmp;\n+}\n+\n+static struct ref_sort *ref_sort;\n+static int compare_refs(const void *a_, const void *b_)\n+{\n+\tstruct refinfo *a = *((struct refinfo **)a_);\n+\tstruct refinfo *b = *((struct refinfo **)b_);\n+\tstruct ref_sort *s;\n+\n+\tfor (s = ref_sort; s; s = s->next) {\n+\t\tint cmp = cmp_ref_sort(s, a, b);\n+\t\tif (cmp)\n+\t\t\treturn cmp;\n+\t}\n+\treturn 0;\n+}\n+\n+static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)\n+{\n+\tref_sort = sort;\n+\tqsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);\n+}\n+\n+static void print_value(struct refinfo *ref, int atom, int quote_style)\n+{\n+\tstruct atom_value *v;\n+\tget_value(ref, atom, &v);\n+\tswitch (quote_style) {\n+\tcase QUOTE_NONE:\n+\t\tfputs(v->s, stdout);\n+\t\tbreak;\n+\tcase QUOTE_SHELL:\n+\t\tsq_quote_print(stdout, v->s);\n+\t\tbreak;\n+\tcase QUOTE_PERL:\n+\t\tperl_quote_print(stdout, v->s);\n+\t\tbreak;\n+\tcase QUOTE_PYTHON:\n+\t\tpython_quote_print(stdout, v->s);\n+\t\tbreak;\n+\t}\n+}\n+\n+static void emit(const char *cp, const char *ep)\n+{\n+\twhile (*cp && (!ep || cp < ep)) {\n+\t\tif (*cp == '%')\n+\t\t\tif (cp[1] == '%')\n+\t\t\t\tcp++;\n+\t\tputchar(*cp);\n+\t\tcp++;\n+\t}\n+}\n+\n+static void show_ref(struct refinfo *info, const char *format, int quote_style)\n+{\n+\tconst char *cp, *sp, *ep;\n+\n+\tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n+\t\tep = strchr(sp, ')');\n+\t\tif (cp < sp)\n+\t\t\temit(cp, sp);\n+\t\tprint_value(info, parse_atom(sp + 2, ep), quote_style);\n+\t}\n+\tif (*cp)\n+\t\tfputs(cp, stdout);\n+\tputchar('\\n');\n+}\n+\n+static struct ref_sort *default_sort(void)\n+{\n+\tstatic const char cstr_name[] = \"refname\";\n+\n+\tstruct ref_sort *sort = xcalloc(1, sizeof(*sort));\n+\n+\tsort->next = NULL;\n+\tsort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));\n+\treturn sort;\n+}\n+\n+int cmd_for_each_ref(int ac, const char **av, char *prefix)\n+{\n+\tint i, num_refs;\n+\tconst char *format = NULL;\n+\tstruct ref_sort *sort = NULL, **sort_tail = &sort;\n+\tint maxcount = 0;\n+\tint quote_style = -1; /* unspecified yet */\n+\tstruct refinfo **refs;\n+\n+\tfor (i = 1; i < ac; i++) {\n+\t\tconst char *arg = av[i];\n+\t\tif (arg[0] != '-')\n+\t\t\tbreak;\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--format=\", 9)) {\n+\t\t\tif (format)\n+\t\t\t\tdie(\"more than one --format?\");\n+\t\t\tformat = arg + 9;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-s\") || !strcmp(arg, \"--shell\") ) {\n+\t\t\tif (0 <= quote_style)\n+\t\t\t\tdie(\"more than one quoting style?\");\n+\t\t\tquote_style = QUOTE_SHELL;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-p\") || !strcmp(arg, \"--perl\") ) {\n+\t\t\tif (0 <= quote_style)\n+\t\t\t\tdie(\"more than one quoting style?\");\n+\t\t\tquote_style = QUOTE_PERL;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--python\") ) {\n+\t\t\tif (0 <= quote_style)\n+\t\t\t\tdie(\"more than one quoting style?\");\n+\t\t\tquote_style = QUOTE_PYTHON;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--count=\", 8)) {\n+\t\t\tif (maxcount)\n+\t\t\t\tdie(\"more than one --count?\");\n+\t\t\tmaxcount = atoi(arg + 8);\n+\t\t\tif (maxcount <= 0)\n+\t\t\t\tdie(\"The number %s did not parse\", arg);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--sort=\", 7)) {\n+\t\t\tstruct ref_sort *s = xcalloc(1, sizeof(*s));\n+\t\t\tint len;\n+\n+\t\t\ts->next = NULL;\n+\t\t\t*sort_tail = s;\n+\t\t\tsort_tail = &s->next;\n+\n+\t\t\targ += 7;\n+\t\t\tif (*arg == '-') {\n+\t\t\t\ts->reverse = 1;\n+\t\t\t\targ++;\n+\t\t\t}\n+\t\t\tlen = strlen(arg);\n+\t\t\tsort->atom = parse_atom(arg, arg+len);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tif (quote_style < 0)\n+\t\tquote_style = QUOTE_NONE;\n+\n+\tif (!sort)\n+\t\tsort = default_sort();\n+\tsort_atom_limit = used_atom_cnt;\n+\tif (!format)\n+\t\tformat = \"%(objectname) %(objecttype)\\t%(refname)\";\n+\n+\tverify_format(format);\n+\n+\trefs = grab_refs(av + i, &num_refs);\n+\n+\tfor (i = 0; i < used_atom_cnt; i++) {\n+\t\tif (used_atom[i][0] == '*') {\n+\t\t\tneed_tagged = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tsort_refs(sort, refs, num_refs);\n+\n+\tif (!maxcount || num_refs < maxcount)\n+\t\tmaxcount = num_refs;\n+\tfor (i = 0; i < maxcount; i++)\n+\t\tshow_ref(refs[i], format, quote_style);\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 25431d7..87d3326 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -26,6 +26,7 @@ extern int cmd_diff(int argc, const char\n extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);\n extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);\n+extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_format_patch(int argc, const char **argv, const char *prefix);\n extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);\n extern int cmd_grep(int argc, const char **argv, const char *prefix);\n@@ -47,8 +48,8 @@ extern int cmd_repo_config(int argc, con\n extern int cmd_rev_list(int argc, const char **argv, const char *prefix);\n extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);\n extern int cmd_rm(int argc, const char **argv, const char *prefix);\n-extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_show(int argc, const char **argv, const char *prefix);\n+extern int cmd_show_branch(int argc, const char **argv, const char *prefix);\n extern int cmd_stripspace(int argc, const char **argv, const char *prefix);\n extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/git.c b/git.c\nindex 8c182a5..10db27b 100644\n--- a/git.c\n+++ b/git.c\n@@ -231,6 +231,7 @@ static void handle_internal_command(int \n \t\t{ \"diff-stages\", cmd_diff_stages, RUN_SETUP },\n \t\t{ \"diff-tree\", cmd_diff_tree, RUN_SETUP },\n \t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg, RUN_SETUP },\n+\t\t{ \"for-each-ref\", cmd_for_each_ref, RUN_SETUP },\n \t\t{ \"format-patch\", cmd_format_patch, RUN_SETUP },\n \t\t{ \"get-tar-commit-id\", cmd_get_tar_commit_id },\n \t\t{ \"grep\", cmd_grep, RUN_SETUP },\ndiff --git a/object.c b/object.c\nindex 9281300..de244e2 100644\n--- a/object.c\n+++ b/object.c\n@@ -138,42 +138,56 @@ struct object *lookup_unknown_object(con\n \treturn obj;\n }\n \n+struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p)\n+{\n+\tstruct object *obj;\n+\tint eaten = 0;\n+\n+\tif (!strcmp(type, blob_type)) {\n+\t\tstruct blob *blob = lookup_blob(sha1);\n+\t\tparse_blob_buffer(blob, buffer, size);\n+\t\tobj = &blob->object;\n+\t} else if (!strcmp(type, tree_type)) {\n+\t\tstruct tree *tree = lookup_tree(sha1);\n+\t\tobj = &tree->object;\n+\t\tif (!tree->object.parsed) {\n+\t\t\tparse_tree_buffer(tree, buffer, size);\n+\t\t\teaten = 1;\n+\t\t}\n+\t} else if (!strcmp(type, commit_type)) {\n+\t\tstruct commit *commit = lookup_commit(sha1);\n+\t\tparse_commit_buffer(commit, buffer, size);\n+\t\tif (!commit->buffer) {\n+\t\t\tcommit->buffer = buffer;\n+\t\t\teaten = 1;\n+\t\t}\n+\t\tobj = &commit->object;\n+\t} else if (!strcmp(type, tag_type)) {\n+\t\tstruct tag *tag = lookup_tag(sha1);\n+\t\tparse_tag_buffer(tag, buffer, size);\n+\t\tobj = &tag->object;\n+\t} else {\n+\t\tobj = NULL;\n+\t}\n+\t*eaten_p = eaten;\n+\treturn obj;\n+}\n+\n struct object *parse_object(const unsigned char *sha1)\n {\n \tunsigned long size;\n \tchar type[20];\n+\tint eaten;\n \tvoid *buffer = read_sha1_file(sha1, type, &size);\n+\n \tif (buffer) {\n \t\tstruct object *obj;\n \t\tif (check_sha1_signature(sha1, buffer, size, type) < 0)\n \t\t\tprintf(\"sha1 mismatch %s\\n\", sha1_to_hex(sha1));\n-\t\tif (!strcmp(type, blob_type)) {\n-\t\t\tstruct blob *blob = lookup_blob(sha1);\n-\t\t\tparse_blob_buffer(blob, buffer, size);\n-\t\t\tobj = &blob->object;\n-\t\t} else if (!strcmp(type, tree_type)) {\n-\t\t\tstruct tree *tree = lookup_tree(sha1);\n-\t\t\tobj = &tree->object;\n-\t\t\tif (!tree->object.parsed) {\n-\t\t\t\tparse_tree_buffer(tree, buffer, size);\n-\t\t\t\tbuffer = NULL;\n-\t\t\t}\n-\t\t} else if (!strcmp(type, commit_type)) {\n-\t\t\tstruct commit *commit = lookup_commit(sha1);\n-\t\t\tparse_commit_buffer(commit, buffer, size);\n-\t\t\tif (!commit->buffer) {\n-\t\t\t\tcommit->buffer = buffer;\n-\t\t\t\tbuffer = NULL;\n-\t\t\t}\n-\t\t\tobj = &commit->object;\n-\t\t} else if (!strcmp(type, tag_type)) {\n-\t\t\tstruct tag *tag = lookup_tag(sha1);\n-\t\t\tparse_tag_buffer(tag, buffer, size);\n-\t\t\tobj = &tag->object;\n-\t\t} else {\n-\t\t\tobj = NULL;\n-\t\t}\n-\t\tfree(buffer);\n+\n+\t\tobj = parse_object_buffer(sha1, type, size, buffer, &eaten);\n+\t\tif (!eaten)\n+\t\t\tfree(buffer);\n \t\treturn obj;\n \t}\n \treturn NULL;\ndiff --git a/object.h b/object.h\nindex 3d4ff46..caee733 100644\n--- a/object.h\n+++ b/object.h\n@@ -59,6 +59,12 @@ void created_object(const unsigned char \n /** Returns the object, having parsed it to find out what it is. **/\n struct object *parse_object(const unsigned char *sha1);\n \n+/* Given the result of read_sha1_file(), returns the object after\n+ * parsing it.  eaten_p indicates if the object has a borrowed copy\n+ * of buffer and the caller should not free() it.\n+ */\n+struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p);\n+\n /** Returns the object, with potentially excess memory allocated. **/\n struct object *lookup_unknown_object(const unsigned  char *sha1);\n \ndiff --git a/quote.c b/quote.c\nindex e3a4d4a..ee7d62c 100644\n--- a/quote.c\n+++ b/quote.c\n@@ -349,3 +349,41 @@ void write_name_quoted(const char *prefi\n \telse\n \t\tgoto no_quote;\n }\n+\n+/* quoting as a string literal for other languages */\n+\n+void perl_quote_print(FILE *stream, const char *src)\n+{\n+\tconst char sq = '\\'';\n+\tconst char bq = '\\\\';\n+\tchar c;\n+\n+\tfputc(sq, stream);\n+\twhile ((c = *src++)) {\n+\t\tif (c == sq || c == bq)\n+\t\t\tfputc(bq, stream);\n+\t\tfputc(c, stream);\n+\t}\n+\tfputc(sq, stream);\n+}\n+\n+void python_quote_print(FILE *stream, const char *src)\n+{\n+\tconst char sq = '\\'';\n+\tconst char bq = '\\\\';\n+\tconst char nl = '\\n';\n+\tchar c;\n+\n+\tfputc(sq, stream);\n+\twhile ((c = *src++)) {\n+\t\tif (c == nl) {\n+\t\t\tfputc(bq, stream);\n+\t\t\tfputc('n', stream);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (c == sq || c == bq)\n+\t\t\tfputc(bq, stream);\n+\t\tfputc(c, stream);\n+\t}\n+\tfputc(sq, stream);\n+}\ndiff --git a/quote.h b/quote.h\nindex 1a29e79..b55e699 100644\n--- a/quote.h\n+++ b/quote.h\n@@ -52,4 +52,8 @@ extern char *unquote_c_style(const char \n extern void write_name_quoted(const char *prefix, int prefix_len,\n \t\t\t      const char *name, int quote, FILE *out);\n \n+/* quoting as a string literal for other languages */\n+extern void perl_quote_print(FILE *stream, const char *src);\n+extern void python_quote_print(FILE *stream, const char *src);\n+\n #endif\n-- \n1.4.2.1.gcd6f1\n"},{"id":"26985","messageId":"eef5m8$euj$1@sea.gmane.org","threadId":"5576","inReplyTo":"7vmz90g80m.fsf@assigned-by-dhcp.cox.net","subject":"Re: Add \"git show-ref\" builtin command","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-09-15T21:24:56Z","receivedAt":"2006-09-15T21:24:56Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> +<format>::\n> +       A string that interpolates `%(fieldname)` from the\n> +       object pointed at by a ref being shown.  If `fieldname`\n> +       is prefixed with an asterisk (`*`) and the ref points\n> +       at a tag object, the value for the field in the object\n> +       tag refers is used.  When unspecified, defaults to\n> +       `%(refname)`.\n\nWouldn't it be better to be able to use (or be able to enable, like echo -e\noption) interpretation of the  backslash-escaped characters, like \n\\t, \\n, \\0?\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"26987","messageId":"Pine.LNX.4.64.0609151455150.4388@g5.osdl.org","threadId":"5576","inReplyTo":"Pine.LNX.4.64.0609151108560.4388@g5.osdl.org","subject":"Teach \"git checkout\" to use git-show-ref","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-15T21:56:55Z","receivedAt":"2006-09-15T21:56:55Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThat way, it doesn't care how the refs are stored any more\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nJust as an example of how to use git-show-ref rather than knowing about \n\"$GIT_DIR/refs/...\" paths.\n\nThis basically replaces my much hackier version that is in the \"lt/refs\" \nbranch, but can go into the master branch independently of the \nrefs-packing work.\n\ndiff --git a/git-checkout.sh b/git-checkout.sh\nindex 580a9e8..6e4c535 100755\n--- a/git-checkout.sh\n+++ b/git-checkout.sh\n@@ -22,7 +22,7 @@ while [ \"$#\" != \"0\" ]; do\n \t\tshift\n \t\t[ -z \"$newbranch\" ] &&\n \t\t\tdie \"git checkout: -b needs a branch name\"\n-\t\t[ -e \"$GIT_DIR/refs/heads/$newbranch\" ] &&\n+\t\tgit-show-ref --verify --quiet -- \"refs/heads/$newbranch\" &&\n \t\t\tdie \"git checkout: branch $newbranch already exists\"\n \t\tgit-check-ref-format \"heads/$newbranch\" ||\n \t\t\tdie \"git checkout: we do not like '$newbranch' as a branch name.\"\n@@ -51,7 +51,7 @@ while [ \"$#\" != \"0\" ]; do\n \t\t\tfi\n \t\t\tnew=\"$rev\"\n \t\t\tnew_name=\"$arg^0\"\n-\t\t\tif [ -f \"$GIT_DIR/refs/heads/$arg\" ]; then\n+\t\t\tif git-show-ref --verify --quiet -- \"refs/heads/$arg\" ]; then\n \t\t\t\tbranch=\"$arg\"\n \t\t\tfi\n \t\telif rev=$(git-rev-parse --verify \"$arg^{tree}\" 2>/dev/null)\n"},{"id":"26988","messageId":"eef8dq$nbc$1@sea.gmane.org","threadId":"5576","inReplyTo":"Pine.LNX.4.64.0609151455150.4388@g5.osdl.org","subject":"Re: Teach \"git checkout\" to use git-show-ref","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-09-15T22:11:38Z","receivedAt":"2006-09-15T22:11:38Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Linus Torvalds wrote:\n\n> @@ -51,7 +51,7 @@ while [ \"$#\" != \"0\" ]; do\n>                         fi\n>                         new=\"$rev\"\n>                         new_name=\"$arg^0\"\n> -                       if [ -f \"$GIT_DIR/refs/heads/$arg\" ]; then\n> +                       if git-show-ref --verify --quiet -- \"refs/heads/$arg\" ]; then\n  +                       if git-show-ref --verify --quiet -- \"refs/heads/$arg\"; then\n>                                 branch=\"$arg\"\n>                         fi\n>                 elif rev=$(git-rev-parse --verify \"$arg^{tree}\" 2>/dev/null)\n\nThere is leftover ] before ; in if.\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"26989","messageId":"7vy7skeo5v.fsf@assigned-by-dhcp.cox.net","threadId":"5576","inReplyTo":"eef5m8$euj$1@sea.gmane.org","subject":"Re: Add \"git show-ref\" builtin command","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-15T22:54:36Z","receivedAt":"2006-09-15T22:54:36Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Wouldn't it be better to be able to use (or be able to enable, like echo -e\n> option) interpretation of the  backslash-escaped characters, like \n> \\t, \\n, \\0?\n\nI've been thinking about letting the --format to specify\nembedding arbitrary byte value in the output.\n\nThis option however is mostly to help Porcelain written in\nlanguages other than C (and that is where the language specific\nquoting styles come in) to allow a template of a scriptlet to be\nspecified, as you have probably seen in the examples in the\ndocumentation page, so I think it is more user friendly to leave\nbackslash as just a literal character.\n\nMy current thinking is to allow you to say %XX (a per-cent\nfollowed by exactly two hexadecimal digits) to do embed a\nliteral byte value.  Then a Porcelain written in Perl that does\nnot want to eval output can do something like this:\n\n\tmy $fmt = 'r%(refname)%00o%(objectname)%00%00';\n\topen R, '-|', 'git-for-each-ref', \"--format=$fmt\";\n\tmy $all = join('', <R>);\n\tclose R;        \t        \n        for (split(/\\0\\0/, $all)) {\n\t\t/r(.*?)\\0o(.*)/ &&\n                print \"ref = $1, obj = $2\\n\";\n        }\n\nAnother thing is that originally I picked %(name) syntax because\nI thought we might want to do fancier \"%20(column)d\" like Python\ndoes with its string formatting operator.  But I now think it\nmakes more sense to output whatever is asked as string literals\nand have host language worry about formatting.  So in that\nsense, using %() as our formatting specifier will get in the way\nfor people who writes in Python.  Maybe I should change it to\nsomething like %{name} instead (not ${name} -- that would\ninterfere with the shell and Perl).\n\nAnyhow, on top of the previous one, this will let you say %00 to\nembed a NUL in your string.\n\ndiff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c\nindex f064e7e..698618b 100644\n--- a/builtin-for-each-ref.c\n+++ b/builtin-for-each-ref.c\n@@ -710,12 +710,39 @@ static void print_value(struct refinfo *\n \t}\n }\n \n+static int hex1(char ch)\n+{\n+\tif ('0' <= ch && ch <= '9')\n+\t\treturn ch - '0';\n+\telse if ('a' <= ch && ch <= 'f')\n+\t\treturn ch - 'a' + 10;\n+\telse if ('A' <= ch && ch <= 'F')\n+\t\treturn ch - 'A' + 10;\n+\treturn -1;\n+}\n+static int hex2(const char *cp)\n+{\n+\tif (cp[0] && cp[1])\n+\t\treturn (hex1(cp[0]) << 4) | hex1(cp[1]);\n+\telse\n+\t\treturn -1;\n+}\n+\n static void emit(const char *cp, const char *ep)\n {\n \twhile (*cp && (!ep || cp < ep)) {\n-\t\tif (*cp == '%')\n+\t\tif (*cp == '%') {\n \t\t\tif (cp[1] == '%')\n \t\t\t\tcp++;\n+\t\t\telse {\n+\t\t\t\tint ch = hex2(cp + 1);\n+\t\t\t\tif (0 <= ch) {\n+\t\t\t\t\tputchar(ch);\n+\t\t\t\t\tcp += 3;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n \t\tputchar(*cp);\n \t\tcp++;\n \t}\n@@ -731,8 +758,10 @@ static void show_ref(struct refinfo *inf\n \t\t\temit(cp, sp);\n \t\tprint_value(info, parse_atom(sp + 2, ep), quote_style);\n \t}\n-\tif (*cp)\n-\t\tfputs(cp, stdout);\n+\tif (*cp) {\n+\t\tsp = cp + strlen(cp);\n+\t\temit(cp, sp);\n+\t}\n \tputchar('\\n');\n }\n \n"},{"id":"26998","messageId":"7vpsdw9mvg.fsf@assigned-by-dhcp.cox.net","threadId":"5576","inReplyTo":"Pine.LNX.4.64.0609151455150.4388@g5.osdl.org","subject":"Re: Teach \"git checkout\" to use git-show-ref","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-16T09:33:39Z","receivedAt":"2006-09-16T09:33:39Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> That way, it doesn't care how the refs are stored any more\n>\n> Signed-off-by: Linus Torvalds <torvalds@osdl.org>\n> ---\n>\n> Just as an example of how to use git-show-ref rather than knowing about \n> \"$GIT_DIR/refs/...\" paths.\n>\n> This basically replaces my much hackier version that is in the \"lt/refs\" \n> branch, but can go into the master branch independently of the \n> refs-packing work.\n\nI kind of liked the rev-parse one better for this particular\ncommand, but only slightly (by using --verify on \"$arg^0\", you\nwere also making sure what's in refs/heads/ was a commit object,\nbut that check is gone with this patch.  It is not a loss,\nbecause the original code did not check it, and nobody should be\nplacing anything but commits under refs/heads anyway).\n\n> diff --git a/git-checkout.sh b/git-checkout.sh\n> index 580a9e8..6e4c535 100755\n> --- a/git-checkout.sh\n> +++ b/git-checkout.sh\n>...\n> @@ -51,7 +51,7 @@ while [ \"$#\" != \"0\" ]; do\n>  \t\t\tfi\n>  \t\t\tnew=\"$rev\"\n>  \t\t\tnew_name=\"$arg^0\"\n> -\t\t\tif [ -f \"$GIT_DIR/refs/heads/$arg\" ]; then\n> +\t\t\tif git-show-ref --verify --quiet -- \"refs/heads/$arg\" ]; then\n>  \t\t\t\tbranch=\"$arg\"\n>  \t\t\tfi\n>  \t\telif rev=$(git-rev-parse --verify \"$arg^{tree}\" 2>/dev/null)\n"}]}