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 --- cache.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cache.h') diff --git a/cache.h b/cache.h index 57db7c9b20..282eed6f07 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); -- cgit v1.2.3 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 --- builtin-fmt-merge-msg.c | 2 +- builtin-name-rev.c | 2 +- builtin-pack-refs.c | 3 ++- builtin-prune.c | 2 +- builtin-push.c | 2 +- builtin-rev-parse.c | 2 +- builtin-show-branch.c | 22 +++++++++++----------- builtin-symbolic-ref.c | 6 +++++- cache.h | 2 +- describe.c | 2 +- fetch-pack.c | 4 ++-- fetch.c | 2 +- fsck-objects.c | 7 ++++--- http-push.c | 2 +- receive-pack.c | 4 ++-- refs.c | 44 +++++++++++++++++++++++++++++++------------- refs.h | 4 +++- revision.c | 2 +- send-pack.c | 2 +- server-info.c | 2 +- sha1_name.c | 2 +- upload-pack.c | 2 +- wt-status.c | 2 +- 23 files changed, 75 insertions(+), 49 deletions(-) (limited to 'cache.h') diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index b93c17c2f0..3d3097d299 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 9e3e537f38..618aa314d2 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 b3d5470cc0..98710893b0 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 e21c29baec..e79b515c76 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 88fc8e2a3b..581c44ba8e 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 c7712748bc..3b716fba13 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 b3548ae36f..5d6ce56836 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 13163baa88..d8be0527f4 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 282eed6f07..6def155162 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 ea0f2ce55b..f4029ee74e 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 6264ea1af9..99ac08b2c2 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 36d1e7668e..a2cbdfba82 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 bb0c94e9d3..46b628cb94 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 460c9bec10..ecefdfd4f8 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 7abc9210c5..abbcb6af0b 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 85564f0dc7..40f16af185 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 886c857105..305d408690 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 0e84b8a0fb..cb13b90776 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 ee1309313f..fbd792ccf4 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 7667b41257..6cd38be329 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 b4975289d5..84d24c6abf 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 10237ebe54..9412a9b260 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 050922df54..d8e284c311 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 v1.2.3 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 --- Documentation/git-update-ref.txt | 10 ++++++++-- builtin-update-ref.c | 18 +++++++++++++++--- cache.h | 1 + refs.c | 26 ++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) (limited to 'cache.h') diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index e062030e91..71bcb7954f 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 ab528337aa..b34e5987dd 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 6def155162..6e004505be 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 9a1bc0db59..3d4cdd1eb9 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 v1.2.3