summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Documentation/git-for-each-ref.txt181
-rw-r--r--Documentation/git-show-ref.txt152
-rw-r--r--Documentation/git-update-ref.txt10
-rw-r--r--Makefile5
-rw-r--r--builtin-fmt-merge-msg.c7
-rw-r--r--builtin-for-each-ref.c871
-rw-r--r--builtin-init-db.c4
-rw-r--r--builtin-name-rev.c8
-rw-r--r--builtin-pack-refs.c112
-rw-r--r--builtin-prune.c4
-rw-r--r--builtin-push.c4
-rw-r--r--builtin-rev-parse.c10
-rw-r--r--builtin-show-branch.c78
-rw-r--r--builtin-show-ref.c120
-rw-r--r--builtin-symbolic-ref.c18
-rw-r--r--builtin-update-index.c6
-rw-r--r--builtin-update-ref.c20
-rw-r--r--builtin.h5
-rw-r--r--cache.h7
-rw-r--r--describe.c4
-rw-r--r--fetch-pack.c8
-rw-r--r--fetch.c6
-rw-r--r--fsck-objects.c15
-rwxr-xr-xgit-branch.sh11
-rwxr-xr-xgit-checkout.sh5
-rwxr-xr-xgit-commit.sh6
-rwxr-xr-xgit-tag.sh9
-rw-r--r--git.c3
-rw-r--r--http-push.c4
-rw-r--r--object.c68
-rw-r--r--object.h6
-rw-r--r--quote.c38
-rw-r--r--quote.h4
-rw-r--r--receive-pack.c95
-rw-r--r--refs.c395
-rw-r--r--refs.h19
-rw-r--r--revision.c4
-rw-r--r--send-pack.c4
-rw-r--r--server-info.c4
-rw-r--r--sha1_name.c14
-rwxr-xr-xt/t1400-update-ref.sh7
-rw-r--r--upload-pack.c6
-rw-r--r--wt-status.c6
44 files changed, 2008 insertions, 358 deletions
diff --git a/.gitignore b/.gitignore
index 25eb463..4c8c8e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ git-fetch
git-fetch-pack
git-findtags
git-fmt-merge-msg
+git-for-each-ref
git-format-patch
git-fsck-objects
git-get-tar-commit-id
@@ -74,6 +75,7 @@ git-name-rev
git-mv
git-pack-redundant
git-pack-objects
+git-pack-refs
git-parse-remote
git-patch-id
git-peek-remote
@@ -105,6 +107,7 @@ git-shortlog
git-show
git-show-branch
git-show-index
+git-show-ref
git-ssh-fetch
git-ssh-pull
git-ssh-push
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
new file mode 100644
index 0000000..d5fdcef
--- /dev/null
+++ b/Documentation/git-for-each-ref.txt
@@ -0,0 +1,181 @@
+git-for-each-ref(1)
+===================
+
+NAME
+----
+git-for-each-ref - Output information on each ref
+
+SYNOPSIS
+--------
+'git-for-each-ref' [--count=<count>]* [--shell|--perl|--python] [--sort=<key>]* [--format=<format>] [<pattern>]
+
+DESCRIPTION
+-----------
+
+Iterate over all refs that match `<pattern>` and show them
+according to the given `<format>`, after sorting them according
+to the given set of `<key>`s. If `<max>` is given, stop after
+showing that many refs. The interporated values in `<format>`
+can optionally be quoted as string literals in the specified
+host language allowing their direct evaluation in that language.
+
+OPTIONS
+-------
+<count>::
+ By default the command shows all refs that match
+ `<pattern>`. This option makes it stop after showing
+ that many refs.
+
+<key>::
+ A field name to sort on. Prefix `-` to sort in
+ descending order of the value. When unspecified,
+ `refname` is used. More than one sort keys can be
+ given.
+
+<format>::
+ A string that interpolates `%(fieldname)` from the
+ object pointed at by a ref being shown. If `fieldname`
+ is prefixed with an asterisk (`*`) and the ref points
+ at a tag object, the value for the field in the object
+ tag refers is used. When unspecified, defaults to
+ `%(refname)`.
+
+<pattern>::
+ If given, the name of the ref is matched against this
+ using fnmatch(3). Refs that do not match the pattern
+ are not shown.
+
+--shell, --perl, --python::
+ If given, strings that substitute `%(fieldname)`
+ placeholders are quoted as string literals suitable for
+ the specified host language. This is meant to produce
+ a scriptlet that can directly be `eval`ed.
+
+
+FIELD NAMES
+-----------
+
+Various values from structured fields in referenced objects can
+be used to interpolate into the resulting output, or as sort
+keys.
+
+For all objects, the following names can be used:
+
+refname::
+ The name of the ref (the part after $GIT_DIR/refs/).
+
+objecttype::
+ The type of the object (`blob`, `tree`, `commit`, `tag`).
+
+objectsize::
+ The size of the object (the same as `git-cat-file -s` reports).
+
+objectname::
+ The object name (aka SHA-1).
+
+In addition to the above, for commit and tag objects, the header
+field names (`tree`, `parent`, `object`, `type`, and `tag`) can
+be used to specify the value in the header field.
+
+Fields that have name-email-date tuple as its value (`author`,
+`committer`, and `tagger`) can be suffixed with `name`, `email`,
+and `date` to extract the named component.
+
+The first line of the message in a commit and tag object is
+`subject`, the remaining lines are `body`. The whole message
+is `contents`.
+
+For sorting purposes, fields with numeric values sort in numeric
+order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
+All other fields are used to sort in their byte-value order.
+
+In any case, a field name that refers to a field inapplicable to
+the object referred by the ref does not cause an error. It
+returns an empty string instead.
+
+
+EXAMPLES
+--------
+
+An example directly producing formatted text. Show the most recent
+3 tagged commits::
+
+------------
+#!/bin/sh
+
+git-for-each-ref --count=3 --sort='-*authordate' \
+--format='From: %(*authorname) %(*authoremail)
+Subject: %(*subject)
+Date: %(*authordate)
+Ref: %(*refname)
+
+%(*body)
+' 'refs/tags'
+------------
+
+
+A simple example showing the use of shell eval on the output,
+demonstrating the use of --shell. List the prefixes of all heads::
+------------
+#!/bin/sh
+
+git-for-each-ref --shell --format="ref=%(refname)" refs/heads | \
+while read entry
+do
+ eval "$entry"
+ echo `dirname $ref`
+done
+------------
+
+
+A bit more elaborate report on tags, demonstrating that the format
+may be an entire script::
+------------
+#!/bin/sh
+
+fmt='
+ r=%(refname)
+ t=%(*objecttype)
+ T=${r#refs/tags/}
+
+ o=%(*objectname)
+ n=%(*authorname)
+ e=%(*authoremail)
+ s=%(*subject)
+ d=%(*authordate)
+ b=%(*body)
+
+ kind=Tag
+ if test "z$t" = z
+ then
+ # could be a lightweight tag
+ t=%(objecttype)
+ kind="Lightweight tag"
+ o=%(objectname)
+ n=%(authorname)
+ e=%(authoremail)
+ s=%(subject)
+ d=%(authordate)
+ b=%(body)
+ fi
+ echo "$kind $T points at a $t object $o"
+ if test "z$t" = zcommit
+ then
+ echo "The commit was authored by $n $e
+at $d, and titled
+
+ $s
+
+Its message reads as:
+"
+ echo "$b" | sed -e "s/^/ /"
+ echo
+ fi
+'
+
+eval=`git-for-each-ref --shell --format="$fmt" \
+ --sort='*objecttype' \
+ --sort=-taggerdate \
+ refs/tags`
+eval "$eval"
+------------
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
new file mode 100644
index 0000000..b724d83
--- /dev/null
+++ b/Documentation/git-show-ref.txt
@@ -0,0 +1,152 @@
+git-show-ref(1)
+===============
+
+NAME
+----
+git-show-ref - List references in a local repository
+
+SYNOPSIS
+--------
+[verse]
+'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
+ [-s|--hash] [--tags] [--heads] [--] <pattern>...
+
+DESCRIPTION
+-----------
+
+Displays references available in a local repository along with the associated
+commit IDs. Results can be filtered using a pattern and tags can be
+dereferenced into object IDs. Additionally, it can be used to test whether a
+particular ref exists.
+
+Use of this utility is encouraged in favor of directly accessing files under
+in the `.git` directory.
+
+OPTIONS
+-------
+
+-h, --head::
+
+ Show the HEAD reference.
+
+--tags, --heads::
+
+ Limit to only "refs/heads" and "refs/tags", respectively. These
+ options are not mutually exclusive; when given both, references stored
+ in "refs/heads" and "refs/tags" are displayed.
+
+-d, --dereference::
+
+ Dereference tags into object IDs. They will be shown with "^{}"
+ appended.
+
+-s, --hash::
+
+ Only show the SHA1 hash, not the reference name. When also using
+ --dereference the dereferenced tag will still be shown after the SHA1,
+ this maybe a bug.
+
+--verify::
+
+ Enable stricter reference checking by requiring an exact ref path.
+ Aside from returning an error code of 1, it will also print an error
+ message if '--quiet' was not specified.
+
+-q, --quiet::
+
+ Do not print any results to stdout. When combined with '--verify' this
+ can be used to silently check if a reference exists.
+
+<pattern>::
+
+ Show references matching one or more patterns.
+
+OUTPUT
+------
+
+The output is in the format: '<SHA-1 ID>' '<space>' '<reference name>'.
+
+-----------------------------------------------------------------------------
+$ git show-ref --head --dereference
+832e76a9899f560a90ffd62ae2ce83bbeff58f54 HEAD
+832e76a9899f560a90ffd62ae2ce83bbeff58f54 refs/heads/master
+832e76a9899f560a90ffd62ae2ce83bbeff58f54 refs/heads/origin
+3521017556c5de4159da4615a39fa4d5d2c279b5 refs/tags/v0.99.9c
+6ddc0964034342519a87fe013781abf31c6db6ad refs/tags/v0.99.9c^{}
+055e4ae3ae6eb344cbabf2a5256a49ea66040131 refs/tags/v1.0rc4
+423325a2d24638ddcc82ce47be5e40be550f4507 refs/tags/v1.0rc4^{}
+...
+-----------------------------------------------------------------------------
+
+When using --hash (and not --dereference) the output format is: '<SHA-1 ID>'
+
+-----------------------------------------------------------------------------
+$ git show-ref --heads --hash
+2e3ba0114a1f52b47df29743d6915d056be13278
+185008ae97960c8d551adcd9e23565194651b5d1
+03adf42c988195b50e1a1935ba5fcbc39b2b029b
+...
+-----------------------------------------------------------------------------
+
+EXAMPLE
+-------
+
+To show all references called "master", whether tags or heads or anything
+else, and regardless of how deep in the reference naming hierarchy they are,
+use:
+
+-----------------------------------------------------------------------------
+ git show-ref master
+-----------------------------------------------------------------------------
+
+This will show "refs/heads/master" but also "refs/remote/other-repo/master",
+if such references exists.
+
+When using the '--verify' flag, the command requires an exact path:
+
+-----------------------------------------------------------------------------
+ git show-ref --verify refs/heads/master
+-----------------------------------------------------------------------------
+
+will only match the exact branch called "master".
+
+If nothing matches, gitlink:git-show-ref[1] will return an error code of 1,
+and in the case of verification, it will show an error message.
+
+For scripting, you can ask it to be quiet with the "--quiet" flag, which
+allows you to do things like
+
+-----------------------------------------------------------------------------
+ git-show-ref --quiet --verify -- "refs/heads/$headname" ||
+ echo "$headname is not a valid branch"
+-----------------------------------------------------------------------------
+
+to check whether a particular branch exists or not (notice how we don't
+actually want to show any results, and we want to use the full refname for it
+in order to not trigger the problem with ambiguous partial matches).
+
+To show only tags, or only proper branch heads, use "--tags" and/or "--heads"
+respectively (using both means that it shows tags and heads, but not other
+random references under the refs/ subdirectory).
+
+To do automatic tag object dereferencing, use the "-d" or "--dereference"
+flag, so you can do
+
+-----------------------------------------------------------------------------
+ git show-ref --tags --dereference
+-----------------------------------------------------------------------------
+
+to get a listing of all tags together with what they dereference.
+
+SEE ALSO
+--------
+gitlink:git-ls-remote[1], gitlink:git-peek-remote[1]
+
+AUTHORS
+-------
+Written by Linus Torvalds <torvalds@osdl.org>.
+Man page by Jonas Fonseca <fonseca@diku.dk>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt
index e062030..71bcb79 100644
--- a/Documentation/git-update-ref.txt
+++ b/Documentation/git-update-ref.txt
@@ -7,7 +7,7 @@ git-update-ref - update the object name stored in a ref safely
SYNOPSIS
--------
-'git-update-ref' [-m <reason>] <ref> <newvalue> [<oldvalue>]
+'git-update-ref' [-m <reason>] (-d <ref> <oldvalue> | <ref> <newvalue> [<oldvalue>])
DESCRIPTION
-----------
@@ -20,7 +20,9 @@ possibly dereferencing the symbolic refs, after verifying that
the current value of the <ref> matches <oldvalue>.
E.g. `git-update-ref refs/heads/master <newvalue> <oldvalue>`
updates the master branch head to <newvalue> only if its current
-value is <oldvalue>.
+value is <oldvalue>. You can specify 40 "0" or an empty string
+as <oldvalue> to make sure that the ref you are creating does
+not exist.
It also allows a "ref" file to be a symbolic pointer to another
ref file by starting with the four-byte header sequence of
@@ -49,6 +51,10 @@ for reading but not for writing (so we'll never write through a
ref symlink to some other tree, if you have copied a whole
archive by creating a symlink tree).
+With `-d` flag, it deletes the named <ref> after verifying it
+still contains <oldvalue>.
+
+
Logging Updates
---------------
If config parameter "core.logAllRefUpdates" is true or the file
diff --git a/Makefile b/Makefile
index c888c81..65fb828 100644
--- a/Makefile
+++ b/Makefile
@@ -271,6 +271,7 @@ BUILTIN_OBJS = \
builtin-diff-stages.o \
builtin-diff-tree.o \
builtin-fmt-merge-msg.o \
+ builtin-for-each-ref.o \
builtin-grep.o \
builtin-init-db.o \
builtin-log.o \
@@ -299,7 +300,9 @@ BUILTIN_OBJS = \
builtin-update-ref.o \
builtin-upload-archive.o \
builtin-verify-pack.o \
- builtin-write-tree.o
+ builtin-write-tree.o \
+ builtin-show-ref.o \
+ builtin-pack-refs.o
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
LIBS = $(GITLIBS) -lz
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index c407c03..3d3097d 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
FILE *in = stdin;
const char *sep = "";
unsigned char head_sha1[20];
- const char *head, *current_branch;
+ const char *current_branch;
git_config(fmt_merge_msg_config);
@@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
usage(fmt_merge_msg_usage);
/* get current branch */
- head = xstrdup(git_path("HEAD"));
- current_branch = resolve_ref(head, head_sha1, 1);
- current_branch += strlen(head) - 4;
- free((char *)head);
+ current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
if (!strncmp(current_branch, "refs/heads/", 11))
current_branch += 11;
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
new file mode 100644
index 0000000..93d3d7e
--- /dev/null
+++ b/builtin-for-each-ref.c
@@ -0,0 +1,871 @@
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "quote.h"
+#include <fnmatch.h>
+
+/* Quoting styles */
+#define QUOTE_NONE 0
+#define QUOTE_SHELL 1
+#define QUOTE_PERL 2
+#define QUOTE_PYTHON 3
+
+typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
+
+struct atom_value {
+ const char *s;
+ unsigned long ul; /* used for sorting when not FIELD_STR */
+};
+
+struct ref_sort {
+ struct ref_sort *next;
+ int atom; /* index into used_atom array */
+ unsigned reverse : 1;
+};
+
+struct refinfo {
+ char *refname;
+ unsigned char objectname[20];
+ struct atom_value *value;
+};
+
+static struct {
+ const char *name;
+ cmp_type cmp_type;
+} valid_atom[] = {
+ { "refname" },
+ { "objecttype" },
+ { "objectsize", FIELD_ULONG },
+ { "objectname" },
+ { "tree" },
+ { "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */
+ { "numparent", FIELD_ULONG },
+ { "object" },
+ { "type" },
+ { "tag" },
+ { "author" },
+ { "authorname" },
+ { "authoremail" },
+ { "authordate", FIELD_TIME },
+ { "committer" },
+ { "committername" },
+ { "committeremail" },
+ { "committerdate", FIELD_TIME },
+ { "tagger" },
+ { "taggername" },
+ { "taggeremail" },
+ { "taggerdate", FIELD_TIME },
+ { "subject" },
+ { "body" },
+ { "contents" },
+};
+
+/*
+ * An atom is a valid field atom listed above, possibly prefixed with
+ * a "*" to denote deref_tag().
+ *
+ * We parse given format string and sort specifiers, and make a list
+ * of properties that we need to extract out of objects. refinfo
+ * structure will hold an array of values extracted that can be
+ * indexed with the "atom number", which is an index into this
+ * array.
+ */
+static const char **used_atom;
+static cmp_type *used_atom_type;
+static int used_atom_cnt, sort_atom_limit, need_tagged;
+
+/*
+ * Used to parse format string and sort specifiers
+ */
+static int parse_atom(const char *atom, const char *ep)
+{
+ const char *sp;
+ char *n;
+ int i, at;
+
+ sp = atom;
+ if (*sp == '*' && sp < ep)
+ sp++; /* deref */
+ if (ep <= sp)
+ die("malformed field name: %.*s", (int)(ep-atom), atom);
+
+ /* Do we have the atom already used elsewhere? */
+ for (i = 0; i < used_atom_cnt; i++) {
+ int len = strlen(used_atom[i]);
+ if (len == ep - atom && !memcmp(used_atom[i], atom, len))
+ return i;
+ }
+
+ /* Is the atom a valid one? */
+ for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
+ int len = strlen(valid_atom[i].name);
+ if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
+ break;
+ }
+
+ if (ARRAY_SIZE(valid_atom) <= i)
+ die("unknown field name: %.*s", (int)(ep-atom), atom);
+
+ /* Add it in, including the deref prefix */
+ at = used_atom_cnt;
+ used_atom_cnt++;
+ used_atom = xrealloc(used_atom,
+ (sizeof *used_atom) * used_atom_cnt);
+ used_atom_type = xrealloc(used_atom_type,
+ (sizeof(*used_atom_type) * used_atom_cnt));
+ n = xmalloc(ep - atom + 1);
+ memcpy(n, atom, ep - atom);
+ n[ep-atom] = 0;
+ used_atom[at] = n;
+ used_atom_type[at] = valid_atom[i].cmp_type;
+ return at;
+}
+
+/*
+ * In a format string, find the next occurrence of %(atom).
+ */
+static const char *find_next(const char *cp)
+{
+ while (*cp) {
+ if (*cp == '%') {
+ /* %( is the start of an atom;
+ * %% is a quoteed per-cent.
+ */
+ if (cp[1] == '(')
+ return cp;
+ else if (cp[1] == '%')
+ cp++; /* skip over two % */
+ /* otherwise this is a singleton, literal % */
+ }
+ cp++;
+ }
+ return NULL;
+}
+
+/*
+ * Make sure the format string is well formed, and parse out
+ * the used atoms.
+ */
+static void verify_format(const char *format)
+{
+ const char *cp, *sp;
+ for (cp = format; *cp && (sp = find_next(cp)); ) {
+ const char *ep = strchr(sp, ')');
+ if (!ep)
+ die("malformatted format string %s", sp);
+ /* sp points at "%(" and ep points at the closing ")" */
+ parse_atom(sp + 2, ep);
+ cp = ep + 1;
+ }
+}
+
+/*
+ * Given an object name, read the object data and size, and return a
+ * "struct object". If the object data we are returning is also borrowed
+ * by the "struct object" representation, set *eaten as well---it is a
+ * signal from parse_object_buffer to us not to free the buffer.
+ */
+static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
+{
+ char type[20];
+ void *buf = read_sha1_file(sha1, type, sz);
+
+ if (buf)
+ *obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
+ else
+ *obj = NULL;
+ return buf;
+}
+
+/* See grab_values */
+static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+ int i;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &val[i];
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+ if (!strcmp(name, "objecttype"))
+ v->s = type_names[obj->type];
+ else if (!strcmp(name, "objectsize")) {
+ char *s = xmalloc(40);
+ sprintf(s, "%lu", sz);
+ v->ul = sz;
+ v->s = s;
+ }
+ else if (!strcmp(name, "objectname")) {
+ char *s = xmalloc(41);
+ strcpy(s, sha1_to_hex(obj->sha1));
+ v->s = s;
+ }
+ }
+}
+
+/* See grab_values */
+static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+ int i;
+ struct tag *tag = (struct tag *) obj;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &val[i];
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+ if (!strcmp(name, "tag"))
+ v->s = tag->tag;
+ }
+}
+
+static int num_parents(struct commit *commit)
+{
+ struct commit_list *parents;
+ int i;
+
+ for (i = 0, parents = commit->parents;
+ parents;
+ parents = parents->next)
+ i++;
+ return i;
+}
+
+/* See grab_values */
+static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+ int i;
+ struct commit *commit = (struct commit *) obj;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &val[i];
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+ if (!strcmp(name, "tree")) {
+ char *s = xmalloc(41);
+ strcpy(s, sha1_to_hex(commit->tree->object.sha1));
+ v->s = s;
+ }
+ if (!strcmp(name, "numparent")) {
+ char *s = xmalloc(40);
+ sprintf(s, "%lu", v->ul);
+ v->s = s;
+ v->ul = num_parents(commit);
+ }
+ else if (!strcmp(name, "parent")) {
+ int num = num_parents(commit);
+ int i;
+ struct commit_list *parents;
+ char *s = xmalloc(42 * num);
+ v->s = s;
+ for (i = 0, parents = commit->parents;
+ parents;
+ parents = parents->next, i = i + 42) {
+ struct commit *parent = parents->item;
+ strcpy(s+i, sha1_to_hex(parent->object.sha1));
+ if (parents->next)
+ s[i+40] = ' ';
+ }
+ }
+ }
+}
+
+static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
+{
+ const char *eol;
+ while (*buf) {
+ if (!strncmp(buf, who, wholen) &&
+ buf[wholen] == ' ')
+ return buf + wholen + 1;
+ eol = strchr(buf, '\n');
+ if (!eol)
+ return "";
+ eol++;
+ if (eol[1] == '\n')
+ return ""; /* end of header */
+ buf = eol;
+ }
+ return "";
+}
+
+static char *copy_line(const char *buf)
+{
+ const char *eol = strchr(buf, '\n');
+ char *line;
+ int len;
+ if (!eol)
+ return "";
+ len = eol - buf;
+ line = xmalloc(len + 1);
+ memcpy(line, buf, len);
+ line[len] = 0;
+ return line;
+}
+
+static char *copy_name(const char *buf)
+{
+ const char *eol = strchr(buf, '\n');
+ const char *eoname = strstr(buf, " <");
+ char *line;
+ int len;
+ if (!(eoname && eol && eoname < eol))
+ return "";
+ len = eoname - buf;
+ line = xmalloc(len + 1);
+ memcpy(line, buf, len);
+ line[len] = 0;
+ return line;
+}
+
+static char *copy_email(const char *buf)
+{
+ const char *email = strchr(buf, '<');
+ const char *eoemail = strchr(email, '>');
+ char *line;
+ int len;
+ if (!email || !eoemail)
+ return "";
+ eoemail++;
+ len = eoemail - email;
+ line = xmalloc(len + 1);
+ memcpy(line, email, len);
+ line[len] = 0;
+ return line;
+}
+
+static void grab_date(const char *buf, struct atom_value *v)
+{
+ const char *eoemail = strstr(buf, "> ");
+ char *zone;
+ unsigned long timestamp;
+ long tz;
+
+ if (!eoemail)
+ goto bad;
+ timestamp = strtoul(eoemail + 2, &zone, 10);
+ if (timestamp == ULONG_MAX)
+ goto bad;
+ tz = strtol(zone, NULL, 10);
+ if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
+ goto bad;
+ v->s = xstrdup(show_date(timestamp, tz, 0));
+ v->ul = timestamp;
+ return;
+ bad:
+ v->s = "";
+ v->ul = 0;
+}
+
+/* See grab_values */
+static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+ int i;
+ int wholen = strlen(who);
+ const char *wholine = NULL;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &val[i];
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+ if (strncmp(who, name, wholen))
+ continue;
+ if (name[wholen] != 0 &&
+ strcmp(name + wholen, "name") &&
+ strcmp(name + wholen, "email") &&
+ strcmp(name + wholen, "date"))
+ continue;
+ if (!wholine)
+ wholine = find_wholine(who, wholen, buf, sz);
+ if (!wholine)
+ return; /* no point looking for it */
+ if (name[wholen] == 0)
+ v->s = copy_line(wholine);
+ else if (!strcmp(name + wholen, "name"))
+ v->s = copy_name(wholine);
+ else if (!strcmp(name + wholen, "email"))
+ v->s = copy_email(wholine);
+ else if (!strcmp(name + wholen, "date"))
+ grab_date(wholine, v);
+ }
+}
+
+static void find_subpos(const char *buf, unsigned long sz, const char **sub, const char **body)
+{
+ while (*buf) {
+ const char *eol = strchr(buf, '\n');
+ if (!eol)
+ return;
+ if (eol[1] == '\n') {
+ buf = eol + 1;
+ break; /* found end of header */
+ }
+ buf = eol + 1;
+ }
+ while (*buf == '\n')
+ buf++;
+ if (!*buf)
+ return;
+ *sub = buf; /* first non-empty line */
+ buf = strchr(buf, '\n');
+ if (!buf)
+ return; /* no body */
+ while (*buf == '\n')
+ buf++; /* skip blank between subject and body */
+ *body = buf;
+}
+
+/* See grab_values */
+static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+ int i;
+ const char *subpos = NULL, *bodypos = NULL;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &val[i];
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+ if (strcmp(name, "subject") &&
+ strcmp(name, "body") &&
+ strcmp(name, "contents"))
+ continue;
+ if (!subpos)
+ find_subpos(buf, sz, &subpos, &bodypos);
+ if (!subpos)
+ return;
+
+ if (!strcmp(name, "subject"))
+ v->s = copy_line(subpos);
+ else if (!strcmp(name, "body"))
+ v->s = bodypos;
+ else if (!strcmp(name, "contents"))
+ v->s = subpos;
+ }
+}
+
+/* We want to have empty print-string for field requests
+ * that do not apply (e.g. "authordate" for a tag object)
+ */
+static void fill_missing_values(struct atom_value *val)
+{
+ int i;
+ for (i = 0; i < used_atom_cnt; i++) {
+ struct atom_value *v = &val[i];
+ if (v->s == NULL)
+ v->s = "";
+ }
+}
+
+/*
+ * val is a list of atom_value to hold returned values. Extract
+ * the values for atoms in used_atom array out of (obj, buf, sz).
+ * when deref is false, (obj, buf, sz) is the object that is
+ * pointed at by the ref itself; otherwise it is the object the
+ * ref (which is a tag) refers to.
+ */
+static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
+{
+ grab_common_values(val, deref, obj, buf, sz);
+ switch (obj->type) {
+ case OBJ_TAG:
+ grab_tag_values(val, deref, obj, buf, sz);
+ grab_sub_body_contents(val, deref, obj, buf, sz);
+ grab_person("tagger", val, deref, obj, buf, sz);
+ break;
+ case OBJ_COMMIT:
+ grab_commit_values(val, deref, obj, buf, sz);
+ grab_sub_body_contents(val, deref, obj, buf, sz);
+ grab_person("author", val, deref, obj, buf, sz);
+ grab_person("committer", val, deref, obj, buf, sz);
+ break;
+ case OBJ_TREE:
+ // grab_tree_values(val, deref, obj, buf, sz);
+ break;
+ case OBJ_BLOB:
+ // grab_blob_values(val, deref, obj, buf, sz);
+ break;
+ default:
+ die("Eh? Object of type %d?", obj->type);
+ }
+}
+
+/*
+ * Parse the object referred by ref, and grab needed value.
+ */
+static void populate_value(struct refinfo *ref)
+{
+ void *buf;
+ struct object *obj;
+ int eaten, i;
+ unsigned long size;
+ const unsigned char *tagged;
+
+ ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
+
+ buf = get_obj(ref->objectname, &obj, &size, &eaten);
+ if (!buf)
+ die("missing object %s for %s",
+ sha1_to_hex(ref->objectname), ref->refname);
+ if (!obj)
+ die("parse_object_buffer failed on %s for %s",
+ sha1_to_hex(ref->objectname), ref->refname);
+
+ /* Fill in specials first */
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &ref->value[i];
+ if (!strcmp(name, "refname"))
+ v->s = ref->refname;
+ else if (!strcmp(name, "*refname")) {
+ int len = strlen(ref->refname);
+ char *s = xmalloc(len + 4);
+ sprintf(s, "%s^{}", ref->refname);
+ v->s = s;
+ }
+ }
+
+ grab_values(ref->value, 0, obj, buf, size);
+ if (!eaten)
+ free(buf);
+
+ /* If there is no atom that wants to know about tagged
+ * object, we are done.
+ */
+ if (!need_tagged || (obj->type != OBJ_TAG))
+ return;
+
+ /* If it is a tag object, see if we use a value that derefs
+ * the object, and if we do grab the object it refers to.
+ */
+ tagged = ((struct tag *)obj)->tagged->sha1;
+
+ /* NEEDSWORK: This derefs tag only once, which
+ * is good to deal with chains of trust, but
+ * is not consistent with what deref_tag() does
+ * which peels the onion to the core.
+ */
+ buf = get_obj(tagged, &obj, &size, &eaten);
+ if (!buf)
+ die("missing object %s for %s",
+ sha1_to_hex(tagged), ref->refname);
+ if (!obj)
+ die("parse_object_buffer failed on %s for %s",
+ sha1_to_hex(tagged), ref->refname);
+ grab_values(ref->value, 1, obj, buf, size);
+ if (!eaten)
+ free(buf);
+}
+
+/*
+ * Given a ref, return the value for the atom. This lazily gets value
+ * out of the object by calling populate value.
+ */
+static void get_value(struct refinfo *ref, int atom, struct atom_value **v)
+{
+ if (!ref->value) {
+ populate_value(ref);
+ fill_missing_values(ref->value);
+ }
+ *v = &ref->value[atom];
+}
+
+struct grab_ref_cbdata {
+ struct refinfo **grab_array;
+ const char **grab_pattern;
+ int grab_cnt;
+};
+
+/*
+ * A call-back given to for_each_ref(). It is unfortunate that we
+ * need to use global variables to pass extra information to this
+ * function.
+ */
+static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+ struct grab_ref_cbdata *cb = cb_data;
+ struct refinfo *ref;
+ int cnt;
+
+ if (*cb->grab_pattern) {
+ const char **pattern;
+ int namelen = strlen(refname);
+ for (pattern = cb->grab_pattern; *pattern; pattern++) {
+ const char *p = *pattern;
+ int plen = strlen(p);
+
+ if ((plen <= namelen) &&
+ !strncmp(refname, p, plen) &&
+ (refname[plen] == '\0' ||
+ refname[plen] == '/'))
+ break;
+ if (!fnmatch(p, refname, FNM_PATHNAME))
+ break;
+ }
+ if (!*pattern)
+ return 0;
+ }
+
+ /* We do not open the object yet; sort may only need refname
+ * to do its job and the resulting list may yet to be pruned
+ * by maxcount logic.
+ */
+ ref = xcalloc(1, sizeof(*ref));
+ ref->refname = xstrdup(refname);
+ hashcpy(ref->objectname, sha1);
+
+ cnt = cb->grab_cnt;
+ cb->grab_array = xrealloc(cb->grab_array,
+ sizeof(*cb->grab_array) * (cnt + 1));
+ cb->grab_array[cnt++] = ref;
+ cb->grab_cnt = cnt;
+ return 0;
+}
+
+static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
+{
+ struct atom_value *va, *vb;
+ int cmp;
+ cmp_type cmp_type = used_atom_type[s->atom];
+
+ get_value(a, s->atom, &va);
+ get_value(b, s->atom, &vb);
+ switch (cmp_type) {
+ case FIELD_STR:
+ cmp = strcmp(va->s, vb->s);
+ break;
+ default:
+ if (va->ul < vb->ul)
+ cmp = -1;
+ else if (va->ul == vb->ul)
+ cmp = 0;
+ else
+ cmp = 1;
+ break;
+ }
+ return (s->reverse) ? -cmp : cmp;
+}
+
+static struct ref_sort *ref_sort;
+static int compare_refs(const void *a_, const void *b_)
+{
+ struct refinfo *a = *((struct refinfo **)a_);
+ struct refinfo *b = *((struct refinfo **)b_);
+ struct ref_sort *s;
+
+ for (s = ref_sort; s; s = s->next) {
+ int cmp = cmp_ref_sort(s, a, b);
+ if (cmp)
+ return cmp;
+ }
+ return 0;
+}
+
+static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs)
+{
+ ref_sort = sort;
+ qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);
+}
+
+static void print_value(struct refinfo *ref, int atom, int quote_style)
+{
+ struct atom_value *v;
+ get_value(ref, atom, &v);
+ switch (quote_style) {
+ case QUOTE_NONE:
+ fputs(v->s, stdout);
+ break;
+ case QUOTE_SHELL:
+ sq_quote_print(stdout, v->s);
+ break;
+ case QUOTE_PERL:
+ perl_quote_print(stdout, v->s);
+ break;
+ case QUOTE_PYTHON:
+ python_quote_print(stdout, v->s);
+ break;
+ }
+}
+
+static int hex1(char ch)
+{
+ if ('0' <= ch && ch <= '9')
+ return ch - '0';
+ else if ('a' <= ch && ch <= 'f')
+ return ch - 'a' + 10;
+ else if ('A' <= ch && ch <= 'F')
+ return ch - 'A' + 10;
+ return -1;
+}
+static int hex2(const char *cp)
+{
+ if (cp[0] && cp[1])
+ return (hex1(cp[0]) << 4) | hex1(cp[1]);
+ else
+ return -1;
+}
+
+static void emit(const char *cp, const char *ep)
+{
+ while (*cp && (!ep || cp < ep)) {
+ if (*cp == '%') {
+ if (cp[1] == '%')
+ cp++;
+ else {
+ int ch = hex2(cp + 1);
+ if (0 <= ch) {
+ putchar(ch);
+ cp += 3;
+ continue;
+ }
+ }
+ }
+ putchar(*cp);
+ cp++;
+ }
+}
+
+static void show_ref(struct refinfo *info, const char *format, int quote_style)
+{
+ const char *cp, *sp, *ep;
+
+ for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
+ ep = strchr(sp, ')');
+ if (cp < sp)
+ emit(cp, sp);
+ print_value(info, parse_atom(sp + 2, ep), quote_style);
+ }
+ if (*cp) {
+ sp = cp + strlen(cp);
+ emit(cp, sp);
+ }
+ putchar('\n');
+}
+
+static struct ref_sort *default_sort(void)
+{
+ static const char cstr_name[] = "refname";
+
+ struct ref_sort *sort = xcalloc(1, sizeof(*sort));
+
+ sort->next = NULL;
+ sort->atom = parse_atom(cstr_name, cstr_name + strlen(cstr_name));
+ return sort;
+}
+
+int cmd_for_each_ref(int ac, const char **av, char *prefix)
+{
+ int i, num_refs;
+ const char *format = NULL;
+ struct ref_sort *sort = NULL, **sort_tail = &sort;
+ int maxcount = 0;
+ int quote_style = -1; /* unspecified yet */
+ struct refinfo **refs;
+ struct grab_ref_cbdata cbdata;
+
+ for (i = 1; i < ac; i++) {
+ const char *arg = av[i];
+ if (arg[0] != '-')
+ break;
+ if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ if (!strncmp(arg, "--format=", 9)) {
+ if (format)
+ die("more than one --format?");
+ format = arg + 9;
+ continue;
+ }
+ if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) {
+ if (0 <= quote_style)
+ die("more than one quoting style?");
+ quote_style = QUOTE_SHELL;
+ continue;
+ }
+ if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) {
+ if (0 <= quote_style)
+ die("more than one quoting style?");
+ quote_style = QUOTE_PERL;
+ continue;
+ }
+ if (!strcmp(arg, "--python") ) {
+ if (0 <= quote_style)
+ die("more than one quoting style?");
+ quote_style = QUOTE_PYTHON;
+ continue;
+ }
+ if (!strncmp(arg, "--count=", 8)) {
+ if (maxcount)
+ die("more than one --count?");
+ maxcount = atoi(arg + 8);
+ if (maxcount <= 0)
+ die("The number %s did not parse", arg);
+ continue;
+ }
+ if (!strncmp(arg, "--sort=", 7)) {
+ struct ref_sort *s = xcalloc(1, sizeof(*s));
+ int len;
+
+ s->next = NULL;
+ *sort_tail = s;
+ sort_tail = &s->next;
+
+ arg += 7;
+ if (*arg == '-') {
+ s->reverse = 1;
+ arg++;
+ }
+ len = strlen(arg);
+ sort->atom = parse_atom(arg, arg+len);
+ continue;
+ }
+ break;
+ }
+ if (quote_style < 0)
+ quote_style = QUOTE_NONE;
+
+ if (!sort)
+ sort = default_sort();
+ sort_atom_limit = used_atom_cnt;
+ if (!format)
+ format = "%(objectname) %(objecttype)\t%(refname)";
+
+ verify_format(format);
+
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.grab_pattern = av + i;
+ for_each_ref(grab_single_ref, &cbdata);
+ refs = cbdata.grab_array;
+ num_refs = cbdata.grab_cnt;
+
+ for (i = 0; i < used_atom_cnt; i++) {
+ if (used_atom[i][0] == '*') {
+ need_tagged = 1;
+ break;
+ }
+ }
+
+ sort_refs(sort, refs, num_refs);
+
+ if (!maxcount || num_refs < maxcount)
+ maxcount = num_refs;
+ for (i = 0; i < maxcount; i++)
+ show_ref(refs[i], format, quote_style);
+ return 0;
+}
diff --git a/builtin-init-db.c b/builtin-init-db.c
index c3ed1ce..235a0ee 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -218,8 +218,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
* branch, if it does not exist yet.
*/
strcpy(path + len, "HEAD");
- if (read_ref(path, sha1) < 0) {
- if (create_symref(path, "refs/heads/master") < 0)
+ if (read_ref("HEAD", sha1) < 0) {
+ if (create_symref("HEAD", "refs/heads/master") < 0)
exit(1);
}
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index 52886b6..618aa31 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -75,11 +75,10 @@ copy_data:
}
}
-static int tags_only;
-
-static int name_ref(const char *path, const unsigned char *sha1)
+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
{
struct object *o = parse_object(sha1);
+ int tags_only = *(int*)cb_data;
int deref = 0;
if (tags_only && strncmp(path, "refs/tags/", 10))
@@ -131,6 +130,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = { 0, 0, NULL };
int as_is = 0, all = 0, transform_stdin = 0;
+ int tags_only = 0;
git_config(git_default_config);
@@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
add_object_array((struct object *)commit, *argv, &revs);
}
- for_each_ref(name_ref);
+ for_each_ref(name_ref, &tags_only);
if (transform_stdin) {
char buffer[2048];
diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
new file mode 100644
index 0000000..4093973
--- /dev/null
+++ b/builtin-pack-refs.c
@@ -0,0 +1,112 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char *result_path, *lock_path;
+static const char builtin_pack_refs_usage[] =
+"git-pack-refs [--prune]";
+
+struct ref_to_prune {
+ struct ref_to_prune *next;
+ unsigned char sha1[20];
+ char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+ int prune;
+ struct ref_to_prune *ref_to_prune;
+ FILE *refs_file;
+};
+
+static void remove_lock_file(void)
+{
+ if (lock_path)
+ unlink(lock_path);
+}
+
+static int do_not_prune(int flags)
+{
+ /* If it is already packed or if it is a symref,
+ * do not prune it.
+ */
+ return (flags & (REF_ISSYMREF|REF_ISPACKED));
+}
+
+static int handle_one_ref(const char *path, const unsigned char *sha1,
+ int flags, void *cb_data)
+{
+ struct pack_refs_cb_data *cb = cb_data;
+
+ /* Do not pack the symbolic refs */
+ if (!(flags & REF_ISSYMREF))
+ fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+ if (cb->prune && !do_not_prune(flags)) {
+ int namelen = strlen(path) + 1;
+ struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+ hashcpy(n->sha1, sha1);
+ strcpy(n->name, path);
+ n->next = cb->ref_to_prune;
+ cb->ref_to_prune = n;
+ }
+ return 0;
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+ struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
+
+ if (lock) {
+ unlink(git_path("%s", r->name));
+ unlock_ref(lock);
+ }
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+ while (r) {
+ prune_ref(r);
+ r = r->next;
+ }
+}
+
+int cmd_pack_refs(int argc, const char **argv, const char *prefix)
+{
+ int fd, i;
+ struct pack_refs_cb_data cbdata;
+
+ memset(&cbdata, 0, sizeof(cbdata));
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--prune")) {
+ cbdata.prune = 1;
+ continue;
+ }
+ /* perhaps other parameters later... */
+ break;
+ }
+ if (i != argc)
+ usage(builtin_pack_refs_usage);
+
+ result_path = xstrdup(git_path("packed-refs"));
+ lock_path = xstrdup(mkpath("%s.lock", result_path));
+
+ fd = open(lock_path, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0)
+ die("unable to create new ref-pack file (%s)", strerror(errno));
+ atexit(remove_lock_file);
+
+ cbdata.refs_file = fdopen(fd, "w");
+ if (!cbdata.refs_file)
+ die("unable to create ref-pack file structure (%s)",
+ strerror(errno));
+ for_each_ref(handle_one_ref, &cbdata);
+ fsync(fd);
+ fclose(cbdata.refs_file);
+ if (rename(lock_path, result_path) < 0)
+ die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+ lock_path = NULL;
+ if (cbdata.prune)
+ prune_refs(cbdata.ref_to_prune);
+ return 0;
+}
diff --git a/builtin-prune.c b/builtin-prune.c
index 6228c79..e79b515 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs)
}
}
-static int add_one_ref(const char *path, const unsigned char *sha1)
+static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = parse_object(sha1);
if (!object)
@@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
revs.tree_objects = 1;
/* Add all external refs */
- for_each_ref(add_one_ref);
+ for_each_ref(add_one_ref, NULL);
/* Add all refs from the index file */
add_cache_refs();
diff --git a/builtin-push.c b/builtin-push.c
index c43f256..581c44b 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -27,7 +27,7 @@ static void add_refspec(const char *ref)
refspec_nr = nr;
}
-static int expand_one_ref(const char *ref, const unsigned char *sha1)
+static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)
{
/* Ignore the "refs/" at the beginning of the refname */
ref += 5;
@@ -51,7 +51,7 @@ static void expand_refspecs(void)
}
if (!tags)
return;
- for_each_ref(expand_one_ref);
+ for_each_ref(expand_one_ref, NULL);
}
static void set_refspecs(const char **refs, int nr)
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index fd3ccc8..3b716fb 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -137,7 +137,7 @@ static void show_default(void)
}
}
-static int show_reference(const char *refname, const unsigned char *sha1)
+static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
show_rev(NORMAL, sha1, refname);
return 0;
@@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--all")) {
- for_each_ref(show_reference);
+ for_each_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--branches")) {
- for_each_branch_ref(show_reference);
+ for_each_branch_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--tags")) {
- for_each_tag_ref(show_reference);
+ for_each_tag_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--remotes")) {
- for_each_remote_ref(show_reference);
+ for_each_remote_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--show-prefix")) {
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 578c9fa..fb1a400 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top)
compare_ref_name);
}
-static int append_ref(const char *refname, const unsigned char *sha1)
+static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
int i;
@@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1)
return 0;
}
-static int append_head_ref(const char *refname, const unsigned char *sha1)
+static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
unsigned char tmp[20];
int ofs = 11;
@@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1)
*/
if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
ofs = 5;
- return append_ref(refname + ofs, sha1);
+ return append_ref(refname + ofs, sha1, flag, cb_data);
}
-static int append_tag_ref(const char *refname, const unsigned char *sha1)
+static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
if (strncmp(refname, "refs/tags/", 10))
return 0;
- return append_ref(refname + 5, sha1);
+ return append_ref(refname + 5, sha1, flag, cb_data);
}
static const char *match_ref_pattern = NULL;
@@ -401,7 +401,7 @@ static int count_slash(const char *s)
return cnt;
}
-static int append_matching_ref(const char *refname, const unsigned char *sha1)
+static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
@@ -417,41 +417,39 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1)
if (fnmatch(match_ref_pattern, tail, 0))
return 0;
if (!strncmp("refs/heads/", refname, 11))
- return append_head_ref(refname, sha1);
+ return append_head_ref(refname, sha1, flag, cb_data);
if (!strncmp("refs/tags/", refname, 10))
- return append_tag_ref(refname, sha1);
- return append_ref(refname, sha1);
+ return append_tag_ref(refname, sha1, flag, cb_data);
+ return append_ref(refname, sha1, flag, cb_data);
}
static void snarf_refs(int head, int tag)
{
if (head) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_head_ref);
+ for_each_ref(append_head_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
if (tag) {
int orig_cnt = ref_name_cnt;
- for_each_ref(append_tag_ref);
+ for_each_ref(append_tag_ref, NULL);
sort_ref_range(orig_cnt, ref_name_cnt);
}
}
-static int rev_is_head(char *head_path, int headlen, char *name,
+static int rev_is_head(char *head, int headlen, char *name,
unsigned char *head_sha1, unsigned char *sha1)
{
- int namelen;
- if ((!head_path[0]) ||
+ if ((!head[0]) ||
(head_sha1 && sha1 && hashcmp(head_sha1, sha1)))
return 0;
- namelen = strlen(name);
- if ((headlen < namelen) ||
- memcmp(head_path + headlen - namelen, name, namelen))
- return 0;
- if (headlen == namelen ||
- head_path[headlen - namelen - 1] == '/')
- return 1;
- return 0;
+ if (!strncmp(head, "refs/heads/", 11))
+ head += 11;
+ if (!strncmp(name, "refs/heads/", 11))
+ name += 11;
+ else if (!strncmp(name, "heads/", 6))
+ name += 6;
+ return !strcmp(head, name);
}
static int show_merge_base(struct commit_list *seen, int num_rev)
@@ -495,7 +493,7 @@ static void append_one_rev(const char *av)
{
unsigned char revkey[20];
if (!get_sha1(av, revkey)) {
- append_ref(av, revkey);
+ append_ref(av, revkey, 0, NULL);
return;
}
if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
@@ -503,7 +501,7 @@ static void append_one_rev(const char *av)
int saved_matches = ref_name_cnt;
match_ref_pattern = av;
match_ref_slash = count_slash(av);
- for_each_ref(append_matching_ref);
+ for_each_ref(append_matching_ref, NULL);
if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS)
error("no matching refs with %s", av);
@@ -559,9 +557,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
int all_heads = 0, all_tags = 0;
int all_mask, all_revs;
int lifo = 1;
- char head_path[128];
- const char *head_path_p;
- int head_path_len;
+ char head[128];
+ const char *head_p;
+ int head_len;
unsigned char head_sha1[20];
int merge_base = 0;
int independent = 0;
@@ -638,31 +636,31 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
ac--; av++;
}
- head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
- if (head_path_p) {
- head_path_len = strlen(head_path_p);
- memcpy(head_path, head_path_p, head_path_len + 1);
+ head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+ if (head_p) {
+ head_len = strlen(head_p);
+ memcpy(head, head_p, head_len + 1);
}
else {
- head_path_len = 0;
- head_path[0] = 0;
+ head_len = 0;
+ head[0] = 0;
}
- if (with_current_branch && head_path_p) {
+ if (with_current_branch && head_p) {
int has_head = 0;
for (i = 0; !has_head && i < ref_name_cnt; i++) {
/* We are only interested in adding the branch
* HEAD points at.
*/
- if (rev_is_head(head_path,
- head_path_len,
+ if (rev_is_head(head,
+ head_len,
ref_name[i],
head_sha1, NULL))
has_head++;
}
if (!has_head) {
- int pfxlen = strlen(git_path("refs/heads/"));
- append_one_rev(head_path + pfxlen);
+ int pfxlen = strlen("refs/heads/");
+ append_one_rev(head + pfxlen);
}
}
@@ -713,8 +711,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
if (1 < num_rev || extra < 0) {
for (i = 0; i < num_rev; i++) {
int j;
- int is_head = rev_is_head(head_path,
- head_path_len,
+ int is_head = rev_is_head(head,
+ head_len,
ref_name[i],
head_sha1,
rev[i]->object.sha1);
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
new file mode 100644
index 0000000..12c457c
--- /dev/null
+++ b/builtin-show-ref.c
@@ -0,0 +1,120 @@
+#include "cache.h"
+#include "refs.h"
+#include "object.h"
+#include "tag.h"
+
+static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash] [--tags] [--heads] [--] [pattern*]";
+
+static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
+ found_match = 0, verify = 0, quiet = 0, hash_only = 0;
+static const char **pattern;
+
+static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+ struct object *obj;
+
+ if (tags_only || heads_only) {
+ int match;
+
+ match = heads_only && !strncmp(refname, "refs/heads/", 11);
+ match |= tags_only && !strncmp(refname, "refs/tags/", 10);
+ if (!match)
+ return 0;
+ }
+ if (pattern) {
+ int reflen = strlen(refname);
+ const char **p = pattern, *m;
+ while ((m = *p++) != NULL) {
+ int len = strlen(m);
+ if (len > reflen)
+ continue;
+ if (memcmp(m, refname + reflen - len, len))
+ continue;
+ if (len == reflen)
+ goto match;
+ /* "--verify" requires an exact match */
+ if (verify)
+ continue;
+ if (refname[reflen - len - 1] == '/')
+ goto match;
+ }
+ return 0;
+ }
+
+match:
+ found_match++;
+ obj = parse_object(sha1);
+ if (!obj) {
+ if (quiet)
+ return 0;
+ die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1));
+ }
+ if (quiet)
+ return 0;
+ if (hash_only)
+ printf("%s\n", sha1_to_hex(sha1));
+ else
+ printf("%s %s\n", sha1_to_hex(sha1), refname);
+ if (deref_tags && obj->type == OBJ_TAG) {
+ obj = deref_tag(obj, refname, 0);
+ printf("%s %s^{}\n", sha1_to_hex(obj->sha1), refname);
+ }
+ return 0;
+}
+
+int cmd_show_ref(int argc, const char **argv, const char *prefix)
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (*arg != '-') {
+ pattern = argv + i;
+ break;
+ }
+ if (!strcmp(arg, "--")) {
+ pattern = argv + i + 1;
+ if (!*pattern)
+ pattern = NULL;
+ break;
+ }
+ if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
+ quiet = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-h") || !strcmp(arg, "--head")) {
+ show_head = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-d") || !strcmp(arg, "--dereference")) {
+ deref_tags = 1;
+ continue;
+ }
+ if (!strcmp(arg, "-s") || !strcmp(arg, "--hash")) {
+ hash_only = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--verify")) {
+ verify = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--tags")) {
+ tags_only = 1;
+ continue;
+ }
+ if (!strcmp(arg, "--heads")) {
+ heads_only = 1;
+ continue;
+ }
+ usage(show_ref_usage);
+ }
+ if (show_head)
+ head_ref(show_ref, NULL);
+ for_each_ref(show_ref, NULL);
+ if (!found_match) {
+ if (verify && !quiet)
+ die("No match");
+ return 1;
+ }
+ return 0;
+}
diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c
index 1d3a5e2..d8be052 100644
--- a/builtin-symbolic-ref.c
+++ b/builtin-symbolic-ref.c
@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
+#include "refs.h"
static const char git_symbolic_ref_usage[] =
"git-symbolic-ref name [ref]";
@@ -7,15 +8,14 @@ static const char git_symbolic_ref_usage[] =
static void check_symref(const char *HEAD)
{
unsigned char sha1[20];
- const char *git_HEAD = xstrdup(git_path("%s", HEAD));
- const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);
- if (git_refs_heads_master) {
- /* we want to strip the .git/ part */
- int pfxlen = strlen(git_HEAD) - strlen(HEAD);
- puts(git_refs_heads_master + pfxlen);
- }
- else
+ int flag;
+ const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+
+ if (!refs_heads_master)
die("No such ref: %s", HEAD);
+ else if (!(flag & REF_ISSYMREF))
+ die("ref %s is not a symbolic ref", HEAD);
+ puts(refs_heads_master);
}
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
@@ -26,7 +26,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
check_symref(argv[1]);
break;
case 3:
- create_symref(xstrdup(git_path("%s", argv[1])), argv[2]);
+ create_symref(argv[1], argv[2]);
break;
default:
usage(git_symbolic_ref_usage);
diff --git a/builtin-update-index.c b/builtin-update-index.c
index a3c0a45..7f9c638 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -406,9 +406,9 @@ static int unresolve_one(const char *path)
static void read_head_pointers(void)
{
- if (read_ref(git_path("HEAD"), head_sha1))
+ if (read_ref("HEAD", head_sha1))
die("No HEAD -- no initial commit yet?\n");
- if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) {
+ if (read_ref("MERGE_HEAD", merge_head_sha1)) {
fprintf(stderr, "Not in the middle of a merge.\n");
exit(0);
}
@@ -445,7 +445,7 @@ static int do_reupdate(int ac, const char **av,
int has_head = 1;
const char **pathspec = get_pathspec(prefix, av + 1);
- if (read_ref(git_path("HEAD"), head_sha1))
+ if (read_ref("HEAD", head_sha1))
/* If there is no HEAD, that means it is an initial
* commit. Update everything in the index.
*/
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 90a3da5..b34e598 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -3,15 +3,16 @@
#include "builtin.h"
static const char git_update_ref_usage[] =
-"git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
+"git-update-ref [-m <reason>] (-d <refname> <value> | <refname> <value> [<oldval>])";
int cmd_update_ref(int argc, const char **argv, const char *prefix)
{
const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
struct ref_lock *lock;
unsigned char sha1[20], oldsha1[20];
- int i;
+ int i, delete;
+ delete = 0;
setup_ident();
git_config(git_default_config);
@@ -26,6 +27,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with \\n in message.");
continue;
}
+ if (!strcmp("-d", argv[i])) {
+ delete = 1;
+ continue;
+ }
if (!refname) {
refname = argv[i];
continue;
@@ -44,11 +49,18 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
if (get_sha1(value, sha1))
die("%s: not a valid SHA1", value);
+
+ if (delete) {
+ if (oldval)
+ usage(git_update_ref_usage);
+ return delete_ref(refname, sha1);
+ }
+
hashclr(oldsha1);
- if (oldval && get_sha1(oldval, oldsha1))
+ if (oldval && *oldval && get_sha1(oldval, oldsha1))
die("%s: not a valid old SHA1", oldval);
- lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0);
+ lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL);
if (!lock)
return 1;
if (write_ref_sha1(lock, sha1, msg) < 0)
diff --git a/builtin.h b/builtin.h
index f9fa9ff..721b8d8 100644
--- a/builtin.h
+++ b/builtin.h
@@ -26,6 +26,7 @@ extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
extern int cmd_grep(int argc, const char **argv, const char *prefix);
@@ -48,8 +49,8 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
extern int cmd_rm(int argc, const char **argv, const char *prefix);
extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
-extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
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_tar_tree(int argc, const char **argv, const char *prefix);
@@ -62,5 +63,7 @@ extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
#endif
diff --git a/cache.h b/cache.h
index 97debd0..0565333 100644
--- a/cache.h
+++ b/cache.h
@@ -179,6 +179,7 @@ struct lock_file {
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
extern int commit_lock_file(struct lock_file *);
extern void rollback_lock_file(struct lock_file *);
+extern int delete_ref(const char *, unsigned char *sha1);
/* Environment bits from configuration mechanism */
extern int use_legacy_headers;
@@ -293,9 +294,9 @@ extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
-extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
-extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
-extern int validate_symref(const char *git_HEAD);
+extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+extern int create_symref(const char *ref, const char *refs_heads_master);
+extern int validate_symref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
diff --git a/describe.c b/describe.c
index ab192f8..f4029ee 100644
--- a/describe.c
+++ b/describe.c
@@ -53,7 +53,7 @@ static void add_to_known_names(const char *path,
names = ++idx;
}
-static int get_name(const char *path, const unsigned char *sha1)
+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
struct object *object;
@@ -113,7 +113,7 @@ static void describe(const char *arg, int last_one)
if (!initialized) {
initialized = 1;
- for_each_ref(get_name);
+ for_each_ref(get_name, NULL);
qsort(name_array, names, sizeof(*name_array), compare_names);
}
diff --git a/fetch-pack.c b/fetch-pack.c
index e8708aa..99ac08b 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark)
}
}
-static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = deref_tag(parse_object(sha1), path, 0);
@@ -143,7 +143,7 @@ static int find_common(int fd[2], unsigned char *result_sha1,
unsigned in_vain = 0;
int got_continue = 0;
- for_each_ref(rev_list_insert_ref);
+ for_each_ref(rev_list_insert_ref, NULL);
fetching = 0;
for ( ; refs ; refs = refs->next) {
@@ -253,7 +253,7 @@ done:
static struct commit_list *complete;
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = parse_object(sha1);
@@ -365,7 +365,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
}
}
- for_each_ref(mark_complete);
+ for_each_ref(mark_complete, NULL);
if (cutoff)
mark_recent_complete_commits(cutoff);
diff --git a/fetch.c b/fetch.c
index 34df8d3..c426c04 100644
--- a/fetch.c
+++ b/fetch.c
@@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1)
return -1;
}
-static int mark_complete(const char *path, const unsigned char *sha1)
+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct commit *commit = lookup_commit_reference_gently(sha1, 1);
if (commit) {
@@ -266,7 +266,7 @@ int pull(int targets, char **target, const char **write_ref,
if (!write_ref || !write_ref[i])
continue;
- lock[i] = lock_ref_sha1(write_ref[i], NULL, 0);
+ lock[i] = lock_ref_sha1(write_ref[i], NULL);
if (!lock[i]) {
error("Can't lock ref %s", write_ref[i]);
goto unlock_and_fail;
@@ -274,7 +274,7 @@ int pull(int targets, char **target, const char **write_ref,
}
if (!get_recover)
- for_each_ref(mark_complete);
+ for_each_ref(mark_complete, NULL);
for (i = 0; i < targets; i++) {
if (interpret_target(target[i], &sha1[20 * i])) {
diff --git a/fsck-objects.c b/fsck-objects.c
index 4d994f3..46b628c 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path)
static int default_refs;
-static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *obj;
@@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1)
static void get_default_heads(void)
{
- for_each_ref(fsck_handle_ref);
+ for_each_ref(fsck_handle_ref, NULL);
/*
* Not having any default heads isn't really fatal, but
@@ -458,15 +458,14 @@ static void fsck_object_dir(const char *path)
static int fsck_head_link(void)
{
unsigned char sha1[20];
- const char *git_HEAD = xstrdup(git_path("HEAD"));
- const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 1);
- int pfxlen = strlen(git_HEAD) - 4; /* strip .../.git/ part */
+ int flag;
+ const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
- if (!git_refs_heads_master)
+ if (!head_points_at || !(flag & REF_ISSYMREF))
return error("HEAD is not a symbolic ref");
- if (strncmp(git_refs_heads_master + pfxlen, "refs/heads/", 11))
+ if (strncmp(head_points_at, "refs/heads/", 11))
return error("HEAD points to something strange (%s)",
- git_refs_heads_master + pfxlen);
+ head_points_at);
if (is_null_sha1(sha1))
return error("HEAD: not a valid git pointer");
return 0;
diff --git a/git-branch.sh b/git-branch.sh
index 4f31903..bf84b30 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -21,7 +21,7 @@ delete_branch () {
,,)
die "What branch are you on anyway?" ;;
esac
- branch=$(cat "$GIT_DIR/refs/heads/$branch_name") &&
+ branch=$(git-show-ref --verify --hash -- "refs/heads/$branch_name") &&
branch=$(git-rev-parse --verify "$branch^0") ||
die "Seriously, what branch are you talking about?"
case "$option" in
@@ -42,8 +42,7 @@ If you are sure you want to delete it, run 'git branch -D $branch_name'."
esac
;;
esac
- rm -f "$GIT_DIR/logs/refs/heads/$branch_name"
- rm -f "$GIT_DIR/refs/heads/$branch_name"
+ git update-ref -d "refs/heads/$branch_name" "$branch"
echo "Deleted branch $branch_name."
done
exit 0
@@ -122,7 +121,8 @@ then
done
fi
-if [ -e "$GIT_DIR/refs/heads/$branchname" ]
+prev=''
+if git-show-ref --verify --quiet -- "refs/heads/$branchname"
then
if test '' = "$force"
then
@@ -131,10 +131,11 @@ then
then
die "cannot force-update the current branch."
fi
+ prev=`git rev-parse --verify "refs/heads/$branchname"`
fi
if test "$create_log" = 'yes'
then
mkdir -p $(dirname "$GIT_DIR/logs/refs/heads/$branchname")
touch "$GIT_DIR/logs/refs/heads/$branchname"
fi
-git update-ref -m "branch: Created from $head" "refs/heads/$branchname" $rev
+git update-ref -m "branch: Created from $head" "refs/heads/$branchname" "$rev" "$prev"
diff --git a/git-checkout.sh b/git-checkout.sh
index dd47724..119bca1 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -22,7 +22,7 @@ while [ "$#" != "0" ]; do
shift
[ -z "$newbranch" ] &&
die "git checkout: -b needs a branch name"
- [ -e "$GIT_DIR/refs/heads/$newbranch" ] &&
+ git-show-ref --verify --quiet -- "refs/heads/$newbranch" &&
die "git checkout: branch $newbranch already exists"
git-check-ref-format "heads/$newbranch" ||
die "git checkout: we do not like '$newbranch' as a branch name."
@@ -51,7 +51,8 @@ while [ "$#" != "0" ]; do
fi
new="$rev"
new_name="$arg^0"
- if [ -f "$GIT_DIR/refs/heads/$arg" ]; then
+ if git-show-ref --verify --quiet -- "refs/heads/$arg"
+ then
branch="$arg"
fi
elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
diff --git a/git-commit.sh b/git-commit.sh
index 5a4c659..ee5a165 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -547,15 +547,15 @@ then
PARENTS=$(git-cat-file commit HEAD |
sed -n -e '/^$/q' -e 's/^parent /-p /p')
fi
- current=$(git-rev-parse --verify HEAD)
+ current="$(git-rev-parse --verify HEAD)"
else
if [ -z "$(git-ls-files)" ]; then
echo >&2 Nothing to commit
exit 1
fi
PARENTS=""
- current=
rloga='commit (initial)'
+ current=''
fi
if test -z "$no_edit"
@@ -631,7 +631,7 @@ then
fi &&
commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
- git-update-ref -m "$rloga: $rlogm" HEAD $commit $current &&
+ git-update-ref -m "$rloga: $rlogm" HEAD $commit "$current" &&
rm -f -- "$GIT_DIR/MERGE_HEAD" &&
if test -f "$NEXT_INDEX"
then
diff --git a/git-tag.sh b/git-tag.sh
index a0afa25..2bde3c0 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -63,8 +63,11 @@ done
name="$1"
[ "$name" ] || usage
-if [ -e "$GIT_DIR/refs/tags/$name" -a -z "$force" ]; then
- die "tag '$name' already exists"
+prev=0000000000000000000000000000000000000000
+if test -e "$GIT_DIR/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" ||
@@ -109,4 +112,4 @@ fi
leading=`expr "refs/tags/$name" : '\(.*\)/'` &&
mkdir -p "$GIT_DIR/$leading" &&
-echo $object > "$GIT_DIR/refs/tags/$name"
+GIT_DIR="$GIT_DIR" git update-ref "refs/tags/$name" "$object" "$prev"
diff --git a/git.c b/git.c
index ae80e78..9108fec 100644
--- a/git.c
+++ b/git.c
@@ -232,6 +232,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "diff-stages", cmd_diff_stages, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
+ { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
{ "format-patch", cmd_format_patch, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP },
@@ -267,6 +268,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP },
{ "verify-pack", cmd_verify_pack },
+ { "show-ref", cmd_show_ref, RUN_SETUP },
+ { "pack-refs", cmd_pack_refs, RUN_SETUP },
};
int i;
diff --git a/http-push.c b/http-push.c
index 670ff00..ecefdfd 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock)
static struct ref *local_refs, **local_tail;
static struct ref *remote_refs, **remote_tail;
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct ref *ref;
int len = strlen(refname) + 1;
@@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname)
static void get_local_heads(void)
{
local_tail = &local_refs;
- for_each_ref(one_local_ref);
+ for_each_ref(one_local_ref, NULL);
}
static void get_dav_remote_heads(void)
diff --git a/object.c b/object.c
index 9281300..de244e2 100644
--- a/object.c
+++ b/object.c
@@ -138,42 +138,56 @@ struct object *lookup_unknown_object(const unsigned char *sha1)
return obj;
}
+struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p)
+{
+ struct object *obj;
+ int eaten = 0;
+
+ if (!strcmp(type, blob_type)) {
+ struct blob *blob = lookup_blob(sha1);
+ parse_blob_buffer(blob, buffer, size);
+ obj = &blob->object;
+ } else if (!strcmp(type, tree_type)) {
+ struct tree *tree = lookup_tree(sha1);
+ obj = &tree->object;
+ if (!tree->object.parsed) {
+ parse_tree_buffer(tree, buffer, size);
+ eaten = 1;
+ }
+ } else if (!strcmp(type, commit_type)) {
+ struct commit *commit = lookup_commit(sha1);
+ parse_commit_buffer(commit, buffer, size);
+ if (!commit->buffer) {
+ commit->buffer = buffer;
+ eaten = 1;
+ }
+ obj = &commit->object;
+ } else if (!strcmp(type, tag_type)) {
+ struct tag *tag = lookup_tag(sha1);
+ parse_tag_buffer(tag, buffer, size);
+ obj = &tag->object;
+ } else {
+ obj = NULL;
+ }
+ *eaten_p = eaten;
+ return obj;
+}
+
struct object *parse_object(const unsigned char *sha1)
{
unsigned long size;
char type[20];
+ int eaten;
void *buffer = read_sha1_file(sha1, type, &size);
+
if (buffer) {
struct object *obj;
if (check_sha1_signature(sha1, buffer, size, type) < 0)
printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
- if (!strcmp(type, blob_type)) {
- struct blob *blob = lookup_blob(sha1);
- parse_blob_buffer(blob, buffer, size);
- obj = &blob->object;
- } else if (!strcmp(type, tree_type)) {
- struct tree *tree = lookup_tree(sha1);
- obj = &tree->object;
- if (!tree->object.parsed) {
- parse_tree_buffer(tree, buffer, size);
- buffer = NULL;
- }
- } else if (!strcmp(type, commit_type)) {
- struct commit *commit = lookup_commit(sha1);
- parse_commit_buffer(commit, buffer, size);
- if (!commit->buffer) {
- commit->buffer = buffer;
- buffer = NULL;
- }
- obj = &commit->object;
- } else if (!strcmp(type, tag_type)) {
- struct tag *tag = lookup_tag(sha1);
- parse_tag_buffer(tag, buffer, size);
- obj = &tag->object;
- } else {
- obj = NULL;
- }
- free(buffer);
+
+ obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+ if (!eaten)
+ free(buffer);
return obj;
}
return NULL;
diff --git a/object.h b/object.h
index 3d4ff46..caee733 100644
--- a/object.h
+++ b/object.h
@@ -59,6 +59,12 @@ void created_object(const unsigned char *sha1, struct object *obj);
/** Returns the object, having parsed it to find out what it is. **/
struct object *parse_object(const unsigned char *sha1);
+/* Given the result of read_sha1_file(), returns the object after
+ * parsing it. eaten_p indicates if the object has a borrowed copy
+ * of buffer and the caller should not free() it.
+ */
+struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p);
+
/** Returns the object, with potentially excess memory allocated. **/
struct object *lookup_unknown_object(const unsigned char *sha1);
diff --git a/quote.c b/quote.c
index e3a4d4a..ee7d62c 100644
--- a/quote.c
+++ b/quote.c
@@ -349,3 +349,41 @@ void write_name_quoted(const char *prefix, int prefix_len,
else
goto no_quote;
}
+
+/* quoting as a string literal for other languages */
+
+void perl_quote_print(FILE *stream, const char *src)
+{
+ const char sq = '\'';
+ const char bq = '\\';
+ char c;
+
+ fputc(sq, stream);
+ while ((c = *src++)) {
+ if (c == sq || c == bq)
+ fputc(bq, stream);
+ fputc(c, stream);
+ }
+ fputc(sq, stream);
+}
+
+void python_quote_print(FILE *stream, const char *src)
+{
+ const char sq = '\'';
+ const char bq = '\\';
+ const char nl = '\n';
+ char c;
+
+ fputc(sq, stream);
+ while ((c = *src++)) {
+ if (c == nl) {
+ fputc(bq, stream);
+ fputc('n', stream);
+ continue;
+ }
+ if (c == sq || c == bq)
+ fputc(bq, stream);
+ fputc(c, stream);
+ }
+ fputc(sq, stream);
+}
diff --git a/quote.h b/quote.h
index 1a29e79..b55e699 100644
--- a/quote.h
+++ b/quote.h
@@ -52,4 +52,8 @@ extern char *unquote_c_style(const char *quoted, const char **endp);
extern void write_name_quoted(const char *prefix, int prefix_len,
const char *name, int quote, FILE *out);
+/* quoting as a string literal for other languages */
+extern void perl_quote_print(FILE *stream, const char *src);
+extern void python_quote_print(FILE *stream, const char *src);
+
#endif
diff --git a/receive-pack.c b/receive-pack.c
index ea2dbd4..1fcf3a9 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -14,7 +14,7 @@ static int report_status;
static char capabilities[] = "report-status";
static int capabilities_sent;
-static int show_ref(const char *path, const unsigned char *sha1)
+static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
if (capabilities_sent)
packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
@@ -27,9 +27,9 @@ static int show_ref(const char *path, const unsigned char *sha1)
static void write_head_info(void)
{
- for_each_ref(show_ref);
+ for_each_ref(show_ref, NULL);
if (!capabilities_sent)
- show_ref("capabilities^{}", null_sha1);
+ show_ref("capabilities^{}", null_sha1, 0, NULL);
}
@@ -43,34 +43,6 @@ struct command {
static struct command *commands;
-static int is_all_zeroes(const char *hex)
-{
- int i;
- for (i = 0; i < 40; i++)
- if (*hex++ != '0')
- return 0;
- return 1;
-}
-
-static int verify_old_ref(const char *name, char *hex_contents)
-{
- int fd, ret;
- char buffer[60];
-
- if (is_all_zeroes(hex_contents))
- return 0;
- fd = open(name, O_RDONLY);
- if (fd < 0)
- return -1;
- ret = read(fd, buffer, 40);
- close(fd);
- if (ret != 40)
- return -1;
- if (memcmp(buffer, hex_contents, 40))
- return -1;
- return 0;
-}
-
static char update_hook[] = "hooks/update";
static int run_update_hook(const char *refname,
@@ -107,8 +79,8 @@ static int update(struct command *cmd)
const char *name = cmd->ref_name;
unsigned char *old_sha1 = cmd->old_sha1;
unsigned char *new_sha1 = cmd->new_sha1;
- char new_hex[60], *old_hex, *lock_name;
- int newfd, namelen, written;
+ char new_hex[41], old_hex[41];
+ struct ref_lock *lock;
cmd->error_string = NULL;
if (!strncmp(name, "refs/", 5) && check_ref_format(name + 5)) {
@@ -117,13 +89,8 @@ static int update(struct command *cmd)
name);
}
- namelen = strlen(name);
- lock_name = xmalloc(namelen + 10);
- memcpy(lock_name, name, namelen);
- memcpy(lock_name + namelen, ".lock", 6);
-
strcpy(new_hex, sha1_to_hex(new_sha1));
- old_hex = sha1_to_hex(old_sha1);
+ strcpy(old_hex, sha1_to_hex(old_sha1));
if (!has_sha1_file(new_sha1)) {
cmd->error_string = "bad pack";
return error("unpack should have generated %s, "
@@ -144,47 +111,20 @@ static int update(struct command *cmd)
return error("denying non-fast forward;"
" you should pull first");
}
- safe_create_leading_directories(lock_name);
-
- newfd = open(lock_name, O_CREAT | O_EXCL | O_WRONLY, 0666);
- if (newfd < 0) {
- cmd->error_string = "can't lock";
- return error("unable to create %s (%s)",
- lock_name, strerror(errno));
- }
-
- /* Write the ref with an ending '\n' */
- new_hex[40] = '\n';
- new_hex[41] = 0;
- written = write(newfd, new_hex, 41);
- /* Remove the '\n' again */
- new_hex[40] = 0;
-
- close(newfd);
- if (written != 41) {
- unlink(lock_name);
- cmd->error_string = "can't write";
- return error("unable to write %s", lock_name);
- }
- if (verify_old_ref(name, old_hex) < 0) {
- unlink(lock_name);
- cmd->error_string = "raced";
- return error("%s changed during push", name);
- }
if (run_update_hook(name, old_hex, new_hex)) {
- unlink(lock_name);
cmd->error_string = "hook declined";
return error("hook declined to update %s", name);
}
- else if (rename(lock_name, name) < 0) {
- unlink(lock_name);
- cmd->error_string = "can't rename";
- return error("unable to replace %s", name);
- }
- else {
- fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
- return 0;
+
+ lock = lock_any_ref_for_update(name, old_sha1);
+ if (!lock) {
+ cmd->error_string = "failed to lock";
+ return error("failed to lock %s", name);
}
+ write_ref_sha1(lock, new_sha1, "push");
+
+ fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
+ return 0;
}
static char update_post_hook[] = "hooks/post-update";
@@ -335,9 +275,12 @@ int main(int argc, char **argv)
if (!dir)
usage(receive_pack_usage);
- if(!enter_repo(dir, 0))
+ if (!enter_repo(dir, 0))
die("'%s': unable to chdir or not a git archive", dir);
+ setup_ident();
+ git_config(git_default_config);
+
write_head_info();
/* EOF */
diff --git a/refs.c b/refs.c
index 5e65314..3d4cdd1 100644
--- a/refs.c
+++ b/refs.c
@@ -3,15 +3,165 @@
#include <errno.h>
+struct ref_list {
+ struct ref_list *next;
+ unsigned char flag; /* ISSYMREF? ISPACKED? */
+ unsigned char sha1[20];
+ char name[FLEX_ARRAY];
+};
+
+static const char *parse_ref_line(char *line, unsigned char *sha1)
+{
+ /*
+ * 42: the answer to everything.
+ *
+ * In this case, it happens to be the answer to
+ * 40 (length of sha1 hex representation)
+ * +1 (space in between hex and name)
+ * +1 (newline at the end of the line)
+ */
+ int len = strlen(line) - 42;
+
+ if (len <= 0)
+ return NULL;
+ if (get_sha1_hex(line, sha1) < 0)
+ return NULL;
+ if (!isspace(line[40]))
+ return NULL;
+ line += 41;
+ if (isspace(*line))
+ return NULL;
+ if (line[len] != '\n')
+ return NULL;
+ line[len] = 0;
+ return line;
+}
+
+static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
+ int flag, struct ref_list *list)
+{
+ int len;
+ struct ref_list **p = &list, *entry;
+
+ /* Find the place to insert the ref into.. */
+ while ((entry = *p) != NULL) {
+ int cmp = strcmp(entry->name, name);
+ if (cmp > 0)
+ break;
+
+ /* Same as existing entry? */
+ if (!cmp)
+ return list;
+ p = &entry->next;
+ }
+
+ /* Allocate it and add it in.. */
+ len = strlen(name) + 1;
+ entry = xmalloc(sizeof(struct ref_list) + len);
+ hashcpy(entry->sha1, sha1);
+ memcpy(entry->name, name, len);
+ entry->flag = flag;
+ entry->next = *p;
+ *p = entry;
+ return list;
+}
+
+static struct ref_list *get_packed_refs(void)
+{
+ static int did_refs = 0;
+ static struct ref_list *refs = NULL;
+
+ if (!did_refs) {
+ FILE *f = fopen(git_path("packed-refs"), "r");
+ if (f) {
+ struct ref_list *list = NULL;
+ char refline[PATH_MAX];
+ while (fgets(refline, sizeof(refline), f)) {
+ unsigned char sha1[20];
+ const char *name = parse_ref_line(refline, sha1);
+ if (!name)
+ continue;
+ list = add_ref(name, sha1, REF_ISPACKED, list);
+ }
+ fclose(f);
+ refs = list;
+ }
+ did_refs = 1;
+ }
+ return refs;
+}
+
+static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+{
+ DIR *dir = opendir(git_path("%s", base));
+
+ if (dir) {
+ struct dirent *de;
+ int baselen = strlen(base);
+ char *ref = xmalloc(baselen + 257);
+
+ memcpy(ref, base, baselen);
+ if (baselen && base[baselen-1] != '/')
+ ref[baselen++] = '/';
+
+ while ((de = readdir(dir)) != NULL) {
+ unsigned char sha1[20];
+ struct stat st;
+ int flag;
+ int namelen;
+
+ if (de->d_name[0] == '.')
+ continue;
+ namelen = strlen(de->d_name);
+ if (namelen > 255)
+ continue;
+ if (has_extension(de->d_name, ".lock"))
+ continue;
+ memcpy(ref + baselen, de->d_name, namelen+1);
+ if (stat(git_path("%s", ref), &st) < 0)
+ continue;
+ if (S_ISDIR(st.st_mode)) {
+ list = get_ref_dir(ref, list);
+ continue;
+ }
+ if (!resolve_ref(ref, sha1, 1, &flag)) {
+ error("%s points nowhere!", ref);
+ continue;
+ }
+ list = add_ref(ref, sha1, flag, list);
+ }
+ free(ref);
+ closedir(dir);
+ }
+ return list;
+}
+
+static struct ref_list *get_loose_refs(void)
+{
+ static int did_refs = 0;
+ static struct ref_list *refs = NULL;
+
+ if (!did_refs) {
+ refs = get_ref_dir("refs", NULL);
+ did_refs = 1;
+ }
+ return refs;
+}
+
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
-const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
+const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH, len;
char buffer[256];
+ static char ref_buffer[256];
+
+ if (flag)
+ *flag = 0;
for (;;) {
+ const char *path = git_path("%s", ref);
struct stat st;
char *buf;
int fd;
@@ -27,17 +177,31 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
* reading.
*/
if (lstat(path, &st) < 0) {
+ struct ref_list *list = get_packed_refs();
+ while (list) {
+ if (!strcmp(ref, list->name)) {
+ hashcpy(sha1, list->sha1);
+ if (flag)
+ *flag |= REF_ISPACKED;
+ return ref;
+ }
+ list = list->next;
+ }
if (reading || errno != ENOENT)
return NULL;
hashclr(sha1);
- return path;
+ return ref;
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1);
if (len >= 5 && !memcmp("refs/", buffer, 5)) {
- path = git_path("%.*s", len, buffer);
+ buffer[len] = 0;
+ strcpy(ref_buffer, buffer);
+ ref = ref_buffer;
+ if (flag)
+ *flag |= REF_ISSYMREF;
continue;
}
}
@@ -62,19 +226,24 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
while (len && isspace(*buf))
buf++, len--;
while (len && isspace(buf[len-1]))
- buf[--len] = 0;
- path = git_path("%.*s", len, buf);
+ len--;
+ buf[len] = 0;
+ memcpy(ref_buffer, buf, len + 1);
+ ref = ref_buffer;
+ if (flag)
+ *flag |= REF_ISSYMREF;
}
if (len < 40 || get_sha1_hex(buffer, sha1))
return NULL;
- return path;
+ return ref;
}
-int create_symref(const char *git_HEAD, const char *refs_heads_master)
+int create_symref(const char *ref_target, const char *refs_heads_master)
{
const char *lockpath;
char ref[1000];
int fd, len, written;
+ const char *git_HEAD = git_path("%s", ref_target);
#ifndef NO_SYMLINK_HEAD
if (prefer_symlink_refs) {
@@ -112,104 +281,127 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
return 0;
}
-int read_ref(const char *filename, unsigned char *sha1)
+int read_ref(const char *ref, unsigned char *sha1)
{
- if (resolve_ref(filename, sha1, 1))
+ if (resolve_ref(ref, sha1, 1, NULL))
return 0;
return -1;
}
-static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)
+static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
+ void *cb_data)
{
- int retval = 0;
- DIR *dir = opendir(git_path("%s", base));
-
- if (dir) {
- struct dirent *de;
- int baselen = strlen(base);
- char *path = xmalloc(baselen + 257);
-
- if (!strncmp(base, "./", 2)) {
- base += 2;
- baselen -= 2;
+ int retval;
+ struct ref_list *packed = get_packed_refs();
+ struct ref_list *loose = get_loose_refs();
+
+ while (packed && loose) {
+ struct ref_list *entry;
+ int cmp = strcmp(packed->name, loose->name);
+ if (!cmp) {
+ packed = packed->next;
+ continue;
}
- memcpy(path, base, baselen);
- if (baselen && base[baselen-1] != '/')
- path[baselen++] = '/';
-
- while ((de = readdir(dir)) != NULL) {
- unsigned char sha1[20];
- struct stat st;
- int namelen;
+ if (cmp > 0) {
+ entry = loose;
+ loose = loose->next;
+ } else {
+ entry = packed;
+ packed = packed->next;
+ }
+ if (strncmp(base, entry->name, trim))
+ continue;
+ if (is_null_sha1(entry->sha1))
+ continue;
+ if (!has_sha1_file(entry->sha1)) {
+ error("%s does not point to a valid object!", entry->name);
+ continue;
+ }
+ retval = fn(entry->name + trim, entry->sha1,
+ entry->flag, cb_data);
+ if (retval)
+ return retval;
+ }
- if (de->d_name[0] == '.')
- continue;
- namelen = strlen(de->d_name);
- if (namelen > 255)
- continue;
- if (has_extension(de->d_name, ".lock"))
- continue;
- memcpy(path + baselen, de->d_name, namelen+1);
- if (stat(git_path("%s", path), &st) < 0)
- continue;
- if (S_ISDIR(st.st_mode)) {
- retval = do_for_each_ref(path, fn, trim);
- if (retval)
- break;
- continue;
- }
- if (read_ref(git_path("%s", path), sha1) < 0) {
- error("%s points nowhere!", path);
- continue;
- }
- if (!has_sha1_file(sha1)) {
- error("%s does not point to a valid "
- "commit object!", path);
- continue;
- }
- retval = fn(path + trim, sha1);
+ packed = packed ? packed : loose;
+ while (packed) {
+ if (!strncmp(base, packed->name, trim)) {
+ retval = fn(packed->name + trim, packed->sha1,
+ packed->flag, cb_data);
if (retval)
- break;
+ return retval;
}
- free(path);
- closedir(dir);
+ packed = packed->next;
}
- return retval;
+ return 0;
}
-int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int head_ref(each_ref_fn fn, void *cb_data)
{
unsigned char sha1[20];
- if (!read_ref(git_path("HEAD"), sha1))
- return fn("HEAD", sha1);
+ int flag;
+
+ if (resolve_ref("HEAD", sha1, 1, &flag))
+ return fn("HEAD", sha1, flag, cb_data);
return 0;
}
-int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs", fn, 0);
+ return do_for_each_ref("refs/", fn, 0, cb_data);
}
-int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_tag_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs/tags", fn, 10);
+ return do_for_each_ref("refs/tags/", fn, 10, cb_data);
}
-int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_branch_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs/heads", fn, 11);
+ return do_for_each_ref("refs/heads/", fn, 11, cb_data);
}
-int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))
+int for_each_remote_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref("refs/remotes", fn, 13);
+ return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
}
+/* NEEDSWORK: This is only used by ssh-upload and it should go; the
+ * caller should do resolve_ref or read_ref like everybody else. Or
+ * maybe everybody else should use get_ref_sha1() instead of doing
+ * read_ref().
+ */
int get_ref_sha1(const char *ref, unsigned char *sha1)
{
if (check_ref_format(ref))
return -1;
- return read_ref(git_path("refs/%s", ref), sha1);
+ return read_ref(mkpath("refs/%s", ref), sha1);
+}
+
+int delete_ref(const char *refname, unsigned char *sha1)
+{
+ struct ref_lock *lock;
+ int err, i, ret = 0;
+
+ lock = lock_any_ref_for_update(refname, sha1);
+ if (!lock)
+ return 1;
+ i = strlen(lock->lk->filename) - 5; /* .lock */
+ lock->lk->filename[i] = 0;
+ err = unlink(lock->lk->filename);
+ if (err) {
+ ret = 1;
+ error("unlink(%s) failed: %s",
+ lock->lk->filename, strerror(errno));
+ }
+ lock->lk->filename[i] = '.';
+
+ err = unlink(lock->log_file);
+ if (err && errno != ENOENT)
+ fprintf(stderr, "warning: unlink(%s) failed: %s",
+ lock->log_file, strerror(errno));
+
+ return ret;
}
/*
@@ -267,22 +459,13 @@ int check_ref_format(const char *ref)
static struct ref_lock *verify_lock(struct ref_lock *lock,
const unsigned char *old_sha1, int mustexist)
{
- char buf[40];
- int nr, fd = open(lock->ref_file, O_RDONLY);
- if (fd < 0 && (mustexist || errno != ENOENT)) {
- error("Can't verify ref %s", lock->ref_file);
- unlock_ref(lock);
- return NULL;
- }
- nr = read(fd, buf, 40);
- close(fd);
- if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) {
- error("Can't verify ref %s", lock->ref_file);
+ if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+ error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
return NULL;
}
if (hashcmp(lock->old_sha1, old_sha1)) {
- error("Ref %s is at %s but expected %s", lock->ref_file,
+ error("Ref %s is at %s but expected %s", lock->ref_name,
sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
unlock_ref(lock);
return NULL;
@@ -290,54 +473,52 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
return lock;
}
-static struct ref_lock *lock_ref_sha1_basic(const char *path,
- int plen,
- const unsigned char *old_sha1, int mustexist)
+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
{
- const char *orig_path = path;
+ char *ref_file;
+ const char *orig_ref = ref;
struct ref_lock *lock;
struct stat st;
+ int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
- plen = strlen(path) - plen;
- path = resolve_ref(path, lock->old_sha1, mustexist);
- if (!path) {
+ ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
+ if (!ref) {
int last_errno = errno;
error("unable to resolve reference %s: %s",
- orig_path, strerror(errno));
+ orig_ref, strerror(errno));
unlock_ref(lock);
errno = last_errno;
return NULL;
}
lock->lk = xcalloc(1, sizeof(struct lock_file));
- lock->ref_file = xstrdup(path);
- lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen));
- lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;
+ lock->ref_name = xstrdup(ref);
+ lock->log_file = xstrdup(git_path("logs/%s", ref));
+ ref_file = git_path("%s", ref);
+ lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
- if (safe_create_leading_directories(lock->ref_file))
- die("unable to create directory for %s", lock->ref_file);
- lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1);
+ if (safe_create_leading_directories(ref_file))
+ die("unable to create directory for %s", ref_file);
+ lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
}
-struct ref_lock *lock_ref_sha1(const char *ref,
- const unsigned char *old_sha1, int mustexist)
+struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
{
+ char refpath[PATH_MAX];
if (check_ref_format(ref))
return NULL;
- return lock_ref_sha1_basic(git_path("refs/%s", ref),
- 5 + strlen(ref), old_sha1, mustexist);
+ strcpy(refpath, mkpath("refs/%s", ref));
+ return lock_ref_sha1_basic(refpath, old_sha1);
}
-struct ref_lock *lock_any_ref_for_update(const char *ref,
- const unsigned char *old_sha1, int mustexist)
+struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
{
- return lock_ref_sha1_basic(git_path("%s", ref),
- strlen(ref), old_sha1, mustexist);
+ return lock_ref_sha1_basic(ref, old_sha1);
}
void unlock_ref(struct ref_lock *lock)
@@ -348,7 +529,7 @@ void unlock_ref(struct ref_lock *lock)
if (lock->lk)
rollback_lock_file(lock->lk);
}
- free(lock->ref_file);
+ free(lock->ref_name);
free(lock->log_file);
free(lock);
}
@@ -425,7 +606,7 @@ int write_ref_sha1(struct ref_lock *lock,
return -1;
}
if (commit_lock_file(lock->lk)) {
- error("Couldn't set %s", lock->ref_file);
+ error("Couldn't set %s", lock->ref_name);
unlock_ref(lock);
return -1;
}
diff --git a/refs.h b/refs.h
index 553155c..0d4d79e 100644
--- a/refs.h
+++ b/refs.h
@@ -2,7 +2,7 @@
#define REFS_H
struct ref_lock {
- char *ref_file;
+ char *ref_name;
char *log_file;
struct lock_file *lk;
unsigned char old_sha1[20];
@@ -14,20 +14,23 @@ struct ref_lock {
* Calls the specified function for each ref file until it returns nonzero,
* and returns the value
*/
-extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1));
-extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1));
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
+extern int head_ref(each_ref_fn, void *);
+extern int for_each_ref(each_ref_fn, void *);
+extern int for_each_tag_ref(each_ref_fn, void *);
+extern int for_each_branch_ref(each_ref_fn, void *);
+extern int for_each_remote_ref(each_ref_fn, void *);
/** Reads the refs file specified into sha1 **/
extern int get_ref_sha1(const char *ref, unsigned char *sha1);
/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
-extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist);
+extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */
-extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist);
+extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1);
/** Release any lock taken but not written. **/
extern void unlock_ref(struct ref_lock *lock);
diff --git a/revision.c b/revision.c
index 93f2513..d87cb6c 100644
--- a/revision.c
+++ b/revision.c
@@ -468,7 +468,7 @@ static void limit_list(struct rev_info *revs)
static int all_flags;
static struct rev_info *all_revs;
-static int handle_one_ref(const char *path, const unsigned char *sha1)
+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = get_reference(all_revs, path, sha1, all_flags);
add_pending_object(all_revs, object, "");
@@ -479,7 +479,7 @@ static void handle_all(struct rev_info *revs, unsigned flags)
{
all_revs = revs;
all_flags = flags;
- for_each_ref(handle_one_ref);
+ for_each_ref(handle_one_ref, NULL);
}
static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
diff --git a/send-pack.c b/send-pack.c
index 5bb123a..fbd792c 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1,
static struct ref *local_refs, **local_tail;
static struct ref *remote_refs, **remote_tail;
-static int one_local_ref(const char *refname, const unsigned char *sha1)
+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
struct ref *ref;
int len = strlen(refname) + 1;
@@ -230,7 +230,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1)
static void get_local_heads(void)
{
local_tail = &local_refs;
- for_each_ref(one_local_ref);
+ for_each_ref(one_local_ref, NULL);
}
static int receive_status(int in)
diff --git a/server-info.c b/server-info.c
index 2fb8f57..6cd38be 100644
--- a/server-info.c
+++ b/server-info.c
@@ -7,7 +7,7 @@
/* refs */
static FILE *info_ref_fp;
-static int add_info_ref(const char *path, const unsigned char *sha1)
+static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *o = parse_object(sha1);
@@ -34,7 +34,7 @@ static int update_info_refs(int force)
info_ref_fp = fopen(path1, "w");
if (!info_ref_fp)
return error("unable to update %s", path0);
- for_each_ref(add_info_ref);
+ for_each_ref(add_info_ref, NULL);
fclose(info_ref_fp);
rename(path1, path0);
free(path0);
diff --git a/sha1_name.c b/sha1_name.c
index 9b226e3..ed711f2 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -247,8 +247,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
NULL
};
static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
- const char **p, *pathname;
- char *real_path = NULL;
+ const char **p, *ref;
+ char *real_ref = NULL;
int refs_found = 0, am;
unsigned long at_time = (unsigned long)-1;
unsigned char *this_result;
@@ -276,10 +276,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
for (p = fmt; *p; p++) {
this_result = refs_found ? sha1_from_ref : sha1;
- pathname = resolve_ref(git_path(*p, len, str), this_result, 1);
- if (pathname) {
+ ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+ if (ref) {
if (!refs_found++)
- real_path = xstrdup(pathname);
+ real_ref = xstrdup(ref);
if (!warn_ambiguous_refs)
break;
}
@@ -293,12 +293,12 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
if (at_time != (unsigned long)-1) {
read_ref_at(
- real_path + strlen(git_path(".")) - 1,
+ real_ref,
at_time,
sha1);
}
- free(real_path);
+ free(real_ref);
return 0;
}
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index b3b920e..6a917f2 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -30,11 +30,8 @@ rm -f .git/$m
test_expect_success \
"fail to create $n" \
"touch .git/$n_dir
- git-update-ref $n $A >out 2>err
- test "'$? = 1 &&
- test "" = "$(cat out)" &&
- grep "error: unable to resolve reference" err &&
- grep '"$n err"
+ git-update-ref $n $A >out 2>err"'
+ test $? != 0'
rm -f .git/$n_dir out err
test_expect_success \
diff --git a/upload-pack.c b/upload-pack.c
index 189b239..9412a9b 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -416,7 +416,7 @@ static void receive_needs(void)
}
}
-static int send_ref(const char *refname, const unsigned char *sha1)
+static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
struct object *o = parse_object(sha1);
@@ -444,8 +444,8 @@ static int send_ref(const char *refname, const unsigned char *sha1)
static void upload_pack(void)
{
reset_timeout();
- head_ref(send_ref);
- for_each_ref(send_ref);
+ head_ref(send_ref, NULL);
+ for_each_ref(send_ref, NULL);
packet_flush(1);
receive_needs();
if (want_obj.nr) {
diff --git a/wt-status.c b/wt-status.c
index 4b74e68..d8e284c 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -41,10 +41,8 @@ void wt_status_prepare(struct wt_status *s)
s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0;
- head = resolve_ref(git_path("HEAD"), sha1, 0);
- s->branch = head ?
- strdup(head + strlen(get_git_dir()) + 1) :
- NULL;
+ head = resolve_ref("HEAD", sha1, 0, NULL);
+ s->branch = head ? xstrdup(head) : NULL;
s->reference = "HEAD";
s->amend = 0;