mirror of
https://github.com/git/git.git
synced 2026-03-16 03:30:08 +01:00
Merge branch 'lj/refs' into next
* lj/refs: git-branch: remove D/F check done by hand. delete_ref(): delete packed ref lock_ref_sha1(): check D/F conflict with packed ref when creating. lock_ref_sha1(): do not sometimes error() and sometimes die(). refs: minor restructuring of cached refs data. ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.
This commit is contained in:
@@ -121,16 +121,6 @@ then
|
||||
done
|
||||
fi
|
||||
|
||||
branchdir=$(dirname $branchname)
|
||||
while test "$branchdir" != "."
|
||||
do
|
||||
if git-show-ref --verify --quiet -- "refs/heads/$branchdir"
|
||||
then
|
||||
die "$branchdir already exists."
|
||||
fi
|
||||
branchdir=$(dirname $branchdir)
|
||||
done
|
||||
|
||||
prev=''
|
||||
if git-show-ref --verify --quiet -- "refs/heads/$branchname"
|
||||
then
|
||||
|
||||
271
refs.c
271
refs.c
@@ -66,12 +66,42 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Future: need to be in "struct repository"
|
||||
* when doing a full libification.
|
||||
*/
|
||||
struct cached_refs {
|
||||
char did_loose;
|
||||
char did_packed;
|
||||
struct ref_list *loose;
|
||||
struct ref_list *packed;
|
||||
} cached_refs;
|
||||
|
||||
static void free_ref_list(struct ref_list *list)
|
||||
{
|
||||
struct ref_list *next;
|
||||
for ( ; list; list = next) {
|
||||
next = list->next;
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
static void invalidate_cached_refs(void)
|
||||
{
|
||||
struct cached_refs *ca = &cached_refs;
|
||||
|
||||
if (ca->did_loose && ca->loose)
|
||||
free_ref_list(ca->loose);
|
||||
if (ca->did_packed && ca->packed)
|
||||
free_ref_list(ca->packed);
|
||||
ca->loose = ca->packed = NULL;
|
||||
ca->did_loose = ca->did_packed = 0;
|
||||
}
|
||||
|
||||
static struct ref_list *get_packed_refs(void)
|
||||
{
|
||||
static int did_refs = 0;
|
||||
static struct ref_list *refs = NULL;
|
||||
|
||||
if (!did_refs) {
|
||||
if (!cached_refs.did_packed) {
|
||||
struct ref_list *refs = NULL;
|
||||
FILE *f = fopen(git_path("packed-refs"), "r");
|
||||
if (f) {
|
||||
struct ref_list *list = NULL;
|
||||
@@ -86,9 +116,10 @@ static struct ref_list *get_packed_refs(void)
|
||||
fclose(f);
|
||||
refs = list;
|
||||
}
|
||||
did_refs = 1;
|
||||
cached_refs.packed = refs;
|
||||
cached_refs.did_packed = 1;
|
||||
}
|
||||
return refs;
|
||||
return cached_refs.packed;
|
||||
}
|
||||
|
||||
static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
|
||||
@@ -138,14 +169,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
|
||||
|
||||
static struct ref_list *get_loose_refs(void)
|
||||
{
|
||||
static int did_refs = 0;
|
||||
static struct ref_list *refs = NULL;
|
||||
|
||||
if (!did_refs) {
|
||||
refs = get_ref_dir("refs", NULL);
|
||||
did_refs = 1;
|
||||
if (!cached_refs.did_loose) {
|
||||
cached_refs.loose = get_ref_dir("refs", NULL);
|
||||
cached_refs.did_loose = 1;
|
||||
}
|
||||
return refs;
|
||||
return cached_refs.loose;
|
||||
}
|
||||
|
||||
/* We allow "recursive" symbolic refs. Only within reason, though */
|
||||
@@ -378,32 +406,6 @@ 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:
|
||||
@@ -473,26 +475,116 @@ static struct ref_lock *verify_lock(struct ref_lock *lock,
|
||||
return lock;
|
||||
}
|
||||
|
||||
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)
|
||||
static int remove_empty_dir_recursive(char *path, int len)
|
||||
{
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *e;
|
||||
int ret = 0;
|
||||
|
||||
if (!dir)
|
||||
return -1;
|
||||
if (path[len-1] != '/')
|
||||
path[len++] = '/';
|
||||
while ((e = readdir(dir)) != NULL) {
|
||||
struct stat st;
|
||||
int namlen;
|
||||
if ((e->d_name[0] == '.') &&
|
||||
((e->d_name[1] == 0) ||
|
||||
((e->d_name[1] == '.') && e->d_name[2] == 0)))
|
||||
continue; /* "." and ".." */
|
||||
|
||||
namlen = strlen(e->d_name);
|
||||
if ((len + namlen < PATH_MAX) &&
|
||||
strcpy(path + len, e->d_name) &&
|
||||
!lstat(path, &st) &&
|
||||
S_ISDIR(st.st_mode) &&
|
||||
remove_empty_dir_recursive(path, len + namlen))
|
||||
continue; /* happy */
|
||||
|
||||
/* path too long, stat fails, or non-directory still exists */
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
closedir(dir);
|
||||
if (!ret) {
|
||||
path[len] = 0;
|
||||
ret = rmdir(path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remove_empty_directories(char *file)
|
||||
{
|
||||
/* we want to create a file but there is a directory there;
|
||||
* if that is an empty directory (or a directory that contains
|
||||
* only empty directories), remove them.
|
||||
*/
|
||||
char path[PATH_MAX];
|
||||
int len = strlen(file);
|
||||
|
||||
if (len >= PATH_MAX) /* path too long ;-) */
|
||||
return -1;
|
||||
strcpy(path, file);
|
||||
return remove_empty_dir_recursive(path, len);
|
||||
}
|
||||
|
||||
static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
|
||||
{
|
||||
char *ref_file;
|
||||
const char *orig_ref = ref;
|
||||
struct ref_lock *lock;
|
||||
struct stat st;
|
||||
int last_errno = 0;
|
||||
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
|
||||
|
||||
lock = xcalloc(1, sizeof(struct ref_lock));
|
||||
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) {
|
||||
/* we are trying to lock foo but we used to
|
||||
* have foo/bar which now does not exist;
|
||||
* it is normal for the empty directory 'foo'
|
||||
* to remain.
|
||||
*/
|
||||
ref_file = git_path("%s", orig_ref);
|
||||
if (remove_empty_directories(ref_file)) {
|
||||
last_errno = errno;
|
||||
error("there are still refs under '%s'", orig_ref);
|
||||
goto error_return;
|
||||
}
|
||||
ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
|
||||
}
|
||||
if (!ref) {
|
||||
int last_errno = errno;
|
||||
last_errno = errno;
|
||||
error("unable to resolve reference %s: %s",
|
||||
orig_ref, strerror(errno));
|
||||
unlock_ref(lock);
|
||||
errno = last_errno;
|
||||
return NULL;
|
||||
goto error_return;
|
||||
}
|
||||
if (is_null_sha1(lock->old_sha1)) {
|
||||
/* 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.
|
||||
*/
|
||||
int namlen = strlen(ref); /* e.g. 'foo/bar' */
|
||||
struct ref_list *list = get_packed_refs();
|
||||
while (list) {
|
||||
/* list->name could be 'foo' or 'foo/bar/baz' */
|
||||
int len = strlen(list->name);
|
||||
int cmplen = (namlen < len) ? namlen : len;
|
||||
const char *lead = (namlen < len) ? list->name : ref;
|
||||
|
||||
if (!strncmp(ref, list->name, cmplen) &&
|
||||
lead[cmplen] == '/') {
|
||||
error("'%s' exists; cannot create '%s'",
|
||||
list->name, ref);
|
||||
goto error_return;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
lock->lk = xcalloc(1, sizeof(struct lock_file));
|
||||
|
||||
lock->ref_name = xstrdup(ref);
|
||||
@@ -500,11 +592,19 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
|
||||
ref_file = git_path("%s", ref);
|
||||
lock->force_write = lstat(ref_file, &st) && errno == ENOENT;
|
||||
|
||||
if (safe_create_leading_directories(ref_file))
|
||||
die("unable to create directory for %s", ref_file);
|
||||
if (safe_create_leading_directories(ref_file)) {
|
||||
last_errno = errno;
|
||||
error("unable to create directory for %s", ref_file);
|
||||
goto error_return;
|
||||
}
|
||||
lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
|
||||
|
||||
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
|
||||
|
||||
error_return:
|
||||
unlock_ref(lock);
|
||||
errno = last_errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
|
||||
@@ -513,12 +613,84 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
|
||||
if (check_ref_format(ref))
|
||||
return NULL;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@@ -601,6 +773,7 @@ int write_ref_sha1(struct ref_lock *lock,
|
||||
unlock_ref(lock);
|
||||
return -1;
|
||||
}
|
||||
invalidate_cached_refs();
|
||||
if (log_ref_write(lock, sha1, logmsg) < 0) {
|
||||
unlock_ref(lock);
|
||||
return -1;
|
||||
|
||||
Reference in New Issue
Block a user