};
static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+ const char *dirname, size_t len,
+ int incomplete);
+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
static struct ref_dir *get_ref_dir(struct ref_entry *entry)
{
dir = &entry->u.subdir;
if (entry->flag & REF_INCOMPLETE) {
read_loose_refs(entry->name, dir);
+
+ /*
+ * Manually add refs/bisect, which, being
+ * per-worktree, might not appear in the directory
+ * listing for refs/ in the main repo.
+ */
+ if (!strcmp(entry->name, "refs/")) {
+ int pos = search_ref_dir(dir, "refs/bisect/", 12);
+ if (pos < 0) {
+ struct ref_entry *child_entry;
+ child_entry = create_dir_entry(dir->ref_cache,
+ "refs/bisect/",
+ 12, 1);
+ add_entry_to_dir(dir, child_entry);
+ read_loose_refs("refs/bisect",
+ &child_entry->u.subdir);
+ }
+ }
entry->flag &= ~REF_INCOMPLETE;
}
return dir;
return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
}
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
+{
+ unsigned int flag = 0;
+
+ if (broken)
+ flag = DO_FOR_EACH_INCLUDE_BROKEN;
+ return do_for_each_ref(&ref_cache, prefix, fn, 0, flag, cb_data);
+}
+
int for_each_ref_in_submodule(const char *submodule, const char *prefix,
each_ref_fn fn, void *cb_data)
{
struct ref_to_prune *ref_to_prune;
};
+static int is_per_worktree_ref(const char *refname);
+
/*
* An each_ref_entry_fn that is run over loose references only. If
* the loose reference can be packed, add an entry in the packed ref
struct ref_entry *packed_entry;
int is_tag_ref = starts_with(entry->name, "refs/tags/");
+ /* Do not pack per-worktree refs: */
+ if (is_per_worktree_ref(entry->name))
+ return 0;
+
/* ALWAYS pack tags */
if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
return 0;
return 0;
}
+static int is_per_worktree_ref(const char *refname)
+{
+ return !strcmp(refname, "HEAD") ||
+ starts_with(refname, "refs/bisect/");
+}
+
+static int is_pseudoref_syntax(const char *refname)
+{
+ const char *c;
+
+ for (c = refname; *c; c++) {
+ if (!isupper(*c) && *c != '-' && *c != '_')
+ return 0;
+ }
+
+ return 1;
+}
+
+enum ref_type ref_type(const char *refname)
+{
+ if (is_per_worktree_ref(refname))
+ return REF_TYPE_PER_WORKTREE;
+ if (is_pseudoref_syntax(refname))
+ return REF_TYPE_PSEUDOREF;
+ return REF_TYPE_NORMAL;
+}
+
+static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
+ const unsigned char *old_sha1, struct strbuf *err)
+{
+ const char *filename;
+ int fd;
+ static struct lock_file lock;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = -1;
+
+ strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+
+ filename = git_path("%s", pseudoref);
+ fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+ if (fd < 0) {
+ strbuf_addf(err, "Could not open '%s' for writing: %s",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ if (old_sha1) {
+ unsigned char actual_old_sha1[20];
+
+ if (read_ref(pseudoref, actual_old_sha1))
+ die("could not read ref '%s'", pseudoref);
+ if (hashcmp(actual_old_sha1, old_sha1)) {
+ strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
+ rollback_lock_file(&lock);
+ goto done;
+ }
+ }
+
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
+ strbuf_addf(err, "Could not write to '%s'", filename);
+ rollback_lock_file(&lock);
+ goto done;
+ }
+
+ commit_lock_file(&lock);
+ ret = 0;
+done:
+ strbuf_release(&buf);
+ return ret;
+}
+
+static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
+{
+ static struct lock_file lock;
+ const char *filename;
+
+ filename = git_path("%s", pseudoref);
+
+ if (old_sha1 && !is_null_sha1(old_sha1)) {
+ int fd;
+ unsigned char actual_old_sha1[20];
+
+ fd = hold_lock_file_for_update(&lock, filename,
+ LOCK_DIE_ON_ERROR);
+ if (fd < 0)
+ die_errno(_("Could not open '%s' for writing"), filename);
+ if (read_ref(pseudoref, actual_old_sha1))
+ die("could not read ref '%s'", pseudoref);
+ if (hashcmp(actual_old_sha1, old_sha1)) {
+ warning("Unexpected sha1 when deleting %s", pseudoref);
+ rollback_lock_file(&lock);
+ return -1;
+ }
+
+ unlink(filename);
+ rollback_lock_file(&lock);
+ } else {
+ unlink(filename);
+ }
+
+ return 0;
+}
+
int delete_ref(const char *refname, const unsigned char *old_sha1,
unsigned int flags)
{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
+ if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+ return delete_pseudoref(refname, old_sha1);
+
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_sha1,
{
static char term = '\n';
struct object *o;
+ int fd;
o = parse_object(sha1);
if (!o) {
unlock_ref(lock);
return -1;
}
- if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
- write_in_full(lock->lk->fd, &term, 1) != 1 ||
+ fd = get_lock_file_fd(lock->lk);
+ if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 ||
+ write_in_full(fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
strbuf_addf(err,
- "Couldn't write %s", lock->lk->filename.buf);
+ "Couldn't write %s", get_lock_file_path(lock->lk));
unlock_ref(lock);
return -1;
}
const unsigned char *new_sha1, const unsigned char *old_sha1,
unsigned int flags, enum action_on_err onerr)
{
- struct ref_transaction *t;
+ struct ref_transaction *t = NULL;
struct strbuf err = STRBUF_INIT;
+ int ret = 0;
- t = ref_transaction_begin(&err);
- if (!t ||
- ref_transaction_update(t, refname, new_sha1, old_sha1,
- flags, msg, &err) ||
- ref_transaction_commit(t, &err)) {
+ if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+ ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
+ } else {
+ t = ref_transaction_begin(&err);
+ if (!t ||
+ ref_transaction_update(t, refname, new_sha1, old_sha1,
+ flags, msg, &err) ||
+ ref_transaction_commit(t, &err)) {
+ ret = 1;
+ ref_transaction_free(t);
+ }
+ }
+ if (ret) {
const char *str = "update_ref failed for ref '%s': %s";
- ref_transaction_free(t);
switch (onerr) {
case UPDATE_REFS_MSG_ON_ERR:
error(str, refname, err.buf);
return 1;
}
strbuf_release(&err);
- ref_transaction_free(t);
+ if (t)
+ ref_transaction_free(t);
return 0;
}
cb.newlog = fdopen_lock_file(&reflog_lock, "w");
if (!cb.newlog) {
error("cannot fdopen %s (%s)",
- reflog_lock.filename.buf, strerror(errno));
+ get_lock_file_path(&reflog_lock), strerror(errno));
goto failure;
}
}
status |= error("couldn't write %s: %s", log_file,
strerror(errno));
} else if (update &&
- (write_in_full(lock->lk->fd,
+ (write_in_full(get_lock_file_fd(lock->lk),
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
- write_str_in_full(lock->lk->fd, "\n") != 1 ||
- close_ref(lock) < 0)) {
+ write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
+ close_ref(lock) < 0)) {
status |= error("couldn't write %s",
- lock->lk->filename.buf);
+ get_lock_file_path(lock->lk));
rollback_lock_file(&reflog_lock);
} else if (commit_lock_file(&reflog_lock)) {
status |= error("unable to commit reflog '%s' (%s)",