From 358ddb62cfd03bba1ca2f1ae8e81b9510f42ea9a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 15 Sep 2006 11:19:32 -0700 Subject: Add "git show-ref" builtin command It's kind of like "git peek-remote", but works only locally (and thus avoids the whole overhead of git_connect()) and has some extra verification features. For example, it allows you to filter the results, and to choose whether you want the tag dereferencing or not. You can also use it to just test whether a particular ref exists. For example: git show-ref master will show all references called "master", whether tags or heads or anything else, and regardless of how deep in the reference naming hierarchy they are (so it would show "refs/heads/master" but also "refs/remote/other-repo/master"). When using the "--verify" flag, the command requires an exact ref path: git show-ref --verify refs/heads/master will only match the exact branch called "master". If nothing matches, show-ref 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. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 7b3114f..c365138 100644 --- a/Makefile +++ b/Makefile @@ -295,7 +295,8 @@ BUILTIN_OBJS = \ builtin-upload-tar.o \ builtin-verify-pack.o \ builtin-write-tree.o \ - builtin-zip-tree.o + builtin-zip-tree.o \ + builtin-show-ref.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-show-ref.c b/builtin-show-ref.c new file mode 100644 index 0000000..161b236 --- /dev/null +++ b/builtin-show-ref.c @@ -0,0 +1,112 @@ +#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|--deref] [--tags] [--heads] [--] [pattern*]"; + +static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0, found_match = 0, verify = 0, quiet = 0; +static const char **pattern; + +static int show_ref(const char *refname, const unsigned char *sha1) +{ + 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; + 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, "--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); + for_each_ref(show_ref); + if (!found_match) { + if (verify && !quiet) + die("No match"); + return 1; + } + return 0; +} diff --git a/builtin.h b/builtin.h index 398eafb..a7242be 100644 --- a/builtin.h +++ b/builtin.h @@ -60,5 +60,6 @@ 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); #endif diff --git a/git.c b/git.c index 8c182a5..fedd536 100644 --- a/git.c +++ b/git.c @@ -266,6 +266,7 @@ 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 }, }; int i; -- cgit v0.10.2-6-g49f6 From 305e22c36e674924de6decb29e8a4c22292b5054 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 15 Sep 2006 14:56:55 -0700 Subject: Teach "git checkout" to use git-show-ref That way, it doesn't care how the refs are stored any more Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/git-checkout.sh b/git-checkout.sh index 580a9e8..f03620b 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) -- cgit v0.10.2-6-g49f6 From c40abef89f746ef7b9b7815b12b740e2f22905c8 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 17 Sep 2006 06:20:24 +0200 Subject: Add [-s|--hash] option to Linus' show-ref. With this option only the sha1 hash of the ref should be printed. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 161b236..577d934 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -3,9 +3,10 @@ #include "object.h" #include "tag.h" -static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--deref] [--tags] [--heads] [--] [pattern*]"; +static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--deref] [-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; +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) @@ -50,7 +51,10 @@ match: } if (quiet) return 0; - printf("%s %s\n", sha1_to_hex(sha1), refname); + 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); @@ -86,6 +90,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) deref_tags = 1; continue; } + if (!strcmp(arg, "-s") || !strcmp(arg, "--hash")) { + hash_only = 1; + continue; + } if (!strcmp(arg, "--verify")) { verify = 1; continue; -- cgit v0.10.2-6-g49f6 From ee1a9b2f1824100bbac8d23e7dea1a6e9e85fb21 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 17 Sep 2006 06:32:24 +0200 Subject: Use Linus' show ref in "git-branch.sh". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-branch.sh b/git-branch.sh index e0501ec..2600e9c 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 @@ -112,7 +112,7 @@ rev=$(git-rev-parse --verify "$head") || exit git-check-ref-format "heads/$branchname" || die "we do not like '$branchname' as a branch name." -if [ -e "$GIT_DIR/refs/heads/$branchname" ] +if git-show-ref --verify --quiet -- "refs/heads/$branchname" then if test '' = "$force" then -- cgit v0.10.2-6-g49f6 From e1e22e37f47e3f4d741d28920e1d27e3775c31ad Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 16:37:32 -0700 Subject: Start handling references internally as a sorted in-memory list This also adds some very rudimentary support for the notion of packed refs. HOWEVER! At this point it isn't used to actually look up a ref yet, only for listing them (ie "for_each_ref()" and friends see the packed refs, but none of the other single-ref lookup routines). Note how we keep two separate lists: one for the loose refs, and one for the packed refs we read. That's so that we can easily keep the two apart, and read only one set or the other (and still always make sure that the loose refs take precedence). [ From this, it's not actually obvious why we'd keep the two separate lists, but it's important to have the packed refs on their own list later on, when I add support for looking up a single loose one. For that case, we will want to read _just_ the packed refs in case the single-ref lookup fails, yet we may end up needing the other list at some point in the future, so keeping them separated is important ] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 8467447..cdbb566 100644 --- a/Makefile +++ b/Makefile @@ -301,7 +301,8 @@ BUILTIN_OBJS = \ builtin-upload-tar.o \ builtin-verify-pack.o \ builtin-write-tree.o \ - builtin-zip-tree.o + builtin-zip-tree.o \ + builtin-pack-refs.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c new file mode 100644 index 0000000..0f5d827 --- /dev/null +++ b/builtin-pack-refs.c @@ -0,0 +1,41 @@ +#include "cache.h" +#include "refs.h" + +static FILE *refs_file; +static const char *result_path, *lock_path; + +static void remove_lock_file(void) +{ + if (lock_path) + unlink(lock_path); +} + +static int handle_one_ref(const char *path, const unsigned char *sha1) +{ + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); + return 0; +} + +int cmd_pack_refs(int argc, const char **argv, const char *prefix) +{ + int fd; + + 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); + + refs_file = fdopen(fd, "w"); + if (!refs_file) + die("unable to create ref-pack file structure (%s)", strerror(errno)); + for_each_ref(handle_one_ref); + fsync(fd); + fclose(refs_file); + if (rename(lock_path, result_path) < 0) + die("unable to overwrite old ref-pack file (%s)", strerror(errno)); + lock_path = NULL; + return 0; +} diff --git a/builtin.h b/builtin.h index ccade94..4b11f52 100644 --- a/builtin.h +++ b/builtin.h @@ -63,5 +63,6 @@ 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_pack_refs(int argc, const char **argv, const char *prefix); #endif diff --git a/git.c b/git.c index 44ab0de..de2a06b 100644 --- a/git.c +++ b/git.c @@ -269,6 +269,7 @@ 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 }, + { "pack-refs", cmd_pack_refs, RUN_SETUP }, }; int i; diff --git a/refs.c b/refs.c index 5e65314..5f80a68 100644 --- a/refs.c +++ b/refs.c @@ -3,6 +3,145 @@ #include +struct ref_list { + struct ref_list *next; + 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 (line[len] != '\n') + return NULL; + line[len] = 0; + return line; +} + +static struct ref_list *add_ref(const char *name, const unsigned char *sha1, 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->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, 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 *path = xmalloc(baselen + 257); + + 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 (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)) { + list = get_ref_dir(path, list); + continue; + } + if (read_ref(git_path("%s", path), sha1) < 0) { + error("%s points nowhere!", path); + continue; + } + list = add_ref(path, sha1, list); + } + free(path); + 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 @@ -121,60 +260,41 @@ int read_ref(const char *filename, unsigned char *sha1) static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) { - 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; + retval = fn(entry->name + trim, entry->sha1); + 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); 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)) @@ -187,22 +307,22 @@ int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs", fn, 0); + return do_for_each_ref("refs/", fn, 0); } int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/tags", fn, 10); + return do_for_each_ref("refs/tags/", fn, 10); } int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/heads", fn, 11); + return do_for_each_ref("refs/heads/", fn, 11); } int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/remotes", fn, 13); + return do_for_each_ref("refs/remotes/", fn, 13); } int get_ref_sha1(const char *ref, unsigned char *sha1) -- cgit v0.10.2-6-g49f6 From b37a562a1097af7403c649a5f903a93acaf279e8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 20:10:15 -0700 Subject: Add support for negative refs You can remove a ref that is packed two different ways: either simply repack all the refs without that one, or create a loose ref that has the magic all-zero SHA1. This also adds back the test that a ref actually has the object it points to. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 5f80a68..72e2283 100644 --- a/refs.c +++ b/refs.c @@ -280,6 +280,12 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u } 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); if (retval) return retval; -- cgit v0.10.2-6-g49f6 From ed378ec7e85fd2c5cfcc7bd64b454236357fdd97 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 20:17:35 -0700 Subject: Make ref resolution saner The old code used to totally mix up the notion of a ref-name and the path that that ref was associated with. That was not only horribly ugly (a number of users got the path, and then wanted to try to turn it back into a ref-name again), but it fundamnetally doesn't work at all once we do any setup where a ref doesn't have a 1:1 relationship with a particular pathname. This fixes things up so that we use the ref-name throughout, and only turn it into a pathname once we actually look it up in the filesystem. That makes a lot of things much clearer and more straightforward. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index c407c03..b93c17c 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); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-init-db.c b/builtin-init-db.c index 5085018..23b7714 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-show-branch.c b/builtin-show-branch.c index 578c9fa..4d8db0c 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -437,21 +437,13 @@ static void snarf_refs(int head, int tag) } } -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; + return !strcmp(head, name); } static int show_merge_base(struct commit_list *seen, int num_rev) @@ -559,9 +551,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 +630,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); + 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 +705,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-symbolic-ref.c b/builtin-symbolic-ref.c index 1d3a5e2..6f18db8 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -7,15 +7,11 @@ 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 + const char *refs_heads_master = resolve_ref("HEAD", sha1, 0); + + if (!refs_heads_master) die("No such ref: %s", HEAD); + puts(refs_heads_master); } int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) @@ -26,7 +22,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/cache.h b/cache.h index 57db7c9..282eed6 100644 --- a/cache.h +++ b/cache.h @@ -287,8 +287,8 @@ 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 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/refs.c b/refs.c index 72e2283..50c25d3 100644 --- a/refs.c +++ b/refs.c @@ -93,11 +93,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) if (dir) { struct dirent *de; int baselen = strlen(base); - char *path = xmalloc(baselen + 257); + char *ref = xmalloc(baselen + 257); - memcpy(path, base, baselen); + memcpy(ref, base, baselen); if (baselen && base[baselen-1] != '/') - path[baselen++] = '/'; + ref[baselen++] = '/'; while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; @@ -111,20 +111,20 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) 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) + 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(path, list); + list = get_ref_dir(ref, list); continue; } - if (read_ref(git_path("%s", path), sha1) < 0) { - error("%s points nowhere!", path); + if (read_ref(ref, sha1) < 0) { + error("%s points nowhere!", ref); continue; } - list = add_ref(path, sha1, list); + list = add_ref(ref, sha1, list); } - free(path); + free(ref); closedir(dir); } return list; @@ -145,12 +145,14 @@ static struct ref_list *get_loose_refs(void) /* 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 depth = MAXDEPTH, len; char buffer[256]; + static char ref_buffer[256]; for (;;) { + const char *path = git_path("%s", ref); struct stat st; char *buf; int fd; @@ -169,14 +171,16 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) 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; continue; } } @@ -201,19 +205,22 @@ 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 (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) { @@ -251,9 +258,9 @@ 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)) return 0; return -1; } @@ -306,7 +313,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) { unsigned char sha1[20]; - if (!read_ref(git_path("HEAD"), sha1)) + if (!read_ref("HEAD", sha1)) return fn("HEAD", sha1); return 0; } @@ -335,7 +342,7 @@ 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); } /* @@ -416,31 +423,30 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *path, +static struct ref_lock *lock_ref_sha1_basic(const char *ref, int plen, const unsigned char *old_sha1, int mustexist) { - const char *orig_path = path; + const char *orig_ref = ref; struct ref_lock *lock; struct stat st; 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); + 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->ref_file = xstrdup(git_path("%s", ref)); + lock->log_file = xstrdup(git_path("logs/%s", ref)); lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(lock->ref_file)) @@ -455,15 +461,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, { if (check_ref_format(ref)) return NULL; - return lock_ref_sha1_basic(git_path("refs/%s", ref), + return lock_ref_sha1_basic(mkpath("refs/%s", ref), 5 + strlen(ref), old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist) { - return lock_ref_sha1_basic(git_path("%s", ref), - strlen(ref), old_sha1, mustexist); + return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist); } void unlock_ref(struct ref_lock *lock) diff --git a/sha1_name.c b/sha1_name.c index 1fbc443..b497528 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); + 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; } -- cgit v0.10.2-6-g49f6 From 434cd0cd30d17bc703397a76480fdd129cecc064 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 Sep 2006 10:14:47 -0700 Subject: Enable the packed refs file format This actually "turns on" the packed ref file format, now that the infrastructure to do so sanely exists (ie notably the change to make the reference reading logic take refnames rather than pathnames to the loose objects that no longer necessarily even exist). In particular, when the ref lookup hits a refname that has no loose file associated with it, it falls back on the packed-ref information. Also, the ref-locking code, while still using a loose file for the locking itself (and _creating_ a loose file for the new ref) no longer requires that the old ref be in such an unpacked state. Finally, this does a minimal hack to git-checkout.sh to rather than check the ref-file directly, do a "git-rev-parse" on the "heads/$refname". That's not really wonderful - we should rather really have a special routine to verify the names as proper branch head names, but it is a workable solution for now. With this, I can literally do something like git pack-refs find .git/refs -type f -print0 | xargs -0 rm -f -- and the end result is a largely working repository (ie I've done two commits - which creates _one_ unpacked ref file - done things like run "gitk" and "git log" etc, and it all looks ok). There are probably things missing, but I'm hoping that the missing things are now of the "small and obvious" kind, and that somebody else might want to start looking at this too. Hint hint ;) Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/git-checkout.sh b/git-checkout.sh index 580a9e8..c60e029 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-rev-parse --symbolic "heads/$newbranch" >&/dev/null && 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,7 @@ while [ "$#" != "0" ]; do fi new="$rev" new_name="$arg^0" - if [ -f "$GIT_DIR/refs/heads/$arg" ]; then + if git-rev-parse "heads/$arg^0" >&/dev/null; then branch="$arg" fi elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null) diff --git a/refs.c b/refs.c index 50c25d3..134c0fc 100644 --- a/refs.c +++ b/refs.c @@ -28,6 +28,8 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) if (!isspace(line[40])) return NULL; line += 41; + if (isspace(*line)) + return NULL; if (line[len] != '\n') return NULL; line[len] = 0; @@ -168,6 +170,14 @@ const char *resolve_ref(const char *ref, 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); + return ref; + } + list = list->next; + } if (reading || errno != ENOENT) return NULL; hashclr(sha1); @@ -400,22 +410,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)) { + 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; @@ -427,6 +428,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, int plen, const unsigned char *old_sha1, int mustexist) { + char *ref_file; const char *orig_ref = ref; struct ref_lock *lock; struct stat st; @@ -445,13 +447,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, } lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_file = xstrdup(git_path("%s", ref)); + lock->ref_name = xstrdup(ref); lock->log_file = xstrdup(git_path("logs/%s", ref)); - lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; + ref_file = git_path(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; } @@ -479,7 +482,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); } @@ -556,7 +559,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..af347e6 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]; -- cgit v0.10.2-6-g49f6 From ef176ad06afde424b2167da290a0fdd7fb8ca3d4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 Sep 2006 13:46:00 -0700 Subject: Fix t1400-update-ref test minimally It depended on specific error messages to detect failure but the implementation changed and broke the test. This fixes the breakage minimally. Signed-off-by: Junio C Hamano 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 \ -- cgit v0.10.2-6-g49f6 From f62363fb02865967e5efd17de1a09de6e02f3e3f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 17 Sep 2006 19:13:56 -0700 Subject: wt-status: use simplified resolve_ref to find current branch Signed-off-by: Junio C Hamano diff --git a/wt-status.c b/wt-status.c index 4b74e68..050922d 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); + s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; s->amend = 0; -- cgit v0.10.2-6-g49f6 From b3dc864c6d5ffb96513328f976c076c3a90331b0 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 18 Sep 2006 00:34:38 -0700 Subject: gitignore: git-pack-refs is a generated file. Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index a3d9c7a..1ff28c7 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ git-name-rev git-mv git-pack-redundant git-pack-objects +git-pack-refs git-parse-remote git-patch-id git-peek-remote -- cgit v0.10.2-6-g49f6 From 582c5b09be6336e74a366ad900ca7ec3a5d1572d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 18 Sep 2006 00:35:07 -0700 Subject: gitignore: git-show-ref is a generated file. Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 0d608fe..f53e0b2 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,7 @@ git-shortlog git-show git-show-branch git-show-index +git-show-ref git-ssh-fetch git-ssh-pull git-ssh-push -- cgit v0.10.2-6-g49f6 From 5b10b091139ea5ef87376998c89f3a20dd7c7793 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 18 Sep 2006 01:08:00 -0700 Subject: fsck-objects: adjust to resolve_ref() clean-up. Signed-off-by: Junio C Hamano diff --git a/fsck-objects.c b/fsck-objects.c index 4d994f3..456c17e 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -458,15 +458,13 @@ 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 */ + const char *head_points_at = resolve_ref("HEAD", sha1, 1); - if (!git_refs_heads_master) + if (!head_points_at) 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; -- cgit v0.10.2-6-g49f6 From c0990ff36f0b9b8e806c8f649a0888d05bb22c37 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Mon, 18 Sep 2006 14:32:41 +0200 Subject: Add man page for git-show-ref Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt new file mode 100644 index 0000000..529ea17 --- /dev/null +++ b/Documentation/git-show-ref.txt @@ -0,0 +1,136 @@ +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] + [--tags] [--heads] [--] ... + +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. + +--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. + +:: + + Show references matching one or more patterns. + +OUTPUT +------ + +The output is in the format: '' '' ''. + +----------------------------------------------------------------------------- +$ 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^{} +... +----------------------------------------------------------------------------- + +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 . +Man page by Jonas Fonseca . + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v0.10.2-6-g49f6 From 53cce84c05d3cba237ab45540b1e882be9c97215 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 19 Sep 2006 22:58:23 +0200 Subject: Fix broken sha1 locking Current git#next is totally broken wrt. cloning over HTTP, generating refs at random directories. Of course it's caused by the static get_pathname() buffer. lock_ref_sha1() stores return value of mkpath()'s get_pathname() call, then calls lock_ref_sha1_basic() which calls git_path(ref) which calls get_pathname() at that point returning pointer to the same buffer. So now you are sprintf()ing a format string into itself, wow! The resulting pathnames are really cute. (If you've been paying attention, yes, the mere fact that a format string _could_ write over itself is very wrong and probably exploitable here. See the other mail I've just sent.) I've never liked how we use return values of those functions so liberally, the "allow some random number of get_pathname() return values to work concurrently" is absolutely horrible pit and we've already fallen in this before IIRC. I consider it an awful coding practice, you add a call somewhere and at some other point some distant caller of that breaks since it reuses the same return values. Not to mention this takes quite some time to debug. My gut feeling tells me that there might be more of this. I don't have time to review the rest of the users of the refs.c functions though. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 134c0fc..7bd36e4 100644 --- a/refs.c +++ b/refs.c @@ -462,10 +462,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist) { + char refpath[PATH_MAX]; if (check_ref_format(ref)) return NULL; - return lock_ref_sha1_basic(mkpath("refs/%s", ref), - 5 + strlen(ref), old_sha1, mustexist); + strcpy(refpath, mkpath("refs/%s", ref)); + return lock_ref_sha1_basic(refpath, strlen(refpath), + old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, -- cgit v0.10.2-6-g49f6 From 9581e0fca225927dbcbdf03fe70a1d2711ddc6b9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 20 Sep 2006 06:14:54 +0200 Subject: Document git-show-ref [-s|--hash] option. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index 529ea17..b724d83 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] - [--tags] [--heads] [--] ... + [-s|--hash] [--tags] [--heads] [--] ... DESCRIPTION ----------- @@ -40,6 +40,12 @@ OPTIONS 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. @@ -72,6 +78,16 @@ $ git show-ref --head --dereference ... ----------------------------------------------------------------------------- +When using --hash (and not --dereference) the output format is: '' + +----------------------------------------------------------------------------- +$ git show-ref --heads --hash +2e3ba0114a1f52b47df29743d6915d056be13278 +185008ae97960c8d551adcd9e23565194651b5d1 +03adf42c988195b50e1a1935ba5fcbc39b2b029b +... +----------------------------------------------------------------------------- + EXAMPLE ------- -- cgit v0.10.2-6-g49f6 From 9c13359aaf3176428603cc9dfbdf30da889ab3d3 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 20 Sep 2006 06:21:25 +0200 Subject: Fix show-ref usage for --dereference. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 577d934..fab359b 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -3,7 +3,7 @@ #include "object.h" #include "tag.h" -static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--deref] [-s|--hash] [--tags] [--heads] [--] [pattern*]"; +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; -- cgit v0.10.2-6-g49f6 From cc4c4f0ce25cc3803197bc19c275fd6f59a2a511 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 21:10:17 -0700 Subject: symbolit-ref: fix resolve_ref conversion. An earlier conversion accidentally hardcoded "HEAD" to be passed to resolve_ref(), thereby causing git-symbolic-ref command to always report where the HEAD points at, ignoring the command line parameter. Signed-off-by: Junio C Hamano diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 6f18db8..13163ba 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -7,7 +7,7 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *refs_heads_master = resolve_ref("HEAD", sha1, 0); + const char *refs_heads_master = resolve_ref(HEAD, sha1, 0); if (!refs_heads_master) die("No such ref: %s", HEAD); -- cgit v0.10.2-6-g49f6 From cb5d709ff8a4bae19d57a470ba2b137c25938a44 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 21:47:42 -0700 Subject: Add callback data to for_each_ref() family. This is a long overdue fix to the API for for_each_ref() family of functions. It allows the callers to specify a callback data pointer, so that the caller does not have to use static variables to communicate with the callback funciton. The updated for_each_ref() family takes a function of type int (*fn)(const char *, const unsigned char *, void *) and a void pointer as parameters, and calls the function with the name of the ref and its SHA-1 with the caller-supplied void pointer as parameters. The commit updates two callers, builtin-name-rev.c and builtin-pack-refs.c as an example. Signed-off-by: Junio C Hamano diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 52886b6..9e3e537 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, 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 index 0f5d827..b3d5470 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -1,7 +1,6 @@ #include "cache.h" #include "refs.h" -static FILE *refs_file; static const char *result_path, *lock_path; static void remove_lock_file(void) @@ -10,8 +9,10 @@ static void remove_lock_file(void) unlink(lock_path); } -static int handle_one_ref(const char *path, const unsigned char *sha1) +static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { + FILE *refs_file = cb_data; + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); return 0; } @@ -19,6 +20,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1) int cmd_pack_refs(int argc, const char **argv, const char *prefix) { int fd; + FILE *refs_file; result_path = xstrdup(git_path("packed-refs")); lock_path = xstrdup(mkpath("%s.lock", result_path)); @@ -31,7 +33,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) refs_file = fdopen(fd, "w"); if (!refs_file) die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref); + for_each_ref(handle_one_ref, refs_file); fsync(fd); fclose(refs_file); if (rename(lock_path, result_path) < 0) diff --git a/builtin-prune.c b/builtin-prune.c index 6228c79..e21c29b 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, 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..88fc8e2 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, 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..c771274 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, 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 4d8db0c..b3548ae 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, 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, 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, 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, void *cb_data) { if (strncmp(refname, "refs/tags/", 10)) return 0; - return append_ref(refname + 5, sha1); + return append_ref(refname + 5, sha1, 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, void *cb_data) { /* we want to allow pattern hold/ to show all * branches under refs/heads/hold/, and v0.99.9? to show @@ -417,22 +417,22 @@ 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, 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, cb_data); + return append_ref(refname, sha1, 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); } } @@ -487,7 +487,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, NULL); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -495,7 +495,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); diff --git a/describe.c b/describe.c index ab192f8..ea0f2ce 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, 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..6264ea1 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, 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, 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..36d1e76 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, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { @@ -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 456c17e..bb0c94e 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, 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 diff --git a/http-push.c b/http-push.c index 670ff00..460c9be 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, 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/receive-pack.c b/receive-pack.c index 78f75da..7abc921 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -12,7 +12,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, void *cb_data) { if (capabilities_sent) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); @@ -25,9 +25,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, NULL); } diff --git a/refs.c b/refs.c index 7bd36e4..85564f0 100644 --- a/refs.c +++ b/refs.c @@ -275,7 +275,7 @@ int read_ref(const char *ref, unsigned char *sha1) 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; struct ref_list *packed = get_packed_refs(); @@ -303,7 +303,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u error("%s does not point to a valid object!", entry->name); continue; } - retval = fn(entry->name + trim, entry->sha1); + retval = fn(entry->name + trim, entry->sha1, cb_data); if (retval) return retval; } @@ -311,7 +311,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u packed = packed ? packed : loose; while (packed) { if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1); + retval = fn(packed->name + trim, packed->sha1, cb_data); if (retval) return retval; } @@ -320,34 +320,39 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u 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("HEAD", sha1)) - return fn("HEAD", sha1); + return fn("HEAD", sha1, 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)) diff --git a/refs.h b/refs.h index af347e6..886c857 100644 --- a/refs.h +++ b/refs.h @@ -14,11 +14,12 @@ 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)); +typedef int each_ref_fn(const char *refname, const unsigned char *sha1, 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); diff --git a/revision.c b/revision.c index 6a2539b..0e84b8a 100644 --- a/revision.c +++ b/revision.c @@ -466,7 +466,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, void *cb_data) { struct object *object = get_reference(all_revs, path, sha1, all_flags); add_pending_object(all_revs, object, ""); @@ -477,7 +477,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..ee13093 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, 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..7667b41 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, 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/upload-pack.c b/upload-pack.c index 189b239..10237eb 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, 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) { -- cgit v0.10.2-6-g49f6 From 8da197755450d4f16018bd4b5486dc8ed88b0f2a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 22:02:01 -0700 Subject: Tell between packed, unpacked and symbolic refs. This adds a "int *flag" parameter to resolve_ref() and makes for_each_ref() family to call callback function with an extra "int flag" parameter. They are used to give two bits of information (REF_ISSYMREF and REF_ISPACKED) about the ref. Signed-off-by: Junio C Hamano diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index b93c17c..3d3097d 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -277,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) usage(fmt_merge_msg_usage); /* get current branch */ - current_branch = resolve_ref("HEAD", head_sha1, 1); + current_branch = resolve_ref("HEAD", head_sha1, 1, NULL); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 9e3e537..618aa31 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -75,7 +75,7 @@ copy_data: } } -static int name_ref(const char *path, const unsigned char *sha1, void *cb_data) +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; diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index b3d5470..9871089 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -9,7 +9,8 @@ static void remove_lock_file(void) unlink(lock_path); } -static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int handle_one_ref(const char *path, const unsigned char *sha1, + int flags, void *cb_data) { FILE *refs_file = cb_data; diff --git a/builtin-prune.c b/builtin-prune.c index e21c29b..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, void *cb_data) +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) diff --git a/builtin-push.c b/builtin-push.c index 88fc8e2..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, void *cb_data) +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; diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index c771274..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, void *cb_data) +static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { show_rev(NORMAL, sha1, refname); return 0; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index b3548ae..5d6ce56 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, void *cb_data) +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, void *cb_d return 0; } -static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data) +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, void */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1, cb_data); + return append_ref(refname + ofs, sha1, flag, cb_data); } -static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data) +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, cb_data); + 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, void *cb_data) +static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { /* we want to allow pattern hold/ to show all * branches under refs/heads/hold/, and v0.99.9? to show @@ -417,10 +417,10 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, v if (fnmatch(match_ref_pattern, tail, 0)) return 0; if (!strncmp("refs/heads/", refname, 11)) - return append_head_ref(refname, sha1, cb_data); + return append_head_ref(refname, sha1, flag, cb_data); if (!strncmp("refs/tags/", refname, 10)) - return append_tag_ref(refname, sha1, cb_data); - return append_ref(refname, sha1, cb_data); + 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) @@ -487,7 +487,7 @@ static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { - append_ref(av, revkey, NULL); + append_ref(av, revkey, 0, NULL); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) ac--; av++; } - head_p = resolve_ref("HEAD", head_sha1, 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); diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 13163ba..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,10 +8,13 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *refs_heads_master = resolve_ref(HEAD, sha1, 0); + 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); } diff --git a/cache.h b/cache.h index 282eed6..6def155 100644 --- a/cache.h +++ b/cache.h @@ -286,7 +286,7 @@ 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 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); diff --git a/describe.c b/describe.c index ea0f2ce..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, void *cb_data) +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; diff --git a/fetch-pack.c b/fetch-pack.c index 6264ea1..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, void *cb_data) +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); @@ -253,7 +253,7 @@ done: static struct commit_list *complete; -static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) +static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = parse_object(sha1); diff --git a/fetch.c b/fetch.c index 36d1e76..a2cbdfb 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, void *cb_data) +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) { diff --git a/fsck-objects.c b/fsck-objects.c index bb0c94e..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, void *cb_data) +static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct object *obj; @@ -458,9 +458,10 @@ static void fsck_object_dir(const char *path) static int fsck_head_link(void) { unsigned char sha1[20]; - const char *head_points_at = resolve_ref("HEAD", sha1, 1); + int flag; + const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag); - if (!head_points_at) + if (!head_points_at || !(flag & REF_ISSYMREF)) return error("HEAD is not a symbolic ref"); if (strncmp(head_points_at, "refs/heads/", 11)) return error("HEAD points to something strange (%s)", diff --git a/http-push.c b/http-push.c index 460c9be..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, void *cb_data) +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; diff --git a/receive-pack.c b/receive-pack.c index 7abc921..abbcb6a 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -12,7 +12,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, void *cb_data) +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,7 +27,7 @@ static void write_head_info(void) { for_each_ref(show_ref, NULL); if (!capabilities_sent) - show_ref("capabilities^{}", null_sha1, NULL); + show_ref("capabilities^{}", null_sha1, 0, NULL); } diff --git a/refs.c b/refs.c index 85564f0..40f16af 100644 --- a/refs.c +++ b/refs.c @@ -5,6 +5,7 @@ struct ref_list { struct ref_list *next; + unsigned char flag; /* ISSYMREF? ISPACKED? */ unsigned char sha1[20]; char name[FLEX_ARRAY]; }; @@ -36,7 +37,8 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) return line; } -static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list) +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; @@ -58,6 +60,7 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1, str 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; @@ -78,7 +81,7 @@ static struct ref_list *get_packed_refs(void) const char *name = parse_ref_line(refline, sha1); if (!name) continue; - list = add_ref(name, sha1, list); + list = add_ref(name, sha1, REF_ISPACKED, list); } fclose(f); refs = list; @@ -104,6 +107,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; struct stat st; + int flag; int namelen; if (de->d_name[0] == '.') @@ -120,11 +124,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) list = get_ref_dir(ref, list); continue; } - if (read_ref(ref, sha1) < 0) { + if (!resolve_ref(ref, sha1, 1, &flag)) { error("%s points nowhere!", ref); continue; } - list = add_ref(ref, sha1, list); + list = add_ref(ref, sha1, flag, list); } free(ref); closedir(dir); @@ -147,12 +151,15 @@ static struct ref_list *get_loose_refs(void) /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -const char *resolve_ref(const char *ref, 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; @@ -174,6 +181,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) while (list) { if (!strcmp(ref, list->name)) { hashcpy(sha1, list->sha1); + if (flag) + *flag |= REF_ISPACKED; return ref; } list = list->next; @@ -191,6 +200,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) buffer[len] = 0; strcpy(ref_buffer, buffer); ref = ref_buffer; + if (flag) + *flag |= REF_ISSYMREF; continue; } } @@ -219,6 +230,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) 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; @@ -270,12 +283,13 @@ int create_symref(const char *ref_target, const char *refs_heads_master) int read_ref(const char *ref, unsigned char *sha1) { - if (resolve_ref(ref, sha1, 1)) + if (resolve_ref(ref, sha1, 1, NULL)) return 0; return -1; } -static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data) +static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, + void *cb_data) { int retval; struct ref_list *packed = get_packed_refs(); @@ -303,7 +317,8 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ error("%s does not point to a valid object!", entry->name); continue; } - retval = fn(entry->name + trim, entry->sha1, cb_data); + retval = fn(entry->name + trim, entry->sha1, + entry->flag, cb_data); if (retval) return retval; } @@ -311,7 +326,8 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ packed = packed ? packed : loose; while (packed) { if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1, cb_data); + retval = fn(packed->name + trim, packed->sha1, + packed->flag, cb_data); if (retval) return retval; } @@ -323,8 +339,10 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ int head_ref(each_ref_fn fn, void *cb_data) { unsigned char sha1[20]; - if (!read_ref("HEAD", sha1)) - return fn("HEAD", sha1, cb_data); + int flag; + + if (resolve_ref("HEAD", sha1, 1, &flag)) + return fn("HEAD", sha1, flag, cb_data); return 0; } @@ -415,7 +433,7 @@ int check_ref_format(const char *ref) static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { - if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist)) { + 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; @@ -441,7 +459,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - ref = resolve_ref(ref, lock->old_sha1, mustexist); + ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", diff --git a/refs.h b/refs.h index 886c857..305d408 100644 --- a/refs.h +++ b/refs.h @@ -14,7 +14,9 @@ struct ref_lock { * Calls the specified function for each ref file until it returns nonzero, * and returns the value */ -typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data); +#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 *); diff --git a/revision.c b/revision.c index 0e84b8a..cb13b90 100644 --- a/revision.c +++ b/revision.c @@ -466,7 +466,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, void *cb_data) +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, ""); diff --git a/send-pack.c b/send-pack.c index ee13093..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, void *cb_data) +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; diff --git a/server-info.c b/server-info.c index 7667b41..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, void *cb_data) +static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = parse_object(sha1); diff --git a/sha1_name.c b/sha1_name.c index b497528..84d24c6 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -276,7 +276,7 @@ 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; - ref = resolve_ref(mkpath(*p, len, str), this_result, 1); + ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL); if (ref) { if (!refs_found++) real_ref = xstrdup(ref); diff --git a/upload-pack.c b/upload-pack.c index 10237eb..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, void *cb_data) +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); diff --git a/wt-status.c b/wt-status.c index 050922d..d8e284c 100644 --- a/wt-status.c +++ b/wt-status.c @@ -41,7 +41,7 @@ void wt_status_prepare(struct wt_status *s) s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; - head = resolve_ref("HEAD", sha1, 0); + head = resolve_ref("HEAD", sha1, 0, NULL); s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; -- cgit v0.10.2-6-g49f6 From 13e4aa90acad5738f54385c8a336f89fb6aacdd0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 21 Sep 2006 00:06:05 -0700 Subject: pack-refs: do not pack symbolic refs. Now we can tell which one is symbolic and which one is not, it is easy to do so. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 9871089..0fc8a55 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -14,7 +14,9 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, { FILE *refs_file = cb_data; - fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); + /* Do not pack the symbolic refs */ + if (!(flags & REF_ISSYMREF)) + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); return 0; } -- cgit v0.10.2-6-g49f6 From 968846015229fe0fec28e3c85db722e131c15f93 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 21 Sep 2006 00:06:06 -0700 Subject: git-pack-refs --prune "git pack-refs --prune", after successfully packing the existing refs, removes the loose ref files. It tries to protect against race by doing the usual lock_ref_sha1() which makes sure the contents of the ref has not changed since we last looked at. Also we do not bother trying to prune what was already packed, and we do not try pruning symbolic refs. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 0fc8a55..246dd63 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -2,6 +2,20 @@ #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) { @@ -9,21 +23,70 @@ static void remove_lock_file(void) 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) { - FILE *refs_file = cb_data; + struct pack_refs_cb_data *cb = cb_data; /* Do not pack the symbolic refs */ if (!(flags & REF_ISSYMREF)) - fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); + 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, 1); + + if (lock) { + unlink(git_path(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; - FILE *refs_file; + 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)); @@ -33,14 +96,17 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) die("unable to create new ref-pack file (%s)", strerror(errno)); atexit(remove_lock_file); - refs_file = fdopen(fd, "w"); - if (!refs_file) - die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref, refs_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(refs_file); + 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; } -- cgit v0.10.2-6-g49f6 From 913c983e0481b477ccc11fa72ebeb0d6ec8d06aa Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 21 Sep 2006 23:29:59 +0200 Subject: Fix git-update-index --again It called read_ref(git_path(..)..), where read_ref does the git_path() stuff itself. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-update-index.c b/builtin-update-index.c index 0620e77..09214c8 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -404,9 +404,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); } @@ -443,7 +443,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. */ -- cgit v0.10.2-6-g49f6 From afdcec73660737d0ac28a808829ab76587a9befa Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 22 Sep 2006 00:07:01 +0200 Subject: show-branch: mark active branch with a '*' again This was lost in the packed-ref updates. The original test was a bit dubious, so I cleaned that up, too. It fixes the case when the current HEAD is refs/heads/bla/master: the original test was true for both bla/master _and_ master. However, it shares a hard-to-fix bug with the original test: if the current HEAD is refs/heads/master, and there is a branch refs/heads/heads/master, then both are marked active. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 5d6ce56..fb1a400 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -443,6 +443,12 @@ static int rev_is_head(char *head, int headlen, char *name, if ((!head[0]) || (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) 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); } -- cgit v0.10.2-6-g49f6 From 7c1a278d99a18445e12bd55e308cd69080963198 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Sat, 23 Sep 2006 01:08:45 +0200 Subject: Fix buggy ref recording There is a format string vulnerability introduced with the packed refs file format. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 40f16af..5fdf9c4 100644 --- a/refs.c +++ b/refs.c @@ -472,7 +472,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, lock->ref_name = xstrdup(ref); lock->log_file = xstrdup(git_path("logs/%s", ref)); - ref_file = git_path(ref); + ref_file = git_path("%s", ref); lock->force_write = lstat(ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(ref_file)) -- cgit v0.10.2-6-g49f6 From 6d15987c1e06dab2c0470b86cf9f3df747891f5a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 22 Sep 2006 21:31:40 -0700 Subject: pack-refs: fix git_path() usage. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 246dd63..db57fee 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -56,7 +56,7 @@ static void prune_ref(struct ref_to_prune *r) struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1); if (lock) { - unlink(git_path(r->name)); + unlink(git_path("%s", r->name)); unlock_ref(lock); } } -- cgit v0.10.2-6-g49f6 From 5fd6f5cffc7b8bc679c922b8ff0b1cd35bb1a30c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 22 Sep 2006 21:41:49 -0700 Subject: lock_ref_sha1_basic: remove unused parameter "plen". Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 5fdf9c4..2cef2b4 100644 --- a/refs.c +++ b/refs.c @@ -447,9 +447,7 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, - 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, int mustexist) { char *ref_file; const char *orig_ref = ref; @@ -489,14 +487,13 @@ struct ref_lock *lock_ref_sha1(const char *ref, if (check_ref_format(ref)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, strlen(refpath), - old_sha1, mustexist); + return lock_ref_sha1_basic(refpath, old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist) { - return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist); + return lock_ref_sha1_basic(ref, old_sha1, mustexist); } void unlock_ref(struct ref_lock *lock) -- cgit v0.10.2-6-g49f6 From 4431fcc4b134ae501e3e57dc568ae4f031e57898 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 01:09:18 -0700 Subject: Clean-up lock-ref implementation This drops "mustexist" parameter lock_ref_sha1() and lock_any_ref_forupdate() functions take. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index db57fee..4093973 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -53,7 +53,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, /* 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, 1); + struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); if (lock) { unlink(git_path("%s", r->name)); diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 90a3da5..ab52833 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (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/fetch.c b/fetch.c index a2cbdfb..c426c04 100644 --- a/fetch.c +++ b/fetch.c @@ -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; diff --git a/refs.c b/refs.c index 2cef2b4..9a1bc0d 100644 --- a/refs.c +++ b/refs.c @@ -447,12 +447,13 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int mustexist) +static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1) { 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; @@ -480,20 +481,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char 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; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, old_sha1, mustexist); + 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(ref, old_sha1, mustexist); + return lock_ref_sha1_basic(ref, old_sha1); } void unlock_ref(struct ref_lock *lock) diff --git a/refs.h b/refs.h index 305d408..0d4d79e 100644 --- a/refs.h +++ b/refs.h @@ -27,10 +27,10 @@ extern int for_each_remote_ref(each_ref_fn, void *); 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); -- cgit v0.10.2-6-g49f6 From ac5409e420e5fdd7c4a381f873ffcedfb83d7117 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 01:58:57 -0700 Subject: update-ref: -d flag and ref creation safety. This adds -d flag to update-ref to allow safe deletion of ref. Before deleting it, the command checks if the given still matches the value the caller thought the ref contained. Similarly, it also accepts 0{40} or an empty string as to allow safe creation of a new ref. Signed-off-by: Junio C Hamano 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 ] [] +'git-update-ref' [-m ] (-d | []) DESCRIPTION ----------- @@ -20,7 +20,9 @@ possibly dereferencing the symbolic refs, after verifying that the current value of the matches . E.g. `git-update-ref refs/heads/master ` updates the master branch head to only if its current -value is . +value is . You can specify 40 "0" or an empty string +as 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 after verifying it +still contains . + + Logging Updates --------------- If config parameter "core.logAllRefUpdates" is true or the file diff --git a/builtin-update-ref.c b/builtin-update-ref.c index ab52833..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 [] [-m ]"; +"git-update-ref [-m ] (-d | [])"; 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,8 +49,15 @@ 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); diff --git a/cache.h b/cache.h index 6def155..6e00450 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; diff --git a/refs.c b/refs.c index 9a1bc0d..3d4cdd1 100644 --- a/refs.c +++ b/refs.c @@ -378,6 +378,32 @@ int get_ref_sha1(const char *ref, unsigned char *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; +} + /* * Make sure "ref" is something reasonable to have under ".git/refs/"; * We do not like it if: -- cgit v0.10.2-6-g49f6 From cede7526534c47436de17977eb39e55aa8c1e646 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 02:06:31 -0700 Subject: update a few Porcelain-ish for ref lock safety. This updates the use of git-update-ref in git-branch, git-tag and git-commit to make them safer in a few corner cases as demonstration. - git-tag makes sure that the named tag does not exist, allows you to edit tag message and then creates the tag. If a tag with the same name was created by somebody else in the meantime, it used to happily overwrote it. Now it notices the situation. - git-branch -d and git-commit (for the initial commit) had the same issue but with smaller race window, which is plugged with this. Signed-off-by: Junio C Hamano diff --git a/git-branch.sh b/git-branch.sh index 2600e9c..4379a07 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -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 @@ -112,6 +111,7 @@ rev=$(git-rev-parse --verify "$head") || exit git-check-ref-format "heads/$branchname" || die "we do not like '$branchname' as a branch name." +prev='' if git-show-ref --verify --quiet -- "refs/heads/$branchname" then if test '' = "$force" @@ -121,10 +121,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-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" -- cgit v0.10.2-6-g49f6 From 3159c8dc2da4c88dcecabb95404fe88edc5441e4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 02:40:06 -0700 Subject: Teach receive-pack about ref-log This converts receive-pack to use the standard ref locking code instead of its own. As a side effect, it automatically records the "push" event to ref-log if enabled. Signed-off-by: Junio C Hamano diff --git a/receive-pack.c b/receive-pack.c index abbcb6a..f0b4cb4 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -41,34 +41,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, @@ -105,8 +77,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)) { @@ -115,59 +87,27 @@ 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, " "but I can't find it!", new_hex); } - 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"; @@ -318,9 +258,11 @@ 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); + git_config(git_default_config); + write_head_info(); /* EOF */ -- cgit v0.10.2-6-g49f6 From 94d8213f2c98c4a5fd50484fcb11b4b24b403294 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 13:47:21 -0700 Subject: receive-pack: call setup_ident before git_config Otherwise we would end up getting values from Gecos which is often not what people would want. Signed-off-by: Junio C Hamano diff --git a/receive-pack.c b/receive-pack.c index f0b4cb4..c8aacbb 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -261,6 +261,7 @@ int main(int argc, char **argv) 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(); -- cgit v0.10.2-6-g49f6 From 919a3c981323b6f919747bf6756aa8f5af09361f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 28 Sep 2006 06:58:03 +0200 Subject: Add pack-refs and show-ref test cases. Some of these test cases are from Junio. One test case is commented out because it doesn't work right now. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh new file mode 100755 index 0000000..2cc03e6 --- /dev/null +++ b/t/t3210-pack-refs.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Copyright (c) 2005 Amos Waterland +# Copyright (c) 2006 Christian Couder +# + +test_description='git pack-refs should not change the branch semantic + +This test runs git pack-refs and git show-ref and checks that the branch +semantic is still the same. +' +. ./test-lib.sh + +test_expect_success \ + 'prepare a trivial repository' \ + 'echo Hello > A && + git-update-index --add A && + git-commit -m "Initial commit." && + HEAD=$(git-rev-parse --verify HEAD)' + +SHA1= + +test_expect_success \ + 'see if git show-ref works as expected' \ + 'git-branch a && + SHA1=$(< .git/refs/heads/a) && + echo "$SHA1 refs/heads/a" >expect && + git-show-ref a >result && + diff expect result' + +test_expect_success \ + 'see if a branch still exists when packed' \ + 'git-branch b && + git-pack-refs && + rm .git/refs/heads/b && + echo "$SHA1 refs/heads/b" >expect && + git-show-ref b >result && + diff expect result' + +# test_expect_failure \ +# 'git branch c/d should barf if branch c exists' \ +# 'git-branch c && +# git-pack-refs && +# rm .git/refs/heads/c && +# git-branch c/d' + +test_expect_success \ + 'see if a branch still exists after git pack-refs --prune' \ + 'git-branch e && + git-pack-refs --prune && + echo "$SHA1 refs/heads/e" >expect && + git-show-ref e >result && + diff expect result' + +test_expect_failure \ + 'see if git pack-refs --prune remove ref files' \ + 'git-branch f && + git-pack-refs --prune && + ls .git/refs/heads/f' + +test_expect_success \ + 'git branch g should work when git branch g/h has been deleted' \ + 'git-branch g/h && + git-pack-refs --prune && + git-branch -d g/h && + git-branch g && + git-pack-refs && + git-branch -d g' + +test_done -- cgit v0.10.2-6-g49f6 From 5be7649131379e49f27d89cb6dd5bd8d0912a3d6 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 28 Sep 2006 07:00:38 +0200 Subject: When creating branch c/d check that branch c does not already exists. With packed refs, there may not be a ".git/refs/heads/c" file when branch c exists. And currently in this case, there is no check to prevent creation of branch c/d. This should probably be rewritten in C and done after the ref lock has been taken to make sure no race exists though. This is mainly to make all test cases in "t3210-pack-refs.sh" work. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-branch.sh b/git-branch.sh index bf84b30..c616830 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -121,6 +121,16 @@ then done fi +branchdir=$(dirname $branchname) +while test "$branchdir" != "." +do + if git-show-ref --verify --quiet -- "refs/heads/$branchdir" + then + die "$branchdir already exists." + fi + branchdir=$(dirname $branchdir) +done + prev='' if git-show-ref --verify --quiet -- "refs/heads/$branchname" then -- cgit v0.10.2-6-g49f6 From fc12f0829d027a6f7a86b38ae56b1424f2378470 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 28 Sep 2006 07:02:00 +0200 Subject: Uncomment test case: git branch c/d should barf if branch c exists. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index 2cc03e6..193fe1f 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -37,12 +37,12 @@ test_expect_success \ git-show-ref b >result && diff expect result' -# test_expect_failure \ -# 'git branch c/d should barf if branch c exists' \ -# 'git-branch c && -# git-pack-refs && -# rm .git/refs/heads/c && -# git-branch c/d' +test_expect_failure \ + 'git branch c/d should barf if branch c exists' \ + 'git-branch c && + git-pack-refs && + rm .git/refs/heads/c && + git-branch c/d' test_expect_success \ 'see if a branch still exists after git pack-refs --prune' \ -- cgit v0.10.2-6-g49f6 From bc7127ef0f81d996e9691c6c898b926867aae89f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 02:25:30 -0700 Subject: ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore. It is normal to have .git/refs/heads/foo directory which is empty after the last branch whose name starts with foo/ is removed. Make sure we notice this case and allow creation of branch foo by removing the empty directory. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 3d4cdd1..b433c0c 100644 --- a/refs.c +++ b/refs.c @@ -473,6 +473,59 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } +static int remove_empty_dir_recursive(char *path, int len) +{ + DIR *dir = opendir(path); + struct dirent *e; + int ret = 0; + + if (!dir) + return -1; + if (path[len-1] != '/') + path[len++] = '/'; + while ((e = readdir(dir)) != NULL) { + struct stat st; + int namlen; + if ((e->d_name[0] == '.') && + ((e->d_name[1] == 0) || + ((e->d_name[1] == '.') && e->d_name[2] == 0))) + continue; /* "." and ".." */ + + namlen = strlen(e->d_name); + if ((len + namlen < PATH_MAX) && + strcpy(path + len, e->d_name) && + !lstat(path, &st) && + S_ISDIR(st.st_mode) && + remove_empty_dir_recursive(path, len + namlen)) + continue; /* happy */ + + /* path too long, stat fails, or non-directory still exists */ + ret = -1; + break; + } + closedir(dir); + if (!ret) { + path[len] = 0; + ret = rmdir(path); + } + return ret; +} + +static int remove_empty_directories(char *file) +{ + /* we want to create a file but there is a directory there; + * if that is an empty directory (or a directory that contains + * only empty directories), remove them. + */ + char path[PATH_MAX]; + int len = strlen(file); + + if (len >= PATH_MAX) /* path too long ;-) */ + return -1; + strcpy(path, file); + return remove_empty_dir_recursive(path, len); +} + static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1) { char *ref_file; @@ -485,6 +538,17 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock->lock_fd = -1; ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); + if (!ref && errno == EISDIR) { + /* we are trying to lock foo but we used to + * have foo/bar which now does not exist; + * it is normal for the empty directory 'foo' + * to remain. + */ + ref_file = git_path("%s", orig_ref); + if (remove_empty_directories(ref_file)) + die("there are still refs under '%s'", orig_ref); + ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL); + } if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", -- cgit v0.10.2-6-g49f6 From 5e290ff75a0e6996f297dc438aceb8e1f29a20a5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 12:37:37 -0700 Subject: refs: minor restructuring of cached refs data. Once we read packed and loose refs, for_each_ref() and friends kept using them even after write_ref_sha1() and delete_ref() changed the refs. This adds invalidate_cached_refs() as a way to flush the cached information. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index b433c0c..6ee5f96 100644 --- a/refs.c +++ b/refs.c @@ -66,12 +66,42 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1, return list; } -static struct ref_list *get_packed_refs(void) +/* + * Future: need to be in "struct repository" + * when doing a full libification. + */ +struct cached_refs { + char did_loose; + char did_packed; + struct ref_list *loose; + struct ref_list *packed; +} cached_refs; + +static void free_ref_list(struct ref_list *list) +{ + struct ref_list *next; + for ( ; list; list = next) { + next = list->next; + free(list); + } +} + +static void invalidate_cached_refs(void) { - static int did_refs = 0; - static struct ref_list *refs = NULL; + struct cached_refs *ca = &cached_refs; + + if (ca->did_loose && ca->loose) + free_ref_list(ca->loose); + if (ca->did_packed && ca->packed) + free_ref_list(ca->packed); + ca->loose = ca->packed = NULL; + ca->did_loose = ca->did_packed = 0; +} - if (!did_refs) { +static struct ref_list *get_packed_refs(void) +{ + if (!cached_refs.did_packed) { + struct ref_list *refs = NULL; FILE *f = fopen(git_path("packed-refs"), "r"); if (f) { struct ref_list *list = NULL; @@ -86,9 +116,10 @@ static struct ref_list *get_packed_refs(void) fclose(f); refs = list; } - did_refs = 1; + cached_refs.packed = refs; + cached_refs.did_packed = 1; } - return refs; + return cached_refs.packed; } static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) @@ -138,14 +169,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *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; + if (!cached_refs.did_loose) { + cached_refs.loose = get_ref_dir("refs", NULL); + cached_refs.did_loose = 1; } - return refs; + return cached_refs.loose; } /* We allow "recursive" symbolic refs. Only within reason, though */ @@ -401,6 +429,7 @@ int delete_ref(const char *refname, unsigned char *sha1) fprintf(stderr, "warning: unlink(%s) failed: %s", lock->log_file, strerror(errno)); + invalidate_cached_refs(); return ret; } @@ -665,6 +694,7 @@ int write_ref_sha1(struct ref_lock *lock, unlock_ref(lock); return -1; } + invalidate_cached_refs(); if (log_ref_write(lock, sha1, logmsg) < 0) { unlock_ref(lock); return -1; -- cgit v0.10.2-6-g49f6 From 5cc3cef997503e7543d927dbe23daca891131168 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 14:14:31 -0700 Subject: lock_ref_sha1(): do not sometimes error() and sometimes die(). This cleans up the error path in the function so it does not die() itself sometimes while signalling an error with NULL some other times which was inconsistent and confusing. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 6ee5f96..157de43 100644 --- a/refs.c +++ b/refs.c @@ -561,6 +561,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char const char *orig_ref = ref; struct ref_lock *lock; struct stat st; + int last_errno = 0; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); lock = xcalloc(1, sizeof(struct ref_lock)); @@ -574,17 +575,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char * to remain. */ ref_file = git_path("%s", orig_ref); - if (remove_empty_directories(ref_file)) - die("there are still refs under '%s'", orig_ref); + if (remove_empty_directories(ref_file)) { + last_errno = errno; + error("there are still refs under '%s'", orig_ref); + goto error_return; + } ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL); } if (!ref) { - int last_errno = errno; + last_errno = errno; error("unable to resolve reference %s: %s", orig_ref, strerror(errno)); - unlock_ref(lock); - errno = last_errno; - return NULL; + goto error_return; } lock->lk = xcalloc(1, sizeof(struct lock_file)); @@ -593,11 +595,19 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char ref_file = git_path("%s", ref); lock->force_write = lstat(ref_file, &st) && errno == ENOENT; - if (safe_create_leading_directories(ref_file)) - die("unable to create directory for %s", ref_file); + if (safe_create_leading_directories(ref_file)) { + last_errno = errno; + error("unable to create directory for %s", ref_file); + goto error_return; + } lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; + + error_return: + unlock_ref(lock); + errno = last_errno; + return NULL; } struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) -- cgit v0.10.2-6-g49f6 From 22a3844ebada55c207e2b85fb68509a7c9e2b5f0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 14:19:25 -0700 Subject: lock_ref_sha1(): check D/F conflict with packed ref when creating. This makes the ref locking codepath to notice if an existing ref overlaps with the ref we are creating. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 157de43..2bfa92a 100644 --- a/refs.c +++ b/refs.c @@ -588,6 +588,30 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char orig_ref, strerror(errno)); goto error_return; } + if (is_null_sha1(lock->old_sha1)) { + /* The ref did not exist and we are creating it. + * Make sure there is no existing ref that is packed + * whose name begins with our refname, nor a ref whose + * name is a proper prefix of our refname. + */ + int namlen = strlen(ref); /* e.g. 'foo/bar' */ + struct ref_list *list = get_packed_refs(); + while (list) { + /* list->name could be 'foo' or 'foo/bar/baz' */ + int len = strlen(list->name); + int cmplen = (namlen < len) ? namlen : len; + const char *lead = (namlen < len) ? list->name : ref; + + if (!strncmp(ref, list->name, cmplen) && + lead[cmplen] == '/') { + error("'%s' exists; cannot create '%s'", + list->name, ref); + goto error_return; + } + list = list->next; + } + } + lock->lk = xcalloc(1, sizeof(struct lock_file)); lock->ref_name = xstrdup(ref); -- cgit v0.10.2-6-g49f6 From c0277d15effb9efcaaaa0cd84decf71a327ac07b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 15:02:00 -0700 Subject: delete_ref(): delete packed ref This implements deletion of a packed ref. Since it is a very rare event to delete a ref compared to looking up, creating and updating, this opts to remove the ref from the packed-ref file instead of doing any of the filesystem based "negative ref" trick to optimize the deletion path. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 2bfa92a..858c534 100644 --- a/refs.c +++ b/refs.c @@ -406,33 +406,6 @@ int get_ref_sha1(const char *ref, unsigned char *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)); - - invalidate_cached_refs(); - return ret; -} - /* * Make sure "ref" is something reasonable to have under ".git/refs/"; * We do not like it if: @@ -555,7 +528,7 @@ static int remove_empty_directories(char *file) return remove_empty_dir_recursive(path, len); } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1) +static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag) { char *ref_file; const char *orig_ref = ref; @@ -567,7 +540,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); + ref = resolve_ref(ref, lock->old_sha1, mustexist, flag); if (!ref && errno == EISDIR) { /* we are trying to lock foo but we used to * have foo/bar which now does not exist; @@ -580,7 +553,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char error("there are still refs under '%s'", orig_ref); goto error_return; } - ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL); + ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag); } if (!ref) { last_errno = errno; @@ -640,12 +613,84 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) if (check_ref_format(ref)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, old_sha1); + return lock_ref_sha1_basic(refpath, old_sha1, NULL); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1) { - return lock_ref_sha1_basic(ref, old_sha1); + return lock_ref_sha1_basic(ref, old_sha1, NULL); +} + +static int repack_without_ref(const char *refname) +{ + struct ref_list *list, *packed_ref_list; + int fd; + int found = 0; + struct lock_file packlock; + + packed_ref_list = get_packed_refs(); + for (list = packed_ref_list; list; list = list->next) { + if (!strcmp(refname, list->name)) { + found = 1; + break; + } + } + if (!found) + return 0; + memset(&packlock, 0, sizeof(packlock)); + fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0); + if (fd < 0) + return error("cannot delete '%s' from packed refs", refname); + + for (list = packed_ref_list; list; list = list->next) { + char line[PATH_MAX + 100]; + int len; + + if (!strcmp(refname, list->name)) + continue; + len = snprintf(line, sizeof(line), "%s %s\n", + sha1_to_hex(list->sha1), list->name); + /* this should not happen but just being defensive */ + if (len > sizeof(line)) + die("too long a refname '%s'", list->name); + write_or_die(fd, line, len); + } + return commit_lock_file(&packlock); +} + +int delete_ref(const char *refname, unsigned char *sha1) +{ + struct ref_lock *lock; + int err, i, ret = 0, flag = 0; + + lock = lock_ref_sha1_basic(refname, sha1, &flag); + if (!lock) + return 1; + if (!(flag & REF_ISPACKED)) { + /* loose */ + 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] = '.'; + } + /* removing the loose one could have resurrected an earlier + * packed one. Also, if it was not loose we need to repack + * without it. + */ + ret |= repack_without_ref(refname); + + err = unlink(lock->log_file); + if (err && errno != ENOENT) + fprintf(stderr, "warning: unlink(%s) failed: %s", + lock->log_file, strerror(errno)); + invalidate_cached_refs(); + unlock_ref(lock); + return ret; } void unlock_ref(struct ref_lock *lock) -- cgit v0.10.2-6-g49f6 From 936a9508cc0d5369b00c76ee63cdb81556e7be39 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 15:21:28 -0700 Subject: git-branch: remove D/F check done by hand. Now ref creation codepath in lock_ref_sha1() and friends notices the directory/file conflict situation, we do not do this by hand in git-branch anymore. Signed-off-by: Junio C Hamano diff --git a/git-branch.sh b/git-branch.sh index c616830..bf84b30 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -121,16 +121,6 @@ then done fi -branchdir=$(dirname $branchname) -while test "$branchdir" != "." -do - if git-show-ref --verify --quiet -- "refs/heads/$branchdir" - then - die "$branchdir already exists." - fi - branchdir=$(dirname $branchdir) -done - prev='' if git-show-ref --verify --quiet -- "refs/heads/$branchname" then -- cgit v0.10.2-6-g49f6 From 2eaf22242f61b13c38c87cbb0e84c84974c52d66 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 1 Oct 2006 00:27:27 -0700 Subject: show-ref --hash=len, --abbrev=len, and --abbrev This teaches show-ref to abbreviate the object name. Signed-off-by: Junio C Hamano diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index b724d83..5973a82 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] - [-s|--hash] [--tags] [--heads] [--] ... + [-s|--hash] [--abbrev] [--tags] [--heads] [--] ... DESCRIPTION ----------- @@ -37,14 +37,13 @@ OPTIONS -d, --dereference:: - Dereference tags into object IDs. They will be shown with "^{}" + Dereference tags into object IDs as well. 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. + --dereference the dereferenced tag will still be shown after the SHA1. --verify:: @@ -52,6 +51,11 @@ OPTIONS Aside from returning an error code of 1, it will also print an error message if '--quiet' was not specified. +--abbrev, --abbrev=len:: + + Abbreviate the object name. When using `--hash`, you do + not have to say `--hash --abbrev`; `--hash=len` would do. + -q, --quiet:: Do not print any results to stdout. When combined with '--verify' this diff --git a/builtin-show-ref.c b/builtin-show-ref.c index 12c457c..f2912e8 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -6,12 +6,13 @@ 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; + found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0; static const char **pattern; static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata) { struct object *obj; + const char *hex; if (tags_only || heads_only) { int match; @@ -51,13 +52,16 @@ match: } if (quiet) return 0; + + hex = find_unique_abbrev(sha1, abbrev); if (hash_only) - printf("%s\n", sha1_to_hex(sha1)); + printf("%s\n", hex); else - printf("%s %s\n", sha1_to_hex(sha1), refname); + printf("%s %s\n", hex, refname); if (deref_tags && obj->type == OBJ_TAG) { obj = deref_tag(obj, refname, 0); - printf("%s %s^{}\n", sha1_to_hex(obj->sha1), refname); + hex = find_unique_abbrev(obj->sha1, abbrev); + printf("%s %s^{}\n", hex, refname); } return 0; } @@ -94,6 +98,29 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix) hash_only = 1; continue; } + if (!strncmp(arg, "--hash=", 7) || + (!strncmp(arg, "--abbrev", 8) && + (arg[8] == '=' || arg[8] == '\0'))) { + if (arg[3] != 'h' && !arg[8]) + /* --abbrev only */ + abbrev = DEFAULT_ABBREV; + else { + /* --hash= or --abbrev= */ + char *end; + if (arg[3] == 'h') { + hash_only = 1; + arg += 7; + } + else + arg += 9; + abbrev = strtoul(arg, &end, 10); + if (*end || abbrev > 40) + usage(show_ref_usage); + if (abbrev < MINIMUM_ABBREV) + abbrev = MINIMUM_ABBREV; + } + continue; + } if (!strcmp(arg, "--verify")) { verify = 1; continue; -- cgit v0.10.2-6-g49f6 From 28bed6ea2198f6589ad43e48666906a879839442 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 1 Oct 2006 14:36:49 +0200 Subject: Fix a remove_empty_dir_recursive problem. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 858c534..221eb38 100644 --- a/refs.c +++ b/refs.c @@ -498,7 +498,7 @@ static int remove_empty_dir_recursive(char *path, int len) strcpy(path + len, e->d_name) && !lstat(path, &st) && S_ISDIR(st.st_mode) && - remove_empty_dir_recursive(path, len + namlen)) + !remove_empty_dir_recursive(path, len + namlen)) continue; /* happy */ /* path too long, stat fails, or non-directory still exists */ -- cgit v0.10.2-6-g49f6 From 14c8a681f751c425f47be38a5e98b514f000d499 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 1 Oct 2006 14:38:18 +0200 Subject: Clean up "git-branch.sh" and add remove recursive dir test cases. Now that directory recursive remove works in the core C code, we don't need to do it in "git-branch.sh". Also add test cases to check that directory recursive remove will continue to work. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-branch.sh b/git-branch.sh index bf84b30..4379a07 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -111,16 +111,6 @@ rev=$(git-rev-parse --verify "$head") || exit git-check-ref-format "heads/$branchname" || die "we do not like '$branchname' as a branch name." -if [ -d "$GIT_DIR/refs/heads/$branchname" ] -then - for refdir in `cd "$GIT_DIR" && \ - find "refs/heads/$branchname" -type d | sort -r` - do - rmdir "$GIT_DIR/$refdir" || \ - die "Could not delete '$refdir', there may still be a ref there." - done -fi - prev='' if git-show-ref --verify --quiet -- "refs/heads/$branchname" then diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index 193fe1f..f31e79c 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -67,4 +67,31 @@ test_expect_success \ git-pack-refs && git-branch -d g' +test_expect_failure \ + 'git branch i/j/k should barf if branch i exists' \ + 'git-branch i && + git-pack-refs --prune && + git-branch i/j/k' + +test_expect_success \ + 'test git branch k after branch k/l/m and k/lm have been deleted' \ + 'git-branch k/l && + git-branch k/lm && + git-branch -d k/l && + git-branch k/l/m && + git-branch -d k/l/m && + git-branch -d k/lm && + git-branch k' + +test_expect_success \ + 'test git branch n after some branch deletion and pruning' \ + 'git-branch n/o && + git-branch n/op && + git-branch -d n/o && + git-branch n/o/p && + git-branch -d n/op && + git-pack-refs --prune && + git-branch -d n/o/p && + git-branch n' + test_done -- cgit v0.10.2-6-g49f6 From 26a063a10bca57f65d8fed6c4550a70d44a70b81 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 1 Oct 2006 11:41:00 -0700 Subject: Fix refs.c;:repack_without_ref() clean-up path The function repack_without_ref() passes a lock-file structure on the stack to hold_lock_file_for_update(), which in turn registers it to be cleaned up via atexit(). This is a big no-no. This is the same bug James Bottomley fixed with commit 31f584c242e7af28018ff920b6c8d1952beadbd4. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 221eb38..aa4c4e0 100644 --- a/refs.c +++ b/refs.c @@ -621,12 +621,13 @@ struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *o return lock_ref_sha1_basic(ref, old_sha1, NULL); } +static struct lock_file packlock; + static int repack_without_ref(const char *refname) { struct ref_list *list, *packed_ref_list; int fd; int found = 0; - struct lock_file packlock; packed_ref_list = get_packed_refs(); for (list = packed_ref_list; list; list = list->next) { -- cgit v0.10.2-6-g49f6 From d3d0013c59ed840520b86a65697137cb2c62819c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 1 Oct 2006 22:16:22 +0200 Subject: Use git-update-ref to delete a tag instead of rm()ing the ref file. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-tag.sh b/git-tag.sh index 2bde3c0..6463b31 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -47,8 +47,10 @@ do -d) shift tag_name="$1" - rm "$GIT_DIR/refs/tags/$tag_name" && \ - echo "Deleted tag $tag_name." + tag=$(git-show-ref --verify --hash -- "refs/tags/$tag_name") || + die "Seriously, what tag are you talking about?" + git-update-ref -m 'tag: delete' -d "refs/tags/$tag_name" "$tag" && + echo "Deleted tag $tag_name." exit $? ;; -*) -- cgit v0.10.2-6-g49f6 From b431b2822f361efcb940adbc1f2097e122e90ed9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 1 Oct 2006 22:33:04 +0200 Subject: Check that a tag exists using show-ref instead of looking for the ref file. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-tag.sh b/git-tag.sh index 6463b31..a3f1819 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -66,7 +66,7 @@ done name="$1" [ "$name" ] || usage prev=0000000000000000000000000000000000000000 -if test -e "$GIT_DIR/refs/tags/$name" +if git-show-ref --verify --quiet -- "refs/tags/$name" then test -n "$force" || die "tag '$name' already exists" prev=`git rev-parse "refs/tags/$name"` -- cgit v0.10.2-6-g49f6 From 367337040d8d39294bf676672dfefc542717195b Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 2 Oct 2006 06:36:15 +0200 Subject: Do not create tag leading directories since git update-ref does it. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano diff --git a/git-tag.sh b/git-tag.sh index a3f1819..ac269e3 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -112,6 +112,5 @@ if [ "$annotate" ]; then object=$(git-mktag < "$GIT_DIR"/TAG_TMP) fi -leading=`expr "refs/tags/$name" : '\(.*\)/'` && -mkdir -p "$GIT_DIR/$leading" && -GIT_DIR="$GIT_DIR" git update-ref "refs/tags/$name" "$object" "$prev" +git update-ref "refs/tags/$name" "$object" "$prev" + -- cgit v0.10.2-6-g49f6 From 7a21632fa346df58d94d32f09625025931ef13ec Mon Sep 17 00:00:00 2001 From: Dennis Stosberg Date: Mon, 2 Oct 2006 19:23:53 +0200 Subject: lock_ref_sha1_basic does not remove empty directories on BSD lock_ref_sha1_basic relies on errno beeing set to EISDIR by the call to read() in resolve_ref() to detect directories. But calling read() on a directory under NetBSD returns EPERM, and even succeeds for local filesystems on FreeBSD. Signed-off-by: Dennis Stosberg Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 5e65314..98327d7 100644 --- a/refs.c +++ b/refs.c @@ -42,6 +42,12 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) } } + /* Is it a directory? */ + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + return NULL; + } + /* * Anything else, just open it and try to use it as * a ref -- cgit v0.10.2-6-g49f6 From 47292d65dec754bbe37d82369faabeaf8f0cdb7a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 3 Oct 2006 02:08:19 -0700 Subject: git-fetch: do not look into $GIT_DIR/refs to see if a tag exists. Signed-off-by: Junio C Hamano diff --git a/git-fetch.sh b/git-fetch.sh index f1522bd..e8a7668 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -417,7 +417,7 @@ case "$no_tags$tags" in sed -ne 's|^\([0-9a-f]*\)[ ]\(refs/tags/.*\)^{}$|\1 \2|p' | while read sha1 name do - test -f "$GIT_DIR/$name" && continue + git-show-ref --verify --quiet -- $name && continue git-check-ref-format "$name" || { echo >&2 "warning: tag ${name} ignored" continue -- cgit v0.10.2-6-g49f6 From 03a182107fdb36170a72b8a3d94de2b52e3f6668 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 3 Oct 2006 02:15:18 -0700 Subject: pack-refs: use lockfile as everybody else does. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 4093973..ede4743 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -1,7 +1,6 @@ #include "cache.h" #include "refs.h" -static const char *result_path, *lock_path; static const char builtin_pack_refs_usage[] = "git-pack-refs [--prune]"; @@ -17,12 +16,6 @@ struct pack_refs_cb_data { 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, @@ -69,6 +62,8 @@ static void prune_refs(struct ref_to_prune *r) } } +static struct lock_file packed; + int cmd_pack_refs(int argc, const char **argv, const char *prefix) { int fd, i; @@ -88,14 +83,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) 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); - + fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1); cbdata.refs_file = fdopen(fd, "w"); if (!cbdata.refs_file) die("unable to create ref-pack file structure (%s)", @@ -103,9 +91,8 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) for_each_ref(handle_one_ref, &cbdata); fsync(fd); fclose(cbdata.refs_file); - if (rename(lock_path, result_path) < 0) + if (commit_lock_file(&packed) < 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; -- cgit v0.10.2-6-g49f6 From 422b4a0e03658d0933a7abc149f175735ea9c4b5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 4 Oct 2006 21:37:15 -0700 Subject: pack-refs: call fflush before fsync. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index ede4743..23d0d07 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -89,6 +89,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) die("unable to create ref-pack file structure (%s)", strerror(errno)); for_each_ref(handle_one_ref, &cbdata); + fflush(cbdata.refs_file); fsync(fd); fclose(cbdata.refs_file); if (commit_lock_file(&packed) < 0) -- cgit v0.10.2-6-g49f6 From ab2a1a32ffa5a39aaf4204bd717562bce49e0a36 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 23:16:15 -0700 Subject: ref-log: allow ref@{count} syntax. Often I find myself wanting to say 'tip of "next" before I merged the last three topics'. Now I can say that with: git log next@{3}..next Since small integers alone are invalid input strings to approxidate, there is no fear of confusion. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 305c1a9..d7f4aa5 100644 --- a/refs.c +++ b/refs.c @@ -795,7 +795,7 @@ int write_ref_sha1(struct ref_lock *lock, return 0; } -int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) +int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1) { const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; char *tz_c; @@ -828,7 +828,7 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) if (!lastgt) die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); - if (date <= at_time) { + if (date <= at_time || cnt == 0) { if (lastrec) { if (get_sha1_hex(lastrec, logged_sha1)) die("Log %s is corrupt.", logfile); @@ -859,6 +859,8 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) return 0; } lastrec = rec; + if (cnt > 0) + cnt--; } rec = logdata; diff --git a/refs.h b/refs.h index 0d4d79e..a57d437 100644 --- a/refs.h +++ b/refs.h @@ -39,7 +39,7 @@ extern void unlock_ref(struct ref_lock *lock); extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); /** Reads log for the value of ref during at_time. **/ -extern int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1); +extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1); /** Returns 0 if target has the right format for a ref. **/ extern int check_ref_format(const char *target); diff --git a/sha1_name.c b/sha1_name.c index ed711f2..e517033 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -249,24 +249,23 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; const char **p, *ref; char *real_ref = NULL; - int refs_found = 0, am; - unsigned long at_time = (unsigned long)-1; + int refs_found = 0; + int at, reflog_len; unsigned char *this_result; unsigned char sha1_from_ref[20]; if (len == 40 && !get_sha1_hex(str, sha1)) return 0; - /* At a given period of time? "@{2 hours ago}" */ - for (am = 1; am < len - 1; am++) { - if (str[am] == '@' && str[am+1] == '{' && str[len-1] == '}') { - int date_len = len - am - 3; - char *date_spec = xmalloc(date_len + 1); - strlcpy(date_spec, str + am + 2, date_len + 1); - at_time = approxidate(date_spec); - free(date_spec); - len = am; - break; + /* basic@{time or number} format to query ref-log */ + reflog_len = 0; + if (str[len-1] == '}') { + for (at = 1; at < len - 1; at++) { + if (str[at] == '@' && str[at+1] == '{') { + reflog_len = (len-1) - (at+2); + len = at; + break; + } } } @@ -291,11 +290,22 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (warn_ambiguous_refs && refs_found > 1) fprintf(stderr, warning, len, str); - if (at_time != (unsigned long)-1) { - read_ref_at( - real_ref, - at_time, - sha1); + if (reflog_len) { + /* Is it asking for N-th entry, or approxidate? */ + int nth, i; + unsigned long at_time; + for (i = nth = 0; 0 <= nth && i < reflog_len; i++) { + char ch = str[at+2+i]; + if ('0' <= ch && ch <= '9') + nth = nth * 10 + ch - '0'; + else + nth = -1; + } + if (0 <= nth) + at_time = 0; + else + at_time = approxidate(str + at + 2); + read_ref_at(real_ref, at_time, nth, sha1); } free(real_ref); -- cgit v0.10.2-6-g49f6 From 26e5fc3415a294546b4009c7280fed4d367d4e62 Mon Sep 17 00:00:00 2001 From: Dennis Stosberg Date: Fri, 6 Oct 2006 11:10:54 +0200 Subject: Remove bashism from t3210-pack-refs.sh This bashism makes the test fail if /bin/sh is not bash. Signed-off-by: Dennis Stosberg Signed-off-by: Junio C Hamano diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index f31e79c..ca5bd49 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -23,7 +23,7 @@ SHA1= test_expect_success \ 'see if git show-ref works as expected' \ 'git-branch a && - SHA1=$(< .git/refs/heads/a) && + SHA1=`cat .git/refs/heads/a` && echo "$SHA1 refs/heads/a" >expect && git-show-ref a >result && diff expect result' -- cgit v0.10.2-6-g49f6 From 4057deb5de110176ac19519177654108607b685c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Oct 2006 01:35:18 -0700 Subject: core.logallrefupdates create new log file only for branch heads. It used to mean "create log file for any ref that is updated", but now it creates new log files only for branch heads. The old behaviour made this configuration less useful than otherwise it would be; automatically creating log file for tags is almost always not useful. Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 84e3891..232e2a9 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -71,12 +71,16 @@ core.preferSymlinkRefs:: expect HEAD to be a symbolic link. core.logAllRefUpdates:: - If true, `git-update-ref` will append a line to - "$GIT_DIR/logs/" listing the new SHA1 and the date/time - of the update. If the file does not exist it will be - created automatically. This information can be used to - determine what commit was the tip of a branch "2 days ago". - This value is false by default (no logging). + Updates to a ref is logged to the file + "$GIT_DIR/logs/", by appending the new and old + SHA1, the date/time and the reason of the update, but + only when the file exists. If this configuration + variable is set to true, missing "$GIT_DIR/logs/" + file is automatically created for branch heads. + + This information can be used to determine what commit + was the tip of a branch "2 days ago". This value is + false by default (no automated creation of log files). core.repositoryFormatVersion:: Internal variable identifying the repository format and layout diff --git a/refs.c b/refs.c index 305c1a9..75a0d7b 100644 --- a/refs.c +++ b/refs.c @@ -721,7 +721,8 @@ static int log_ref_write(struct ref_lock *lock, char *logrec; const char *committer; - if (log_all_ref_updates) { + if (log_all_ref_updates && + !strncmp(lock->ref_name, "refs/heads/", 11)) { if (safe_create_leading_directories(lock->log_file) < 0) return error("unable to create directory for %s", lock->log_file); -- cgit v0.10.2-6-g49f6 From b3d4204fc49959ddcd54c329be94189f98714d73 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Oct 2006 01:36:08 -0700 Subject: git-pack-refs --all This changes 'git-pack-refs' to pack only tags by default. Branches are meant to be updated, either by committing onto it yourself or tracking remote branches, and packed entries can become stale easily, but tags are usually "create once and live forever" and benefit more from packing. Signed-off-by: Junio C Hamano diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 23d0d07..1087657 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -2,7 +2,7 @@ #include "refs.h" static const char builtin_pack_refs_usage[] = -"git-pack-refs [--prune]"; +"git-pack-refs [--all] [--prune]"; struct ref_to_prune { struct ref_to_prune *next; @@ -68,6 +68,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) { int fd, i; struct pack_refs_cb_data cbdata; + int (*iterate_ref)(each_ref_fn, void *) = for_each_tag_ref; memset(&cbdata, 0, sizeof(cbdata)); @@ -77,6 +78,10 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) cbdata.prune = 1; continue; } + if (!strcmp(arg, "--all")) { + iterate_ref = for_each_ref; + continue; + } /* perhaps other parameters later... */ break; } @@ -88,7 +93,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) if (!cbdata.refs_file) die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref, &cbdata); + iterate_ref(handle_one_ref, &cbdata); fflush(cbdata.refs_file); fsync(fd); fclose(cbdata.refs_file); diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index ca5bd49..a4fbfda 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -31,7 +31,7 @@ test_expect_success \ test_expect_success \ 'see if a branch still exists when packed' \ 'git-branch b && - git-pack-refs && + git-pack-refs --all && rm .git/refs/heads/b && echo "$SHA1 refs/heads/b" >expect && git-show-ref b >result && @@ -40,14 +40,14 @@ test_expect_success \ test_expect_failure \ 'git branch c/d should barf if branch c exists' \ 'git-branch c && - git-pack-refs && + git-pack-refs --all && rm .git/refs/heads/c && git-branch c/d' test_expect_success \ 'see if a branch still exists after git pack-refs --prune' \ 'git-branch e && - git-pack-refs --prune && + git-pack-refs --all --prune && echo "$SHA1 refs/heads/e" >expect && git-show-ref e >result && diff expect result' @@ -55,22 +55,22 @@ test_expect_success \ test_expect_failure \ 'see if git pack-refs --prune remove ref files' \ 'git-branch f && - git-pack-refs --prune && + git-pack-refs --all --prune && ls .git/refs/heads/f' test_expect_success \ 'git branch g should work when git branch g/h has been deleted' \ 'git-branch g/h && - git-pack-refs --prune && + git-pack-refs --all --prune && git-branch -d g/h && git-branch g && - git-pack-refs && + git-pack-refs --all && git-branch -d g' test_expect_failure \ 'git branch i/j/k should barf if branch i exists' \ 'git-branch i && - git-pack-refs --prune && + git-pack-refs --all --prune && git-branch i/j/k' test_expect_success \ @@ -90,7 +90,7 @@ test_expect_success \ git-branch -d n/o && git-branch n/o/p && git-branch -d n/op && - git-pack-refs --prune && + git-pack-refs --all --prune && git-branch -d n/o/p && git-branch n' -- cgit v0.10.2-6-g49f6 From 1974bf620b436b014bfe86179ff76485610a4887 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Oct 2006 21:15:59 -0700 Subject: core.logallrefupdates thinko-fix diff --git a/refs.c b/refs.c index 75a0d7b..3d100df 100644 --- a/refs.c +++ b/refs.c @@ -731,7 +731,7 @@ static int log_ref_write(struct ref_lock *lock, logfd = open(lock->log_file, oflags, 0666); if (logfd < 0) { - if (!log_all_ref_updates && errno == ENOENT) + if (!(oflags & O_CREAT) && errno == ENOENT) return 0; return error("Unable to append to %s: %s", lock->log_file, strerror(errno)); -- cgit v0.10.2-6-g49f6 From a9cb3c6ecb97c4734423045f47899e03f135d3bd Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 12 Oct 2006 14:52:42 -0700 Subject: git-revert with conflicts to behave as git-merge with conflicts In a busy project, reverting a commit almost always results in a conflict between one or more files (depending on the commit being reverted). It is useful to record this conflict in the commit-to-be message of the resulting commit (after the resolve). The process now becomes: git-revert git-update-index git-commit -s And the commit message is now a merge of the revert commit message and the conflict commit message, giving the user a chance to edit it or add more information: Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano diff --git a/git-commit.sh b/git-commit.sh index ee5a165..8ac8dcc 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -466,7 +466,7 @@ then elif test "$use_commit" != "" then git-cat-file commit "$use_commit" | sed -e '1,/^$/d' -elif test -f "$GIT_DIR/MERGE_HEAD" && test -f "$GIT_DIR/MERGE_MSG" +elif test -f "$GIT_DIR/MERGE_MSG" then cat "$GIT_DIR/MERGE_MSG" elif test -f "$GIT_DIR/SQUASH_MSG" @@ -632,7 +632,7 @@ then 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" && - rm -f -- "$GIT_DIR/MERGE_HEAD" && + rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" && if test -f "$NEXT_INDEX" then mv "$NEXT_INDEX" "$THIS_INDEX" diff --git a/git-revert.sh b/git-revert.sh index 2bf35d1..066e677 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -141,9 +141,18 @@ git-read-tree -m -u --aggressive $base $head $next && result=$(git-write-tree 2>/dev/null) || { echo >&2 "Simple $me fails; trying Automatic $me." git-merge-index -o git-merge-one-file -a || { + mv -f .msg "$GIT_DIR/MERGE_MSG" + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" echo >&2 "Automatic $me failed. After resolving the conflicts," echo >&2 "mark the corrected paths with 'git-update-index '" - echo >&2 "and commit with 'git commit -F .msg'" + echo >&2 "and commit the result." case "$me" in cherry-pick) echo >&2 "You may choose to use the following when making" -- cgit v0.10.2-6-g49f6 From 3b463c3f02f83ef0bce2d5daa193459418e5258f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 19 Oct 2006 01:28:47 -0700 Subject: ref-log: fix D/F conflict coming from deleted refs. After deleting a branch l/k, you should be able to create a branch l. Earlier we added remove_empty_directories() on the ref creation side to remove leftover .git/refs/l directory but we also need a matching code to remove .git/logs/refs/l directory. Signed-off-by: Junio C Hamano diff --git a/refs.c b/refs.c index 3d100df..ed2e3b1 100644 --- a/refs.c +++ b/refs.c @@ -733,8 +733,18 @@ static int log_ref_write(struct ref_lock *lock, if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; - return error("Unable to append to %s: %s", - lock->log_file, strerror(errno)); + + if ((oflags & O_CREAT) && errno == EISDIR) { + if (remove_empty_directories(lock->log_file)) { + return error("There are still logs under '%s'", + lock->log_file); + } + logfd = open(lock->log_file, oflags, 0666); + } + + if (logfd < 0) + return error("Unable to append to %s: %s", + lock->log_file, strerror(errno)); } committer = git_committer_info(1); diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index a4fbfda..b1e9f2e 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -11,6 +11,8 @@ semantic is still the same. ' . ./test-lib.sh +echo '[core] logallrefupdates = true' >>.git/config + test_expect_success \ 'prepare a trivial repository' \ 'echo Hello > A && -- cgit v0.10.2-6-g49f6 From c31820c26b8f164433e67d28c403ca0df0316055 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 23 Oct 2006 23:27:45 +0200 Subject: Make git-branch a builtin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces git-branch.sh with builtin-branch.c The changes is basically a patch from Kristian Høgsberg, updated to apply onto current 'next' Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index e826247..be8bf39 100644 --- a/Makefile +++ b/Makefile @@ -156,7 +156,7 @@ BASIC_CFLAGS = BASIC_LDFLAGS = SCRIPT_SH = \ - git-bisect.sh git-branch.sh git-checkout.sh \ + git-bisect.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ git-fetch.sh \ git-ls-remote.sh \ @@ -267,6 +267,7 @@ BUILTIN_OBJS = \ builtin-add.o \ builtin-apply.o \ builtin-archive.o \ + builtin-branch.o \ builtin-cat-file.o \ builtin-checkout-index.o \ builtin-check-ref-format.o \ diff --git a/builtin-branch.c b/builtin-branch.c new file mode 100644 index 0000000..e028a53 --- /dev/null +++ b/builtin-branch.c @@ -0,0 +1,219 @@ +/* + * Builtin "git branch" + * + * Copyright (c) 2006 Kristian Høgsberg + * Based on git-branch.sh by Junio C Hamano. + */ + +#include "cache.h" +#include "refs.h" +#include "commit.h" +#include "builtin.h" + +static const char builtin_branch_usage[] = +"git-branch (-d | -D) | [-l] [-f] [] | [-r]"; + + +static const char *head; +static unsigned char head_sha1[20]; + +static int in_merge_bases(const unsigned char *sha1, + struct commit *rev1, + struct commit *rev2) +{ + struct commit_list *bases, *b; + int ret = 0; + + bases = get_merge_bases(rev1, rev2, 1); + for (b = bases; b; b = b->next) { + if (!hashcmp(sha1, b->item->object.sha1)) { + ret = 1; + break; + } + } + + free_commit_list(bases); + return ret; +} + +static void delete_branches(int argc, const char **argv, int force) +{ + struct commit *rev, *head_rev; + unsigned char sha1[20]; + char *name; + int i; + + head_rev = lookup_commit_reference(head_sha1); + for (i = 0; i < argc; i++) { + if (!strcmp(head, argv[i])) + die("Cannot delete the branch you are currently on."); + + name = xstrdup(mkpath("refs/heads/%s", argv[i])); + if (!resolve_ref(name, sha1, 1, NULL)) + die("Branch '%s' not found.", argv[i]); + + rev = lookup_commit_reference(sha1); + if (!rev || !head_rev) + die("Couldn't look up commit objects."); + + /* This checks whether the merge bases of branch and + * HEAD contains branch -- which means that the HEAD + * contains everything in both. + */ + + if (!force && + !in_merge_bases(sha1, rev, head_rev)) { + fprintf(stderr, + "The branch '%s' is not a strict subset of your current HEAD.\n" + "If you are sure you want to delete it, run 'git branch -D %s'.\n", + argv[i], argv[i]); + exit(1); + } + + if (delete_ref(name, sha1)) + printf("Error deleting branch '%s'\n", argv[i]); + else + printf("Deleted branch %s.\n", argv[i]); + + free(name); + } +} + +static int ref_index, ref_alloc; +static char **ref_list; + +static int append_ref(const char *refname, const unsigned char *sha1, int flags, + void *cb_data) +{ + if (ref_index >= ref_alloc) { + ref_alloc = alloc_nr(ref_alloc); + ref_list = xrealloc(ref_list, ref_alloc * sizeof(char *)); + } + + ref_list[ref_index++] = xstrdup(refname); + + return 0; +} + +static int ref_cmp(const void *r1, const void *r2) +{ + return strcmp(*(char **)r1, *(char **)r2); +} + +static void print_ref_list(int remote_only) +{ + int i; + + if (remote_only) + for_each_remote_ref(append_ref, NULL); + else + for_each_branch_ref(append_ref, NULL); + + qsort(ref_list, ref_index, sizeof(char *), ref_cmp); + + for (i = 0; i < ref_index; i++) { + if (!strcmp(ref_list[i], head)) + printf("* %s\n", ref_list[i]); + else + printf(" %s\n", ref_list[i]); + } +} + +static void create_branch(const char *name, const char *start, + int force, int reflog) +{ + struct ref_lock *lock; + struct commit *commit; + unsigned char sha1[20]; + char ref[PATH_MAX], msg[PATH_MAX + 20]; + + snprintf(ref, sizeof ref, "refs/heads/%s", name); + if (check_ref_format(ref)) + die("'%s' is not a valid branch name.", name); + + if (resolve_ref(ref, sha1, 1, NULL)) { + if (!force) + die("A branch named '%s' already exists.", name); + else if (!strcmp(head, name)) + die("Cannot force update the current branch."); + } + + if (get_sha1(start, sha1) || + (commit = lookup_commit_reference(sha1)) == NULL) + die("Not a valid branch point: '%s'.", start); + hashcpy(sha1, commit->object.sha1); + + lock = lock_any_ref_for_update(ref, NULL); + if (!lock) + die("Failed to lock ref for update: %s.", strerror(errno)); + + if (reflog) { + log_all_ref_updates = 1; + snprintf(msg, sizeof msg, "branch: Created from %s", start); + } + + if (write_ref_sha1(lock, sha1, msg) < 0) + die("Failed to write ref: %s.", strerror(errno)); +} + +int cmd_branch(int argc, const char **argv, const char *prefix) +{ + int delete = 0, force_delete = 0, force_create = 0, remote_only = 0; + int reflog = 0; + int i; + + git_config(git_default_config); + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (arg[0] != '-') + break; + if (!strcmp(arg, "--")) { + i++; + break; + } + if (!strcmp(arg, "-d")) { + delete = 1; + continue; + } + if (!strcmp(arg, "-D")) { + delete = 1; + force_delete = 1; + continue; + } + if (!strcmp(arg, "-f")) { + force_create = 1; + continue; + } + if (!strcmp(arg, "-r")) { + remote_only = 1; + continue; + } + if (!strcmp(arg, "-l")) { + reflog = 1; + continue; + } + usage(builtin_branch_usage); + } + + head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL)); + if (!head) + die("Failed to resolve HEAD as a valid ref."); + if (strncmp(head, "refs/heads/", 11)) + die("HEAD not found below refs/heads!"); + head += 11; + + if (delete) + delete_branches(argc - i, argv + i, force_delete); + else if (i == argc) + print_ref_list(remote_only); + else if (i == argc - 1) + create_branch(argv[i], head, force_create, reflog); + else if (i == argc - 2) + create_branch(argv[i], argv[i + 1], force_create, reflog); + else + usage(builtin_branch_usage); + + return 0; +} diff --git a/builtin.h b/builtin.h index 721b8d8..db9b369 100644 --- a/builtin.h +++ b/builtin.h @@ -15,6 +15,7 @@ extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_apply(int argc, const char **argv, const char *prefix); extern int cmd_archive(int argc, const char **argv, const char *prefix); +extern int cmd_branch(int argc, const char **argv, const char *prefix); extern int cmd_cat_file(int argc, const char **argv, const char *prefix); extern int cmd_checkout_index(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); diff --git a/git-branch.sh b/git-branch.sh deleted file mode 100755 index 4379a07..0000000 --- a/git-branch.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/bin/sh - -USAGE='[-l] [(-d | -D) ] | [[-f] []] | -r' -LONG_USAGE='If no arguments, show available branches and mark current branch with a star. -If one argument, create a new branch based off of current HEAD. -If two arguments, create a new branch based off of .' - -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -headref=$(git-symbolic-ref HEAD | sed -e 's|^refs/heads/||') - -delete_branch () { - option="$1" - shift - for branch_name - do - case ",$headref," in - ",$branch_name,") - die "Cannot delete the branch you are on." ;; - ,,) - die "What branch are you on anyway?" ;; - esac - 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 - -D) - ;; - *) - mbs=$(git-merge-base -a "$branch" HEAD | tr '\012' ' ') - case " $mbs " in - *' '$branch' '*) - # the merge base of branch and HEAD contains branch -- - # which means that the HEAD contains everything in both. - ;; - *) - echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD. -If you are sure you want to delete it, run 'git branch -D $branch_name'." - exit 1 - ;; - esac - ;; - esac - git update-ref -d "refs/heads/$branch_name" "$branch" - echo "Deleted branch $branch_name." - done - exit 0 -} - -ls_remote_branches () { - git-rev-parse --symbolic --all | - sed -ne 's|^refs/\(remotes/\)|\1|p' | - sort -} - -force= -create_log= -while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac -do - case "$1" in - -d | -D) - delete_branch "$@" - exit - ;; - -r) - ls_remote_branches - exit - ;; - -f) - force="$1" - ;; - -l) - create_log="yes" - ;; - --) - shift - break - ;; - -*) - usage - ;; - esac - shift -done - -case "$#" in -0) - git-rev-parse --symbolic --branches | - sort | - while read ref - do - if test "$headref" = "$ref" - then - pfx='*' - else - pfx=' ' - fi - echo "$pfx $ref" - done - exit 0 ;; -1) - head=HEAD ;; -2) - head="$2^0" ;; -esac -branchname="$1" - -rev=$(git-rev-parse --verify "$head") || exit - -git-check-ref-format "heads/$branchname" || - die "we do not like '$branchname' as a branch name." - -prev='' -if git-show-ref --verify --quiet -- "refs/heads/$branchname" -then - if test '' = "$force" - then - die "$branchname already exists." - elif test "$branchname" = "$headref" - 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" "$prev" diff --git a/git.c b/git.c index 9108fec..f197169 100644 --- a/git.c +++ b/git.c @@ -221,6 +221,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "add", cmd_add, RUN_SETUP }, { "apply", cmd_apply }, { "archive", cmd_archive }, + { "branch", cmd_branch }, { "cat-file", cmd_cat_file, RUN_SETUP }, { "checkout-index", cmd_checkout_index, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, -- cgit v0.10.2-6-g49f6 From 694500edbd51baef365c588986abe41f01acf0de Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 23 Oct 2006 21:15:34 -0700 Subject: sha1_name.c: avoid compilation warnings. Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index e517033..5cf5578 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -258,7 +258,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) return 0; /* basic@{time or number} format to query ref-log */ - reflog_len = 0; + reflog_len = at = 0; if (str[len-1] == '}') { for (at = 1; at < len - 1; at++) { if (str[at] == '@' && str[at+1] == '{') { -- cgit v0.10.2-6-g49f6 From 5d9e8ee78b1df7a9c1b2d13c2b58628201f01f72 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 23 Oct 2006 22:48:45 -0700 Subject: t3200: git-branch testsuite update The test expected "git branch --help" to exit successfully, but built-ins spawn "man" when given --help, and when the test is run, manpages may not be installed yet and "man" can legally exit non-zero in such a case. Also the new implementation logs "Created from master", instead of "Created from HEAD" in the reflog, which makes a lot more sense, so adjust the test to match that. Signed-off-by: Junio C Hamano diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 6907cbc..acb54b6 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -17,13 +17,10 @@ test_expect_success \ git-commit -m "Initial commit." && HEAD=$(git-rev-parse --verify HEAD)' -test_expect_success \ - 'git branch --help should return success now.' \ - 'git-branch --help' - test_expect_failure \ 'git branch --help should not have created a bogus branch' \ - 'test -f .git/refs/heads/--help' + 'git-branch --help /dev/null 2>/dev/null || : + test -f .git/refs/heads/--help' test_expect_success \ 'git branch abc should create a branch' \ @@ -34,7 +31,7 @@ test_expect_success \ 'git-branch a/b/c && test -f .git/refs/heads/a/b/c' cat >expect < 1117150200 +0000 branch: Created from HEAD +0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master EOF test_expect_success \ 'git branch -l d/e/f should create a branch and a log' \ -- cgit v0.10.2-6-g49f6 From 97f7a7bd0d9d618e503623ded5b22eafada9e174 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Thu, 26 Oct 2006 21:39:05 +0200 Subject: Fix show-ref usagestring This describes the abbreviation possibilities for git-show-ref Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano diff --git a/builtin-show-ref.c b/builtin-show-ref.c index f2912e8..06ec400 100644 --- a/builtin-show-ref.c +++ b/builtin-show-ref.c @@ -3,7 +3,7 @@ #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 const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=]] [--abbrev[=]] [--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, abbrev = 0; -- cgit v0.10.2-6-g49f6