diff options
Diffstat (limited to 'refs.c')
-rw-r--r-- | refs.c | 647 |
1 files changed, 464 insertions, 183 deletions
@@ -10,8 +10,7 @@ struct ref_lock { char *ref_name; char *orig_ref_name; struct lock_file *lk; - unsigned char old_sha1[20]; - int lock_fd; + struct object_id old_oid; }; /* @@ -20,12 +19,14 @@ struct ref_lock { * 1: End-of-component * 2: ., look for a preceding . to reject .. in refs * 3: {, look for a preceding @ to reject @{ in refs - * 4: A bad character: ASCII control characters, "~", "^", ":" or SP + * 4: A bad character: ASCII control characters, and + * ":", "?", "[", "\", "^", "~", SP, or TAB + * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set */ static unsigned char refname_disposition[256] = { 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0, @@ -64,6 +65,11 @@ static unsigned char refname_disposition[256] = { #define REF_NEEDS_COMMIT 0x20 /* + * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a + * value to ref_update::flags + */ + +/* * Try to read one refname component from the front of refname. * Return the length of the component found, or -1 if the component is * not legal. It is legal if it is something reasonable to have under @@ -71,12 +77,14 @@ static unsigned char refname_disposition[256] = { * * - any path component of it begins with ".", or * - it has double dots "..", or - * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or - * - it ends with a "/". - * - it ends with ".lock" - * - it contains a "\" (backslash) + * - it has ASCII control characters, or + * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or + * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or + * - it ends with a "/", or + * - it ends with ".lock", or + * - it contains a "@{" portion */ -static int check_refname_component(const char *refname, int flags) +static int check_refname_component(const char *refname, int *flags) { const char *cp; char last = '\0'; @@ -97,6 +105,16 @@ static int check_refname_component(const char *refname, int flags) break; case 4: return -1; + case 5: + if (!(*flags & REFNAME_REFSPEC_PATTERN)) + return -1; /* refspec can't be a pattern */ + + /* + * Unset the pattern flag so that we only accept + * a single asterisk for one side of refspec. + */ + *flags &= ~ REFNAME_REFSPEC_PATTERN; + break; } last = ch; } @@ -121,18 +139,10 @@ int check_refname_format(const char *refname, int flags) while (1) { /* We are at the start of a path component. */ - component_len = check_refname_component(refname, flags); - if (component_len <= 0) { - if ((flags & REFNAME_REFSPEC_PATTERN) && - refname[0] == '*' && - (refname[1] == '\0' || refname[1] == '/')) { - /* Accept one wildcard as a full refname component. */ - flags &= ~REFNAME_REFSPEC_PATTERN; - component_len = 1; - } else { - return -1; - } - } + component_len = check_refname_component(refname, &flags); + if (component_len <= 0) + return -1; + component_count++; if (refname[component_len] == '\0') break; @@ -162,7 +172,7 @@ struct ref_value { * null. If REF_ISSYMREF, then this is the name of the object * referred to by the last reference in the symlink chain. */ - unsigned char sha1[20]; + struct object_id oid; /* * If REF_KNOWS_PEELED, then this field holds the peeled value @@ -170,7 +180,7 @@ struct ref_value { * be peelable. See the documentation for peel_ref() for an * exact definition of "peelable". */ - unsigned char peeled[20]; + struct object_id peeled; }; struct ref_cache; @@ -350,12 +360,10 @@ static struct ref_entry *create_ref_entry(const char *refname, if (check_name && check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) die("Reference has invalid format: '%s'", refname); - if (!check_name && !refname_is_safe(refname)) - die("Reference has invalid name: '%s'", refname); len = strlen(refname) + 1; ref = xmalloc(sizeof(struct ref_entry) + len); - hashcpy(ref->u.value.sha1, sha1); - hashclr(ref->u.value.peeled); + hashcpy(ref->u.value.oid.hash, sha1); + oidclr(&ref->u.value.peeled); memcpy(ref->name, refname, len); ref->flag = flag; return ref; @@ -629,7 +637,7 @@ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2 /* This is impossible by construction */ die("Reference directory conflict: %s", ref1->name); - if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1)) + if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid)) die("Duplicated ref, and SHA1s don't match: %s", ref1->name); warning("Duplicated ref: %s", ref1->name); @@ -677,7 +685,7 @@ static int ref_resolves_to_object(struct ref_entry *entry) { if (entry->flag & REF_ISBROKEN) return 0; - if (!has_sha1_file(entry->u.value.sha1)) { + if (!has_sha1_file(entry->u.value.oid.hash)) { error("%s does not point to a valid object!", entry->name); return 0; } @@ -725,7 +733,7 @@ static int do_one_ref(struct ref_entry *entry, void *cb_data) /* Store the old value, in case this is a recursive call: */ old_current_ref = current_ref; current_ref = entry; - retval = data->fn(entry->name + data->trim, entry->u.value.sha1, + retval = data->fn(entry->name + data->trim, &entry->u.value.oid, entry->flag, data->cb_data); current_ref = old_current_ref; return retval; @@ -1244,6 +1252,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) int flag = REF_ISPACKED; if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(refname)) + die("packed refname is dangerous: %s", refname); hashclr(sha1); flag |= REF_BAD_NAME | REF_ISBROKEN; } @@ -1259,7 +1269,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) line.len == PEELED_LINE_LENGTH && line.buf[PEELED_LINE_LENGTH - 1] == '\n' && !get_sha1_hex(line.buf + 1, sha1)) { - hashcpy(last->u.value.peeled, sha1); + hashcpy(last->u.value.peeled.hash, sha1); /* * Regardless of what the file header said, * we definitely know the value of *this* @@ -1315,7 +1325,13 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs) return get_packed_ref_dir(get_packed_ref_cache(refs)); } -void add_packed_ref(const char *refname, const unsigned char *sha1) +/* + * Add a reference to the in-memory packed reference cache. This may + * only be called while the packed-refs file is locked (see + * lock_packed_refs()). To actually write the packed-refs file, call + * commit_packed_refs(). + */ +static void add_packed_ref(const char *refname, const unsigned char *sha1) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(&ref_cache); @@ -1374,21 +1390,38 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir) create_dir_entry(refs, refname.buf, refname.len, 1)); } else { + int read_ok; + if (*refs->name) { hashclr(sha1); flag = 0; - if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) { - hashclr(sha1); - flag |= REF_ISBROKEN; - } - } else if (read_ref_full(refname.buf, - RESOLVE_REF_READING, - sha1, &flag)) { + read_ok = !resolve_gitlink_ref(refs->name, + refname.buf, sha1); + } else { + read_ok = !read_ref_full(refname.buf, + RESOLVE_REF_READING, + sha1, &flag); + } + + if (!read_ok) { hashclr(sha1); flag |= REF_ISBROKEN; + } else if (is_null_sha1(sha1)) { + /* + * It is so astronomically unlikely + * that NULL_SHA1 is the SHA-1 of an + * actual object that we consider its + * appearance in a loose reference + * file to be repo corruption + * (probably due to a software bug). + */ + flag |= REF_ISBROKEN; } + if (check_refname_format(refname.buf, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(refname.buf)) + die("loose refname is dangerous: %s", refname.buf); hashclr(sha1); flag |= REF_BAD_NAME | REF_ISBROKEN; } @@ -1438,7 +1471,7 @@ static int resolve_gitlink_packed_ref(struct ref_cache *refs, if (ref == NULL) return -1; - hashcpy(sha1, ref->u.value.sha1); + hashcpy(sha1, ref->u.value.oid.hash); return 0; } @@ -1448,7 +1481,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs, { int fd, len; char buffer[128], *p; - char *path; + const char *path; if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN) return -1; @@ -1525,7 +1558,7 @@ static int resolve_missing_loose_ref(const char *refname, */ entry = get_packed_ref(refname); if (entry) { - hashcpy(sha1, entry->u.value.sha1); + hashcpy(sha1, entry->u.value.oid.hash); if (flags) *flags |= REF_ISPACKED; return 0; @@ -1541,7 +1574,11 @@ static int resolve_missing_loose_ref(const char *refname, } /* This function needs to return a meaningful errno on failure */ -const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags) +static const char *resolve_ref_unsafe_1(const char *refname, + int resolve_flags, + unsigned char *sha1, + int *flags, + struct strbuf *sb_path) { int depth = MAXDEPTH; ssize_t len; @@ -1572,7 +1609,7 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned bad_name = 1; } for (;;) { - char path[PATH_MAX]; + const char *path; struct stat st; char *buf; int fd; @@ -1582,7 +1619,9 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned return NULL; } - git_snpath(path, sizeof(path), "%s", refname); + strbuf_reset(sb_path); + strbuf_git_path(sb_path, "%s", refname); + path = sb_path->buf; /* * We might have to loop back here to avoid a race @@ -1709,9 +1748,21 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned } } -char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags) +const char *resolve_ref_unsafe(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags) +{ + struct strbuf sb_path = STRBUF_INIT; + const char *ret = resolve_ref_unsafe_1(refname, resolve_flags, + sha1, flags, &sb_path); + strbuf_release(&sb_path); + return ret; +} + +char *resolve_refdup(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags) { - return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags)); + return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags, + sha1, flags)); } /* The argument to filter_refs */ @@ -1739,13 +1790,14 @@ int ref_exists(const char *refname) return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL); } -static int filter_refs(const char *refname, const unsigned char *sha1, int flags, - void *data) +static int filter_refs(const char *refname, const struct object_id *oid, + int flags, void *data) { struct ref_filter *filter = (struct ref_filter *)data; + if (wildmatch(filter->pattern, refname, 0, NULL)) return 0; - return filter->fn(refname, sha1, flags, filter->cb_data); + return filter->fn(refname, oid, flags, filter->cb_data); } enum peel_status { @@ -1819,9 +1871,9 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel) if (entry->flag & REF_KNOWS_PEELED) { if (repeel) { entry->flag &= ~REF_KNOWS_PEELED; - hashclr(entry->u.value.peeled); + oidclr(&entry->u.value.peeled); } else { - return is_null_sha1(entry->u.value.peeled) ? + return is_null_oid(&entry->u.value.peeled) ? PEEL_NON_TAG : PEEL_PEELED; } } @@ -1830,7 +1882,7 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel) if (entry->flag & REF_ISSYMREF) return PEEL_IS_SYMREF; - status = peel_object(entry->u.value.sha1, entry->u.value.peeled); + status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash); if (status == PEEL_PEELED || status == PEEL_NON_TAG) entry->flag |= REF_KNOWS_PEELED; return status; @@ -1845,7 +1897,7 @@ int peel_ref(const char *refname, unsigned char *sha1) || !strcmp(current_ref->name, refname))) { if (peel_entry(current_ref, 0)) return -1; - hashcpy(sha1, current_ref->u.value.peeled); + hashcpy(sha1, current_ref->u.value.peeled.hash); return 0; } @@ -1865,7 +1917,7 @@ int peel_ref(const char *refname, unsigned char *sha1) if (r) { if (peel_entry(r, 0)) return -1; - hashcpy(sha1, r->u.value.peeled); + hashcpy(sha1, r->u.value.peeled.hash); return 0; } } @@ -1880,17 +1932,17 @@ struct warn_if_dangling_data { const char *msg_fmt; }; -static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1, +static int warn_if_dangling_symref(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct warn_if_dangling_data *d = cb_data; const char *resolves_to; - unsigned char junk[20]; + struct object_id junk; if (!(flags & REF_ISSYMREF)) return 0; - resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL); + resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL); if (!resolves_to || (d->refname ? strcmp(resolves_to, d->refname) @@ -2010,18 +2062,18 @@ static int do_for_each_ref(struct ref_cache *refs, const char *base, static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data) { - unsigned char sha1[20]; + struct object_id oid; int flag; if (submodule) { - if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0) - return fn("HEAD", sha1, 0, cb_data); + if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0) + return fn("HEAD", &oid, 0, cb_data); return 0; } - if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag)) - return fn("HEAD", sha1, flag, cb_data); + if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag)) + return fn("HEAD", &oid, flag, cb_data); return 0; } @@ -2089,19 +2141,20 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c int for_each_replace_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data); + return do_for_each_ref(&ref_cache, git_replace_ref_base, fn, + strlen(git_replace_ref_base), 0, cb_data); } int head_ref_namespaced(each_ref_fn fn, void *cb_data) { struct strbuf buf = STRBUF_INIT; int ret = 0; - unsigned char sha1[20]; + struct object_id oid; int flag; strbuf_addf(&buf, "%sHEAD", get_git_namespace()); - if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag)) - ret = fn(buf.buf, sha1, flag, cb_data); + if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag)) + ret = fn(buf.buf, &oid, flag, cb_data); strbuf_release(&buf); return ret; @@ -2201,27 +2254,35 @@ static void unlock_ref(struct ref_lock *lock) free(lock); } -/* This function should make sure errno is meaningful on error */ -static struct ref_lock *verify_lock(struct ref_lock *lock, - const unsigned char *old_sha1, int mustexist) +/* + * Verify that the reference locked by lock has the value old_sha1. + * Fail if the reference doesn't exist and mustexist is set. Return 0 + * on success. On error, write an error message to err, set errno, and + * return a negative value. + */ +static int verify_lock(struct ref_lock *lock, + const unsigned char *old_sha1, int mustexist, + struct strbuf *err) { + assert(err); + if (read_ref_full(lock->ref_name, mustexist ? RESOLVE_REF_READING : 0, - lock->old_sha1, NULL)) { + lock->old_oid.hash, NULL)) { int save_errno = errno; - error("Can't verify ref %s", lock->ref_name); - unlock_ref(lock); + strbuf_addf(err, "can't verify ref %s", lock->ref_name); errno = save_errno; - return NULL; + return -1; } - if (hashcmp(lock->old_sha1, old_sha1)) { - 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); + if (hashcmp(lock->old_oid.hash, old_sha1)) { + strbuf_addf(err, "ref %s is at %s but expected %s", + lock->ref_name, + sha1_to_hex(lock->old_oid.hash), + sha1_to_hex(old_sha1)); errno = EBUSY; - return NULL; + return -1; } - return lock; + return 0; } static int remove_empty_directories(const char *file) @@ -2342,7 +2403,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, unsigned int flags, int *type_p, struct strbuf *err) { - char *ref_file; + const char *ref_file; const char *orig_refname = refname; struct ref_lock *lock; int last_errno = 0; @@ -2354,7 +2415,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, assert(err); lock = xcalloc(1, sizeof(struct ref_lock)); - lock->lock_fd = -1; if (mustexist) resolve_flags |= RESOLVE_REF_READING; @@ -2365,7 +2425,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, } refname = resolve_ref_unsafe(refname, resolve_flags, - lock->old_sha1, &type); + lock->old_oid.hash, &type); if (!refname && errno == EISDIR) { /* we are trying to lock foo but we used to * have foo/bar which now does not exist; @@ -2384,7 +2444,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, goto error_return; } refname = resolve_ref_unsafe(orig_refname, resolve_flags, - lock->old_sha1, &type); + lock->old_oid.hash, &type); } if (type_p) *type_p = type; @@ -2404,7 +2464,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, * refname, nor a packed ref whose name is a proper prefix of * our refname. */ - if (is_null_sha1(lock->old_sha1) && + if (is_null_oid(&lock->old_oid) && verify_refname_available(refname, extras, skip, get_packed_refs(&ref_cache), err)) { last_errno = ENOTDIR; @@ -2423,7 +2483,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, ref_file = git_path("%s", refname); retry: - switch (safe_create_leading_directories(ref_file)) { + switch (safe_create_leading_directories_const(ref_file)) { case SCLD_OK: break; /* success */ case SCLD_VANISHED: @@ -2436,8 +2496,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, goto error_return; } - lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags); - if (lock->lock_fd < 0) { + if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) { last_errno = errno; if (errno == ENOENT && --attempts_remaining > 0) /* @@ -2451,7 +2510,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, goto error_return; } } - return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; + if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) { + last_errno = errno; + goto error_return; + } + return lock; error_return: unlock_ref(lock); @@ -2481,18 +2544,32 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data) if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) error("internal error: %s is not a valid packed reference!", entry->name); - write_packed_entry(cb_data, entry->name, entry->u.value.sha1, + write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash, peel_status == PEEL_PEELED ? - entry->u.value.peeled : NULL); + entry->u.value.peeled.hash : NULL); return 0; } -/* This should return a meaningful errno on failure */ -int lock_packed_refs(int flags) +/* + * Lock the packed-refs file for writing. Flags is passed to + * hold_lock_file_for_update(). Return 0 on success. On errors, set + * errno appropriately and return a nonzero value. + */ +static int lock_packed_refs(int flags) { + static int timeout_configured = 0; + static int timeout_value = 1000; + struct packed_ref_cache *packed_ref_cache; - if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0) + if (!timeout_configured) { + git_config_get_int("core.packedrefstimeout", &timeout_value); + timeout_configured = 1; + } + + if (hold_lock_file_for_update_timeout( + &packlock, git_path("packed-refs"), + flags, timeout_value) < 0) return -1; /* * Get the current packed-refs while holding the lock. If the @@ -2508,10 +2585,12 @@ int lock_packed_refs(int flags) } /* - * Commit the packed refs changes. - * On error we must make sure that errno contains a meaningful value. + * Write the current version of the packed refs cache from memory to + * disk. The packed-refs file must already be locked for writing (see + * lock_packed_refs()). Return zero on success. On errors, set errno + * and return a nonzero value */ -int commit_packed_refs(void) +static int commit_packed_refs(void) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(&ref_cache); @@ -2540,7 +2619,12 @@ int commit_packed_refs(void) return error; } -void rollback_packed_refs(void) +/* + * Rollback the lockfile for the packed-refs file, and discard the + * in-memory packed reference cache. (The packed-refs file will be + * read anew if it is needed again after this function is called.) + */ +static void rollback_packed_refs(void) { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(&ref_cache); @@ -2590,24 +2674,24 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data) peel_status = peel_entry(entry, 1); if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) die("internal error peeling reference %s (%s)", - entry->name, sha1_to_hex(entry->u.value.sha1)); + entry->name, oid_to_hex(&entry->u.value.oid)); packed_entry = find_ref(cb->packed_refs, entry->name); if (packed_entry) { /* Overwrite existing packed entry with info from loose entry */ packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED; - hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1); + oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid); } else { - packed_entry = create_ref_entry(entry->name, entry->u.value.sha1, + packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash, REF_ISPACKED | REF_KNOWS_PEELED, 0); add_ref(cb->packed_refs, packed_entry); } - hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled); + oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled); /* Schedule the loose reference for pruning if requested. */ if ((cb->flags & PACK_REFS_PRUNE)) { int namelen = strlen(entry->name) + 1; struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen); - hashcpy(n->sha1, entry->u.value.sha1); + hashcpy(n->sha1, entry->u.value.oid.hash); strcpy(n->name, entry->name); n->next = cb->ref_to_prune; cb->ref_to_prune = n; @@ -2698,7 +2782,14 @@ int pack_refs(unsigned int flags) return 0; } -int repack_without_refs(struct string_list *refnames, struct strbuf *err) +/* + * Rewrite the packed-refs file, omitting any refs listed in + * 'refnames'. On error, leave packed-refs unchanged, write an error + * message to 'err', and return a nonzero value. + * + * The refs in 'refnames' needn't be sorted. `err` must not be NULL. + */ +static int repack_without_refs(struct string_list *refnames, struct strbuf *err) { struct ref_dir *packed; struct string_list_item *refname; @@ -2763,15 +2854,15 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err) return 0; } -int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags) +int delete_ref(const char *refname, const unsigned char *old_sha1, + unsigned int flags) { struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; transaction = ref_transaction_begin(&err); if (!transaction || - ref_transaction_delete(transaction, refname, - (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL, + ref_transaction_delete(transaction, refname, old_sha1, flags, NULL, &err) || ref_transaction_commit(transaction, &err)) { error("%s", err.buf); @@ -2784,6 +2875,44 @@ int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flag return 0; } +int delete_refs(struct string_list *refnames) +{ + struct strbuf err = STRBUF_INIT; + int i, result = 0; + + if (!refnames->nr) + return 0; + + result = repack_without_refs(refnames, &err); + if (result) { + /* + * If we failed to rewrite the packed-refs file, then + * it is unsafe to try to remove loose refs, because + * doing so might expose an obsolete packed value for + * a reference that might even point at an object that + * has been garbage collected. + */ + if (refnames->nr == 1) + error(_("could not delete reference %s: %s"), + refnames->items[0].string, err.buf); + else + error(_("could not delete references: %s"), err.buf); + + goto out; + } + + for (i = 0; i < refnames->nr; i++) { + const char *refname = refnames->items[i].string; + + if (delete_ref(refname, NULL, 0)) + result |= error(_("could not remove reference %s"), refname); + } + +out: + strbuf_release(&err); + return result; +} + /* * People using contrib's git-new-workdir have .git/logs/refs -> * /some/other/path/.git/logs/refs, and that may live on another device. @@ -2798,7 +2927,7 @@ static int rename_tmp_log(const char *newrefname) int attempts_remaining = 4; retry: - switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) { + switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) { case SCLD_OK: break; /* success */ case SCLD_VANISHED: @@ -2857,9 +2986,11 @@ static int rename_ref_available(const char *oldname, const char *newname) return ret; } -static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1); +static int write_ref_to_lockfile(struct ref_lock *lock, + const unsigned char *sha1, struct strbuf *err); static int commit_ref_update(struct ref_lock *lock, - const unsigned char *sha1, const char *logmsg); + const unsigned char *sha1, const char *logmsg, + int flags, struct strbuf *err); int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg) { @@ -2918,11 +3049,12 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms strbuf_release(&err); goto rollback; } - hashcpy(lock->old_sha1, orig_sha1); + hashcpy(lock->old_oid.hash, orig_sha1); - if (write_ref_to_lockfile(lock, orig_sha1) || - commit_ref_update(lock, orig_sha1, logmsg)) { - error("unable to write current sha1 into %s", newrefname); + if (write_ref_to_lockfile(lock, orig_sha1, &err) || + commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) { + error("unable to write current sha1 into %s: %s", newrefname, err.buf); + strbuf_release(&err); goto rollback; } @@ -2938,9 +3070,11 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms flag = log_all_ref_updates; log_all_ref_updates = 0; - if (write_ref_to_lockfile(lock, orig_sha1) || - commit_ref_update(lock, orig_sha1, NULL)) - error("unable to write current sha1 into %s", oldrefname); + if (write_ref_to_lockfile(lock, orig_sha1, &err) || + commit_ref_update(lock, orig_sha1, NULL, 0, &err)) { + error("unable to write current sha1 into %s: %s", oldrefname, err.buf); + strbuf_release(&err); + } log_all_ref_updates = flag; rollbacklog: @@ -2959,7 +3093,6 @@ static int close_ref(struct ref_lock *lock) { if (close_lock_file(lock->lk)) return -1; - lock->lock_fd = -1; return 0; } @@ -2967,7 +3100,6 @@ static int commit_ref(struct ref_lock *lock) { if (commit_lock_file(lock->lk)) return -1; - lock->lock_fd = -1; return 0; } @@ -2997,21 +3129,35 @@ static int copy_msg(char *buf, const char *msg) return cp - buf; } -/* This function must set a meaningful errno on failure */ -int log_ref_setup(const char *refname, char *logfile, int bufsize) +static int should_autocreate_reflog(const char *refname) +{ + if (!log_all_ref_updates) + return 0; + return starts_with(refname, "refs/heads/") || + starts_with(refname, "refs/remotes/") || + starts_with(refname, "refs/notes/") || + !strcmp(refname, "HEAD"); +} + +/* + * Create a reflog for a ref. If force_create = 0, the reflog will + * only be created for certain refs (those for which + * should_autocreate_reflog returns non-zero. Otherwise, create it + * regardless of the ref name. Fill in *err and return -1 on failure. + */ +static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create) { int logfd, oflags = O_APPEND | O_WRONLY; + char *logfile; - git_snpath(logfile, bufsize, "logs/%s", refname); - if (log_all_ref_updates && - (starts_with(refname, "refs/heads/") || - starts_with(refname, "refs/remotes/") || - starts_with(refname, "refs/notes/") || - !strcmp(refname, "HEAD"))) { + strbuf_git_path(sb_logfile, "logs/%s", refname); + logfile = sb_logfile->buf; + /* make sure the rest of the function can't change "logfile" */ + sb_logfile = NULL; + if (force_create || should_autocreate_reflog(refname)) { if (safe_create_leading_directories(logfile) < 0) { - int save_errno = errno; - error("unable to create directory for %s", logfile); - errno = save_errno; + strbuf_addf(err, "unable to create directory for %s: " + "%s", logfile, strerror(errno)); return -1; } oflags |= O_CREAT; @@ -3024,20 +3170,16 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize) if (errno == EISDIR) { if (remove_empty_directories(logfile)) { - int save_errno = errno; - error("There are still logs under '%s'", - logfile); - errno = save_errno; + strbuf_addf(err, "There are still logs under " + "'%s'", logfile); return -1; } logfd = open(logfile, oflags, 0666); } if (logfd < 0) { - int save_errno = errno; - error("Unable to append to %s: %s", logfile, - strerror(errno)); - errno = save_errno; + strbuf_addf(err, "unable to append to %s: %s", + logfile, strerror(errno)); return -1; } } @@ -3047,6 +3189,17 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize) return 0; } + +int safe_create_reflog(const char *refname, int force_create, struct strbuf *err) +{ + int ret; + struct strbuf sb = STRBUF_INIT; + + ret = log_ref_setup(refname, &sb, err, force_create); + strbuf_release(&sb); + return ret; +} + static int log_ref_write_fd(int fd, const unsigned char *old_sha1, const unsigned char *new_sha1, const char *committer, const char *msg) @@ -3073,18 +3226,24 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1, return 0; } -static int log_ref_write(const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg) +static int log_ref_write_1(const char *refname, const unsigned char *old_sha1, + const unsigned char *new_sha1, const char *msg, + struct strbuf *sb_log_file, int flags, + struct strbuf *err) { int logfd, result, oflags = O_APPEND | O_WRONLY; - char log_file[PATH_MAX]; + char *log_file; if (log_all_ref_updates < 0) log_all_ref_updates = !is_bare_repository(); - result = log_ref_setup(refname, log_file, sizeof(log_file)); + result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG); + if (result) return result; + log_file = sb_log_file->buf; + /* make sure the rest of the function can't change "log_file" */ + sb_log_file = NULL; logfd = open(log_file, oflags); if (logfd < 0) @@ -3092,21 +3251,30 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1, result = log_ref_write_fd(logfd, old_sha1, new_sha1, git_committer_info(0), msg); if (result) { - int save_errno = errno; + strbuf_addf(err, "unable to append to %s: %s", log_file, + strerror(errno)); close(logfd); - error("Unable to append to %s", log_file); - errno = save_errno; return -1; } if (close(logfd)) { - int save_errno = errno; - error("Unable to append to %s", log_file); - errno = save_errno; + strbuf_addf(err, "unable to append to %s: %s", log_file, + strerror(errno)); return -1; } return 0; } +static int log_ref_write(const char *refname, const unsigned char *old_sha1, + const unsigned char *new_sha1, const char *msg, + int flags, struct strbuf *err) +{ + struct strbuf sb = STRBUF_INIT; + int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags, + err); + strbuf_release(&sb); + return ret; +} + int is_branch(const char *refname) { return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/"); @@ -3114,36 +3282,36 @@ int is_branch(const char *refname) /* * Write sha1 into the open lockfile, then close the lockfile. On - * errors, rollback the lockfile and set errno to reflect the problem. + * errors, rollback the lockfile, fill in *err and + * return -1. */ static int write_ref_to_lockfile(struct ref_lock *lock, - const unsigned char *sha1) + const unsigned char *sha1, struct strbuf *err) { static char term = '\n'; struct object *o; o = parse_object(sha1); if (!o) { - error("Trying to write ref %s with nonexistent object %s", - lock->ref_name, sha1_to_hex(sha1)); + strbuf_addf(err, + "Trying to write ref %s with nonexistent object %s", + lock->ref_name, sha1_to_hex(sha1)); unlock_ref(lock); - errno = EINVAL; return -1; } if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { - error("Trying to write non-commit object %s to branch %s", - sha1_to_hex(sha1), lock->ref_name); + strbuf_addf(err, + "Trying to write non-commit object %s to branch %s", + sha1_to_hex(sha1), lock->ref_name); unlock_ref(lock); - errno = EINVAL; return -1; } - if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 || - write_in_full(lock->lock_fd, &term, 1) != 1 || + if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 || + write_in_full(lock->lk->fd, &term, 1) != 1 || close_ref(lock) < 0) { - int save_errno = errno; - error("Couldn't write %s", lock->lk->filename.buf); + strbuf_addf(err, + "Couldn't write %s", lock->lk->filename.buf); unlock_ref(lock); - errno = save_errno; return -1; } return 0; @@ -3155,12 +3323,17 @@ static int write_ref_to_lockfile(struct ref_lock *lock, * necessary, using the specified lockmsg (which can be NULL). */ static int commit_ref_update(struct ref_lock *lock, - const unsigned char *sha1, const char *logmsg) + const unsigned char *sha1, const char *logmsg, + int flags, struct strbuf *err) { clear_loose_ref_cache(&ref_cache); - if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 || + if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 || (strcmp(lock->ref_name, lock->orig_ref_name) && - log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) { + log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) { + char *old_msg = strbuf_detach(err, NULL); + strbuf_addf(err, "Cannot update the ref '%s': %s", + lock->ref_name, old_msg); + free(old_msg); unlock_ref(lock); return -1; } @@ -3183,14 +3356,21 @@ static int commit_ref_update(struct ref_lock *lock, head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, &head_flag); if (head_ref && (head_flag & REF_ISSYMREF) && - !strcmp(head_ref, lock->ref_name)) - log_ref_write("HEAD", lock->old_sha1, sha1, logmsg); + !strcmp(head_ref, lock->ref_name)) { + struct strbuf log_err = STRBUF_INIT; + if (log_ref_write("HEAD", lock->old_oid.hash, sha1, + logmsg, 0, &log_err)) { + error("%s", log_err.buf); + strbuf_release(&log_err); + } + } } if (commit_ref(lock)) { error("Couldn't set %s", lock->ref_name); unlock_ref(lock); return -1; } + unlock_ref(lock); return 0; } @@ -3203,6 +3383,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master, int fd, len, written; char *git_HEAD = git_pathdup("%s", ref_target); unsigned char old_sha1[20], new_sha1[20]; + struct strbuf err = STRBUF_INIT; if (logmsg && read_ref(ref_target, old_sha1)) hashclr(old_sha1); @@ -3251,8 +3432,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master, #ifndef NO_SYMLINK_HEAD done: #endif - if (logmsg && !read_ref(refs_heads_master, new_sha1)) - log_ref_write(ref_target, old_sha1, new_sha1, logmsg); + if (logmsg && !read_ref(refs_heads_master, new_sha1) && + log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) { + error("%s", err.buf); + strbuf_release(&err); + } free(git_HEAD); return 0; @@ -3303,14 +3487,14 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1, hashcpy(cb->sha1, nsha1); if (hashcmp(cb->osha1, nsha1)) warning("Log for ref %s has gap after %s.", - cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822)); + cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); } else if (cb->date == cb->at_time) hashcpy(cb->sha1, nsha1); else if (hashcmp(nsha1, cb->sha1)) warning("Log for ref %s unexpectedly ended on %s.", cb->refname, show_date(cb->date, cb->tz, - DATE_RFC2822)); + DATE_MODE(RFC2822))); hashcpy(cb->osha1, osha1); hashcpy(cb->nsha1, nsha1); cb->found_it = 1; @@ -3576,11 +3760,12 @@ static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data strbuf_addch(name, '/'); retval = do_for_each_reflog(name, fn, cb_data); } else { - unsigned char sha1[20]; - if (read_ref_full(name->buf, 0, sha1, NULL)) + struct object_id oid; + + if (read_ref_full(name->buf, 0, oid.hash, NULL)) retval = error("bad ref for %s", name->buf); else - retval = fn(name->buf, sha1, 0, cb_data); + retval = fn(name->buf, &oid, 0, cb_data); } if (retval) break; @@ -3870,7 +4055,7 @@ int ref_transaction_commit(struct ref_transaction *transaction, ? TRANSACTION_NAME_CONFLICT : TRANSACTION_GENERIC_ERROR; reason = strbuf_detach(err, NULL); - strbuf_addf(err, "Cannot lock ref '%s': %s", + strbuf_addf(err, "cannot lock ref '%s': %s", update->refname, reason); free(reason); goto cleanup; @@ -3881,20 +4066,25 @@ int ref_transaction_commit(struct ref_transaction *transaction, (update->flags & REF_NODEREF)); if (!overwriting_symref && - !hashcmp(update->lock->old_sha1, update->new_sha1)) { + !hashcmp(update->lock->old_oid.hash, update->new_sha1)) { /* * The reference already has the desired * value, so we don't need to write it. */ } else if (write_ref_to_lockfile(update->lock, - update->new_sha1)) { + update->new_sha1, + err)) { + char *write_err = strbuf_detach(err, NULL); + /* * The lock was freed upon failure of * write_ref_to_lockfile(): */ update->lock = NULL; - strbuf_addf(err, "Cannot update the ref '%s'.", - update->refname); + strbuf_addf(err, + "cannot update the ref '%s': %s", + update->refname, write_err); + free(write_err); ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } else { @@ -3920,11 +4110,10 @@ int ref_transaction_commit(struct ref_transaction *transaction, if (update->flags & REF_NEEDS_COMMIT) { if (commit_ref_update(update->lock, - update->new_sha1, update->msg)) { + update->new_sha1, update->msg, + update->flags, err)) { /* freed by commit_ref_update(): */ update->lock = NULL; - strbuf_addf(err, "Cannot update the ref '%s'.", - update->refname); ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } else { @@ -3969,6 +4158,98 @@ cleanup: return ret; } +static int ref_present(const char *refname, + const struct object_id *oid, int flags, void *cb_data) +{ + struct string_list *affected_refnames = cb_data; + + return string_list_has_string(affected_refnames, refname); +} + +int initial_ref_transaction_commit(struct ref_transaction *transaction, + struct strbuf *err) +{ + struct ref_dir *loose_refs = get_loose_refs(&ref_cache); + struct ref_dir *packed_refs = get_packed_refs(&ref_cache); + int ret = 0, i; + int n = transaction->nr; + struct ref_update **updates = transaction->updates; + struct string_list affected_refnames = STRING_LIST_INIT_NODUP; + + assert(err); + + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: commit called for transaction that is not open"); + + /* Fail if a refname appears more than once in the transaction: */ + for (i = 0; i < n; i++) + string_list_append(&affected_refnames, updates[i]->refname); + string_list_sort(&affected_refnames); + if (ref_update_reject_duplicates(&affected_refnames, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + + /* + * It's really undefined to call this function in an active + * repository or when there are existing references: we are + * only locking and changing packed-refs, so (1) any + * simultaneous processes might try to change a reference at + * the same time we do, and (2) any existing loose versions of + * the references that we are setting would have precedence + * over our values. But some remote helpers create the remote + * "HEAD" and "master" branches before calling this function, + * so here we really only check that none of the references + * that we are creating already exists. + */ + if (for_each_rawref(ref_present, &affected_refnames)) + die("BUG: initial ref transaction called with existing refs"); + + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if ((update->flags & REF_HAVE_OLD) && + !is_null_sha1(update->old_sha1)) + die("BUG: initial ref transaction with old_sha1 set"); + if (verify_refname_available(update->refname, + &affected_refnames, NULL, + loose_refs, err) || + verify_refname_available(update->refname, + &affected_refnames, NULL, + packed_refs, err)) { + ret = TRANSACTION_NAME_CONFLICT; + goto cleanup; + } + } + + if (lock_packed_refs(0)) { + strbuf_addf(err, "unable to lock packed-refs file: %s", + strerror(errno)); + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if ((update->flags & REF_HAVE_NEW) && + !is_null_sha1(update->new_sha1)) + add_packed_ref(update->refname, update->new_sha1); + } + + if (commit_packed_refs()) { + strbuf_addf(err, "unable to commit packed-refs file: %s", + strerror(errno)); + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + +cleanup: + transaction->state = REF_TRANSACTION_CLOSED; + string_list_clear(&affected_refnames, 0); + return ret; +} + char *shorten_unambiguous_ref(const char *refname, int strict) { int i; @@ -4222,9 +4503,9 @@ int reflog_expire(const char *refname, const unsigned char *sha1, status |= error("couldn't write %s: %s", log_file, strerror(errno)); } else if (update && - (write_in_full(lock->lock_fd, + (write_in_full(lock->lk->fd, sha1_to_hex(cb.last_kept_sha1), 40) != 40 || - write_str_in_full(lock->lock_fd, "\n") != 1 || + write_str_in_full(lock->lk->fd, "\n") != 1 || close_ref(lock) < 0)) { status |= error("couldn't write %s", lock->lk->filename.buf); |