delete_ref(): delete packed ref
This implements deletion of a packed ref. Since it is a very rare event to delete a ref compared to looking up, creating and updating, this opts to remove the ref from the packed-ref file instead of doing any of the filesystem based "negative ref" trick to optimize the deletion path. Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
parent
22a3844eba
commit
c0277d15ef
109
refs.c
109
refs.c
@ -406,33 +406,6 @@ int get_ref_sha1(const char *ref, unsigned char *sha1)
|
|||||||
return read_ref(mkpath("refs/%s", ref), sha1);
|
return read_ref(mkpath("refs/%s", ref), sha1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int delete_ref(const char *refname, unsigned char *sha1)
|
|
||||||
{
|
|
||||||
struct ref_lock *lock;
|
|
||||||
int err, i, ret = 0;
|
|
||||||
|
|
||||||
lock = lock_any_ref_for_update(refname, sha1);
|
|
||||||
if (!lock)
|
|
||||||
return 1;
|
|
||||||
i = strlen(lock->lk->filename) - 5; /* .lock */
|
|
||||||
lock->lk->filename[i] = 0;
|
|
||||||
err = unlink(lock->lk->filename);
|
|
||||||
if (err) {
|
|
||||||
ret = 1;
|
|
||||||
error("unlink(%s) failed: %s",
|
|
||||||
lock->lk->filename, strerror(errno));
|
|
||||||
}
|
|
||||||
lock->lk->filename[i] = '.';
|
|
||||||
|
|
||||||
err = unlink(lock->log_file);
|
|
||||||
if (err && errno != ENOENT)
|
|
||||||
fprintf(stderr, "warning: unlink(%s) failed: %s",
|
|
||||||
lock->log_file, strerror(errno));
|
|
||||||
|
|
||||||
invalidate_cached_refs();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure "ref" is something reasonable to have under ".git/refs/";
|
* Make sure "ref" is something reasonable to have under ".git/refs/";
|
||||||
* We do not like it if:
|
* We do not like it if:
|
||||||
@ -555,7 +528,7 @@ static int remove_empty_directories(char *file)
|
|||||||
return remove_empty_dir_recursive(path, len);
|
return remove_empty_dir_recursive(path, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
|
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
|
||||||
{
|
{
|
||||||
char *ref_file;
|
char *ref_file;
|
||||||
const char *orig_ref = ref;
|
const char *orig_ref = ref;
|
||||||
@ -567,7 +540,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
|
|||||||
lock = xcalloc(1, sizeof(struct ref_lock));
|
lock = xcalloc(1, sizeof(struct ref_lock));
|
||||||
lock->lock_fd = -1;
|
lock->lock_fd = -1;
|
||||||
|
|
||||||
ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);
|
ref = resolve_ref(ref, lock->old_sha1, mustexist, flag);
|
||||||
if (!ref && errno == EISDIR) {
|
if (!ref && errno == EISDIR) {
|
||||||
/* we are trying to lock foo but we used to
|
/* we are trying to lock foo but we used to
|
||||||
* have foo/bar which now does not exist;
|
* have foo/bar which now does not exist;
|
||||||
@ -580,7 +553,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
|
|||||||
error("there are still refs under '%s'", orig_ref);
|
error("there are still refs under '%s'", orig_ref);
|
||||||
goto error_return;
|
goto error_return;
|
||||||
}
|
}
|
||||||
ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);
|
ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
|
||||||
}
|
}
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
last_errno = errno;
|
last_errno = errno;
|
||||||
@ -640,12 +613,84 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
|
|||||||
if (check_ref_format(ref))
|
if (check_ref_format(ref))
|
||||||
return NULL;
|
return NULL;
|
||||||
strcpy(refpath, mkpath("refs/%s", ref));
|
strcpy(refpath, mkpath("refs/%s", ref));
|
||||||
return lock_ref_sha1_basic(refpath, old_sha1);
|
return lock_ref_sha1_basic(refpath, old_sha1, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
|
struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)
|
||||||
{
|
{
|
||||||
return lock_ref_sha1_basic(ref, old_sha1);
|
return lock_ref_sha1_basic(ref, old_sha1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int repack_without_ref(const char *refname)
|
||||||
|
{
|
||||||
|
struct ref_list *list, *packed_ref_list;
|
||||||
|
int fd;
|
||||||
|
int found = 0;
|
||||||
|
struct lock_file packlock;
|
||||||
|
|
||||||
|
packed_ref_list = get_packed_refs();
|
||||||
|
for (list = packed_ref_list; list; list = list->next) {
|
||||||
|
if (!strcmp(refname, list->name)) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
return 0;
|
||||||
|
memset(&packlock, 0, sizeof(packlock));
|
||||||
|
fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
|
||||||
|
if (fd < 0)
|
||||||
|
return error("cannot delete '%s' from packed refs", refname);
|
||||||
|
|
||||||
|
for (list = packed_ref_list; list; list = list->next) {
|
||||||
|
char line[PATH_MAX + 100];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!strcmp(refname, list->name))
|
||||||
|
continue;
|
||||||
|
len = snprintf(line, sizeof(line), "%s %s\n",
|
||||||
|
sha1_to_hex(list->sha1), list->name);
|
||||||
|
/* this should not happen but just being defensive */
|
||||||
|
if (len > sizeof(line))
|
||||||
|
die("too long a refname '%s'", list->name);
|
||||||
|
write_or_die(fd, line, len);
|
||||||
|
}
|
||||||
|
return commit_lock_file(&packlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int delete_ref(const char *refname, unsigned char *sha1)
|
||||||
|
{
|
||||||
|
struct ref_lock *lock;
|
||||||
|
int err, i, ret = 0, flag = 0;
|
||||||
|
|
||||||
|
lock = lock_ref_sha1_basic(refname, sha1, &flag);
|
||||||
|
if (!lock)
|
||||||
|
return 1;
|
||||||
|
if (!(flag & REF_ISPACKED)) {
|
||||||
|
/* loose */
|
||||||
|
i = strlen(lock->lk->filename) - 5; /* .lock */
|
||||||
|
lock->lk->filename[i] = 0;
|
||||||
|
err = unlink(lock->lk->filename);
|
||||||
|
if (err) {
|
||||||
|
ret = 1;
|
||||||
|
error("unlink(%s) failed: %s",
|
||||||
|
lock->lk->filename, strerror(errno));
|
||||||
|
}
|
||||||
|
lock->lk->filename[i] = '.';
|
||||||
|
}
|
||||||
|
/* removing the loose one could have resurrected an earlier
|
||||||
|
* packed one. Also, if it was not loose we need to repack
|
||||||
|
* without it.
|
||||||
|
*/
|
||||||
|
ret |= repack_without_ref(refname);
|
||||||
|
|
||||||
|
err = unlink(lock->log_file);
|
||||||
|
if (err && errno != ENOENT)
|
||||||
|
fprintf(stderr, "warning: unlink(%s) failed: %s",
|
||||||
|
lock->log_file, strerror(errno));
|
||||||
|
invalidate_cached_refs();
|
||||||
|
unlock_ref(lock);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlock_ref(struct ref_lock *lock)
|
void unlock_ref(struct ref_lock *lock)
|
||||||
|
Loading…
Reference in New Issue
Block a user