diff options
Diffstat (limited to 'refs.c')
-rw-r--r-- | refs.c | 421 |
1 files changed, 229 insertions, 192 deletions
@@ -12,7 +12,6 @@ struct ref_lock { struct lock_file *lk; unsigned char old_sha1[20]; int lock_fd; - int force_write; }; /* @@ -35,10 +34,29 @@ static unsigned char refname_disposition[256] = { }; /* - * Used as a flag to ref_transaction_delete when a loose ref is being + * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken + * refs (i.e., because the reference is about to be deleted anyway). + */ +#define REF_DELETING 0x02 + +/* + * Used as a flag in ref_update::flags when a loose ref is being * pruned. */ -#define REF_ISPRUNING 0x0100 +#define REF_ISPRUNING 0x04 + +/* + * Used as a flag in ref_update::flags when the reference should be + * updated to new_sha1. + */ +#define REF_HAVE_NEW 0x08 + +/* + * Used as a flag in ref_update::flags when old_sha1 should be + * checked. + */ +#define REF_HAVE_OLD 0x10 + /* * Try to read one refname component from the front of refname. * Return the length of the component found, or -1 if the component is @@ -1077,8 +1095,10 @@ static const char PACKED_REFS_HEADER[] = * Return a pointer to the refname within the line (null-terminated), * or NULL if there was a problem. */ -static const char *parse_ref_line(char *line, unsigned char *sha1) +static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1) { + const char *ref; + /* * 42: the answer to everything. * @@ -1087,22 +1107,23 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) * +1 (space in between hex and name) * +1 (newline at the end of the line) */ - int len = strlen(line) - 42; - - if (len <= 0) + if (line->len <= 42) return NULL; - if (get_sha1_hex(line, sha1) < 0) + + if (get_sha1_hex(line->buf, sha1) < 0) return NULL; - if (!isspace(line[40])) + if (!isspace(line->buf[40])) return NULL; - line += 41; - if (isspace(*line)) + + ref = line->buf + 41; + if (isspace(*ref)) return NULL; - if (line[len] != '\n') + + if (line->buf[line->len - 1] != '\n') return NULL; - line[len] = 0; + line->buf[--line->len] = 0; - return line; + return ref; } /* @@ -1135,16 +1156,15 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) static void read_packed_refs(FILE *f, struct ref_dir *dir) { struct ref_entry *last = NULL; - char refline[PATH_MAX]; + struct strbuf line = STRBUF_INIT; enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE; - while (fgets(refline, sizeof(refline), f)) { + while (strbuf_getwholeline(&line, f, '\n') != EOF) { unsigned char sha1[20]; const char *refname; - static const char header[] = "# pack-refs with:"; + const char *traits; - if (!strncmp(refline, header, sizeof(header)-1)) { - const char *traits = refline + sizeof(header) - 1; + if (skip_prefix(line.buf, "# pack-refs with:", &traits)) { if (strstr(traits, " fully-peeled ")) peeled = PEELED_FULLY; else if (strstr(traits, " peeled ")) @@ -1153,7 +1173,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) continue; } - refname = parse_ref_line(refline, sha1); + refname = parse_ref_line(&line, sha1); if (refname) { int flag = REF_ISPACKED; @@ -1169,10 +1189,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) continue; } if (last && - refline[0] == '^' && - strlen(refline) == PEELED_LINE_LENGTH && - refline[PEELED_LINE_LENGTH - 1] == '\n' && - !get_sha1_hex(refline + 1, sha1)) { + line.buf[0] == '^' && + 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); /* * Regardless of what the file header said, @@ -1182,6 +1202,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir) last->flag |= REF_KNOWS_PEELED; } } + + strbuf_release(&line); } /* @@ -1623,8 +1645,7 @@ 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 *ret = resolve_ref_unsafe(ref, resolve_flags, sha1, flags); - return ret ? xstrdup(ret) : NULL; + return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags)); } /* The argument to filter_refs */ @@ -1913,6 +1934,11 @@ static int do_for_each_ref(struct ref_cache *refs, const char *base, data.fn = fn; data.cb_data = cb_data; + if (ref_paranoia < 0) + ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); + if (ref_paranoia) + data.flags |= DO_FOR_EACH_INCLUDE_BROKEN; + return do_for_each_entry(refs, base, do_one_ref, &data); } @@ -2246,7 +2272,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) static struct ref_lock *lock_ref_sha1_basic(const char *refname, const unsigned char *old_sha1, const struct string_list *skip, - int flags, int *type_p) + unsigned int flags, int *type_p) { char *ref_file; const char *orig_refname = refname; @@ -2255,7 +2281,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, int type, lflags; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); int resolve_flags = 0; - int missing = 0; int attempts_remaining = 3; lock = xcalloc(1, sizeof(struct ref_lock)); @@ -2294,13 +2319,13 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, orig_refname, strerror(errno)); goto error_return; } - missing = is_null_sha1(lock->old_sha1); - /* When 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. + /* + * If the ref did not exist and we are creating it, make sure + * there is no existing packed ref whose name begins with our + * refname, nor a packed ref whose name is a proper prefix of + * our refname. */ - if (missing && + if (is_null_sha1(lock->old_sha1) && !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) { last_errno = ENOTDIR; goto error_return; @@ -2316,10 +2341,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, lock->ref_name = xstrdup(refname); lock->orig_ref_name = xstrdup(orig_refname); ref_file = git_path("%s", refname); - if (missing) - lock->force_write = 1; - if ((flags & REF_NODEREF) && (type & REF_ISSYMREF)) - lock->force_write = 1; retry: switch (safe_create_leading_directories(ref_file)) { @@ -2337,6 +2358,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags); if (lock->lock_fd < 0) { + last_errno = errno; if (errno == ENOENT && --attempts_remaining > 0) /* * Maybe somebody just deleted one of the @@ -2344,8 +2366,13 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, * again: */ goto retry; - else - unable_to_lock_die(ref_file, errno); + else { + struct strbuf err = STRBUF_INIT; + unable_to_lock_message(ref_file, errno, &err); + error("%s", err.buf); + strbuf_release(&err); + goto error_return; + } } return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; @@ -2554,7 +2581,7 @@ static void prune_ref(struct ref_to_prune *r) transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_delete(transaction, r->name, r->sha1, - REF_ISPRUNING, 1, NULL, &err) || + REF_ISPRUNING, NULL, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); error("%s", err.buf); @@ -2594,79 +2621,24 @@ int pack_refs(unsigned int flags) return 0; } -/* - * If entry is no longer needed in packed-refs, add it to the string - * list pointed to by cb_data. Reasons for deleting entries: - * - * - Entry is broken. - * - Entry is overridden by a loose ref. - * - Entry does not point at a valid object. - * - * In the first and third cases, also emit an error message because these - * are indications of repository corruption. - */ -static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data) -{ - struct string_list *refs_to_delete = cb_data; - - if (entry->flag & REF_ISBROKEN) { - /* This shouldn't happen to packed refs. */ - error("%s is broken!", entry->name); - string_list_append(refs_to_delete, entry->name); - return 0; - } - if (!has_sha1_file(entry->u.value.sha1)) { - unsigned char sha1[20]; - int flags; - - if (read_ref_full(entry->name, 0, sha1, &flags)) - /* We should at least have found the packed ref. */ - die("Internal error"); - if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) { - /* - * This packed reference is overridden by a - * loose reference, so it is OK that its value - * is no longer valid; for example, it might - * refer to an object that has been garbage - * collected. For this purpose we don't even - * care whether the loose reference itself is - * invalid, broken, symbolic, etc. Silently - * remove the packed reference. - */ - string_list_append(refs_to_delete, entry->name); - return 0; - } - /* - * There is no overriding loose reference, so the fact - * that this reference doesn't refer to a valid object - * indicates some kind of repository corruption. - * Report the problem, then omit the reference from - * the output. - */ - error("%s does not point to a valid object!", entry->name); - string_list_append(refs_to_delete, entry->name); - return 0; - } - - return 0; -} - -int repack_without_refs(const char **refnames, int n, struct strbuf *err) +int repack_without_refs(struct string_list *refnames, struct strbuf *err) { struct ref_dir *packed; - struct string_list refs_to_delete = STRING_LIST_INIT_DUP; - struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + struct string_list_item *refname; + int ret, needs_repacking = 0, removed = 0; assert(err); /* Look for a packed ref */ - for (i = 0; i < n; i++) - if (get_packed_ref(refnames[i])) + for_each_string_list_item(refname, refnames) { + if (get_packed_ref(refname->string)) { + needs_repacking = 1; break; + } + } /* Avoid locking if we have nothing to do */ - if (i == n) + if (!needs_repacking) return 0; /* no refname exists in packed refs */ if (lock_packed_refs(0)) { @@ -2676,8 +2648,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ - for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) + for_each_string_list_item(refname, refnames) + if (remove_entry(packed, refname->string) != -1) removed = 1; if (!removed) { /* @@ -2688,13 +2660,6 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) return 0; } - /* Remove any other accumulated cruft */ - do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete); - for_each_string_list_item(ref_to_delete, &refs_to_delete) { - if (remove_entry(packed, ref_to_delete->string) == -1) - die("internal error"); - } - /* Write what remains */ ret = commit_packed_refs(); if (ret) @@ -2721,15 +2686,16 @@ 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, int delopt) +int delete_ref(const char *refname, const unsigned char *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, delopt, - sha1 && !is_null_sha1(sha1), NULL, &err) || + ref_transaction_delete(transaction, refname, + (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL, + flags, NULL, &err) || ref_transaction_commit(transaction, &err)) { error("%s", err.buf); ref_transaction_free(transaction); @@ -2865,7 +2831,6 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms error("unable to lock %s for update", newrefname); goto rollback; } - lock->force_write = 1; hashcpy(lock->old_sha1, orig_sha1); if (write_ref_sha1(lock, orig_sha1, logmsg)) { error("unable to write current sha1 into %s", newrefname); @@ -2881,7 +2846,6 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms goto rollbacklog; } - lock->force_write = 1; flag = log_all_ref_updates; log_all_ref_updates = 0; if (write_ref_sha1(lock, orig_sha1, NULL)) @@ -3067,14 +3031,6 @@ static int write_ref_sha1(struct ref_lock *lock, static char term = '\n'; struct object *o; - if (!lock) { - errno = EINVAL; - return -1; - } - if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) { - unlock_ref(lock); - return 0; - } o = parse_object(sha1); if (!o) { error("Trying to write ref %s with nonexistent object %s", @@ -3418,29 +3374,54 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void bp = find_beginning_of_line(buf, scanp); - if (*bp != '\n') { - strbuf_splice(&sb, 0, 0, buf, endp - buf); - if (pos) - break; /* need to fill another block */ - scanp = buf - 1; /* leave loop */ - } else { + if (*bp == '\n') { /* - * (bp + 1) thru endp is the beginning of the - * current line we have in sb + * The newline is the end of the previous line, + * so we know we have complete line starting + * at (bp + 1). Prefix it onto any prior data + * we collected for the line and process it. */ strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1)); scanp = bp; endp = bp + 1; + ret = show_one_reflog_ent(&sb, fn, cb_data); + strbuf_reset(&sb); + if (ret) + break; + } else if (!pos) { + /* + * We are at the start of the buffer, and the + * start of the file; there is no previous + * line, and we have everything for this one. + * Process it, and we can end the loop. + */ + strbuf_splice(&sb, 0, 0, buf, endp - buf); + ret = show_one_reflog_ent(&sb, fn, cb_data); + strbuf_reset(&sb); + break; } - ret = show_one_reflog_ent(&sb, fn, cb_data); - strbuf_reset(&sb); - if (ret) + + if (bp == buf) { + /* + * We are at the start of the buffer, and there + * is more file to read backwards. Which means + * we are in the middle of a line. Note that we + * may get here even if *bp was a newline; that + * just means we are at the exact end of the + * previous line, rather than some spot in the + * middle. + * + * Save away what we have to be combined with + * the data from the next read. + */ + strbuf_splice(&sb, 0, 0, buf, endp - buf); break; + } } } if (!ret && sb.len) - ret = show_one_reflog_ent(&sb, fn, cb_data); + die("BUG: reverse reflog parser had leftover data"); fclose(logfp); strbuf_release(&sb); @@ -3519,16 +3500,27 @@ int for_each_reflog(each_ref_fn fn, void *cb_data) } /** - * Information needed for a single ref update. Set new_sha1 to the - * new value or to zero to delete the ref. To check the old value - * while locking the ref, set have_old to 1 and set old_sha1 to the - * value or to zero to ensure the ref does not exist before update. + * Information needed for a single ref update. Set new_sha1 to the new + * value or to null_sha1 to delete the ref. To check the old value + * while the ref is locked, set (flags & REF_HAVE_OLD) and set + * old_sha1 to the old value, or to null_sha1 to ensure the ref does + * not exist before update. */ struct ref_update { + /* + * If (flags & REF_HAVE_NEW), set the reference to this value: + */ unsigned char new_sha1[20]; + /* + * If (flags & REF_HAVE_OLD), check that the reference + * previously had this value: + */ unsigned char old_sha1[20]; - int flags; /* REF_NODEREF? */ - int have_old; /* 1 if old_sha1 is valid, 0 otherwise */ + /* + * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF, + * REF_DELETING, and REF_ISPRUNING: + */ + unsigned int flags; struct ref_lock *lock; int type; char *msg; @@ -3600,7 +3592,7 @@ int ref_transaction_update(struct ref_transaction *transaction, const char *refname, const unsigned char *new_sha1, const unsigned char *old_sha1, - int flags, int have_old, const char *msg, + unsigned int flags, const char *msg, struct strbuf *err) { struct ref_update *update; @@ -3610,10 +3602,7 @@ int ref_transaction_update(struct ref_transaction *transaction, if (transaction->state != REF_TRANSACTION_OPEN) die("BUG: update called for transaction that is not open"); - if (have_old && !old_sha1) - die("BUG: have_old is true but old_sha1 is NULL"); - - if (!is_null_sha1(new_sha1) && + if (new_sha1 && !is_null_sha1(new_sha1) && check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { strbuf_addf(err, "refusing to update ref with bad name %s", refname); @@ -3621,11 +3610,15 @@ int ref_transaction_update(struct ref_transaction *transaction, } update = add_update(transaction, refname); - hashcpy(update->new_sha1, new_sha1); - update->flags = flags; - update->have_old = have_old; - if (have_old) + if (new_sha1) { + hashcpy(update->new_sha1, new_sha1); + flags |= REF_HAVE_NEW; + } + if (old_sha1) { hashcpy(update->old_sha1, old_sha1); + flags |= REF_HAVE_OLD; + } + update->flags = flags; if (msg) update->msg = xstrdup(msg); return 0; @@ -3634,34 +3627,52 @@ int ref_transaction_update(struct ref_transaction *transaction, int ref_transaction_create(struct ref_transaction *transaction, const char *refname, const unsigned char *new_sha1, - int flags, const char *msg, + unsigned int flags, const char *msg, struct strbuf *err) { + if (!new_sha1 || is_null_sha1(new_sha1)) + die("BUG: create called without valid new_sha1"); return ref_transaction_update(transaction, refname, new_sha1, - null_sha1, flags, 1, msg, err); + null_sha1, flags, msg, err); } int ref_transaction_delete(struct ref_transaction *transaction, const char *refname, const unsigned char *old_sha1, - int flags, int have_old, const char *msg, + unsigned int flags, const char *msg, + struct strbuf *err) +{ + if (old_sha1 && is_null_sha1(old_sha1)) + die("BUG: delete called with old_sha1 set to zeros"); + return ref_transaction_update(transaction, refname, + null_sha1, old_sha1, + flags, msg, err); +} + +int ref_transaction_verify(struct ref_transaction *transaction, + const char *refname, + const unsigned char *old_sha1, + unsigned int flags, struct strbuf *err) { - return ref_transaction_update(transaction, refname, null_sha1, - old_sha1, flags, have_old, msg, err); + if (!old_sha1) + die("BUG: verify called with old_sha1 set to NULL"); + return ref_transaction_update(transaction, refname, + NULL, old_sha1, + flags, NULL, err); } -int update_ref(const char *action, const char *refname, - const unsigned char *sha1, const unsigned char *oldval, - int flags, enum action_on_err onerr) +int update_ref(const char *msg, const char *refname, + const unsigned char *new_sha1, const unsigned char *old_sha1, + unsigned int flags, enum action_on_err onerr) { struct ref_transaction *t; struct strbuf err = STRBUF_INIT; t = ref_transaction_begin(&err); if (!t || - ref_transaction_update(t, refname, sha1, oldval, flags, - !!oldval, action, &err) || + ref_transaction_update(t, refname, new_sha1, old_sha1, + flags, msg, &err) || ref_transaction_commit(t, &err)) { const char *str = "update_ref failed for ref '%s': %s"; @@ -3711,10 +3722,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n, int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { - int ret = 0, delnum = 0, i; - const char **delnames; + int ret = 0, i; int n = transaction->nr; struct ref_update **updates = transaction->updates; + struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; + struct string_list_item *ref_to_delete; assert(err); @@ -3726,9 +3738,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Allocate work space */ - delnames = xmalloc(sizeof(*delnames) * n); - /* Copy, sort, and reject duplicate refs */ qsort(updates, n, sizeof(*updates), ref_update_compare); if (ref_update_reject_duplicates(updates, n, err)) { @@ -3739,17 +3748,17 @@ int ref_transaction_commit(struct ref_transaction *transaction, /* Acquire all locks while verifying old values */ for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; - int flags = update->flags; + unsigned int flags = update->flags; - if (is_null_sha1(update->new_sha1)) + if ((flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) flags |= REF_DELETING; - update->lock = lock_ref_sha1_basic(update->refname, - (update->have_old ? - update->old_sha1 : - NULL), - NULL, - flags, - &update->type); + update->lock = lock_ref_sha1_basic( + update->refname, + ((update->flags & REF_HAVE_OLD) ? + update->old_sha1 : NULL), + NULL, + flags, + &update->type); if (!update->lock) { ret = (errno == ENOTDIR) ? TRANSACTION_NAME_CONFLICT @@ -3763,41 +3772,57 @@ int ref_transaction_commit(struct ref_transaction *transaction, /* Perform updates first so live commits remain referenced */ for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; + int flags = update->flags; - if (!is_null_sha1(update->new_sha1)) { - if (write_ref_sha1(update->lock, update->new_sha1, - update->msg)) { + if ((flags & REF_HAVE_NEW) && !is_null_sha1(update->new_sha1)) { + int overwriting_symref = ((update->type & REF_ISSYMREF) && + (update->flags & REF_NODEREF)); + + if (!overwriting_symref + && !hashcmp(update->lock->old_sha1, update->new_sha1)) { + /* + * The reference already has the desired + * value, so we don't need to write it. + */ + unlock_ref(update->lock); + update->lock = NULL; + } else if (write_ref_sha1(update->lock, update->new_sha1, + update->msg)) { update->lock = NULL; /* freed by write_ref_sha1 */ strbuf_addf(err, "Cannot update the ref '%s'.", update->refname); ret = TRANSACTION_GENERIC_ERROR; goto cleanup; + } else { + /* freed by write_ref_sha1(): */ + update->lock = NULL; } - update->lock = NULL; /* freed by write_ref_sha1 */ } } /* Perform deletes now that updates are safely completed */ for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; + int flags = update->flags; - if (update->lock) { + if ((flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) { if (delete_ref_loose(update->lock, update->type, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - if (!(update->flags & REF_ISPRUNING)) - delnames[delnum++] = update->lock->ref_name; + if (!(flags & REF_ISPRUNING)) + string_list_append(&refs_to_delete, + update->lock->ref_name); } } - if (repack_without_refs(delnames, delnum, err)) { + if (repack_without_refs(&refs_to_delete, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } - for (i = 0; i < delnum; i++) - unlink_or_warn(git_path("logs/%s", delnames[i])); + for_each_string_list_item(ref_to_delete, &refs_to_delete) + unlink_or_warn(git_path("logs/%s", ref_to_delete->string)); clear_loose_ref_cache(&ref_cache); cleanup: @@ -3806,7 +3831,7 @@ cleanup: for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); - free(delnames); + string_list_clear(&refs_to_delete, 0); return ret; } @@ -3995,6 +4020,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1, struct ref_lock *lock; char *log_file; int status = 0; + int type; memset(&cb, 0, sizeof(cb)); cb.flags = flags; @@ -4006,7 +4032,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1, * reference itself, plus we might need to update the * reference if --updateref was specified: */ - lock = lock_ref_sha1_basic(refname, sha1, NULL, 0, NULL); + lock = lock_ref_sha1_basic(refname, sha1, NULL, 0, &type); if (!lock) return error("cannot lock ref '%s'", refname); if (!reflog_exists(refname)) { @@ -4043,10 +4069,21 @@ int reflog_expire(const char *refname, const unsigned char *sha1, (*cleanup_fn)(cb.policy_cb); if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) { + /* + * It doesn't make sense to adjust a reference pointed + * to by a symbolic ref based on expiring entries in + * the symbolic reference's reflog. Nor can we update + * a reference if there are no remaining reflog + * entries. + */ + int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) && + !(type & REF_ISSYMREF) && + !is_null_sha1(cb.last_kept_sha1); + if (close_lock_file(&reflog_lock)) { status |= error("couldn't write %s: %s", log_file, strerror(errno)); - } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && + } else if (update && (write_in_full(lock->lock_fd, sha1_to_hex(cb.last_kept_sha1), 40) != 40 || write_str_in_full(lock->lock_fd, "\n") != 1 || @@ -4057,7 +4094,7 @@ int reflog_expire(const char *refname, const unsigned char *sha1, } else if (commit_lock_file(&reflog_lock)) { status |= error("unable to commit reflog '%s' (%s)", log_file, strerror(errno)); - } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && commit_ref(lock)) { + } else if (update && commit_ref(lock)) { status |= error("couldn't set %s", lock->ref_name); } } |