[PATCH 1/2] Delete ref $frotz by moving ref file to "deleted-$frotz~ref".

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The idea is that moving:

$GIT_DIR/refs/<refpath>/frotz

to:

$GIT_DIR/deleted-refs/<refpath>/frotz~ref

maybe cheaper and safer than repacking the refs without the
deleted one.

On the other hand now when resolving a ref we have to check
if a related deleted ref file exists.

The new "delete_ref" function is similar to "write_ref_sha1".

Signed-off-by: Christian Couder <chriscool@xxxxxxxxxxxxx>
---
 refs.c |  160 ++++++++++++++++++++++++++++++++++++++++------------------------
 refs.h |    2 +
 2 files changed, 102 insertions(+), 60 deletions(-)

diff --git a/refs.c b/refs.c
index 89cffaf..6a8da58 100644
--- a/refs.c
+++ b/refs.c
@@ -176,6 +176,38 @@ static struct ref_list *get_loose_refs(v
 	return cached_refs.loose;
 }
 
+static char *get_del_ref_name(const char *ref_name)
+{
+	size_t ref_name_len = strlen(ref_name);
+	char *delref_name = xmalloc(ref_name_len + 13);
+
+	strncpy(delref_name, "deleted-", 8);
+	strncpy(delref_name + 8, ref_name, ref_name_len);
+	strcpy(delref_name + 8 + ref_name_len, "~ref");
+
+	return delref_name;
+}
+
+static int lstat_del_ref_file(const char *ref_name, struct stat *st)
+{
+	char *delref_name = get_del_ref_name(ref_name);
+	char *delref_file = git_path("%s", delref_name);
+
+	int ret = lstat(delref_file, st);
+
+	free(delref_name);
+	return ret;
+}
+
+static char *get_del_ref_file(const char *ref_name)
+{
+	char *delref_name = get_del_ref_name(ref_name);
+	char *delref_file = xstrdup(git_path("%s", delref_name));
+
+	free(delref_name);
+	return delref_file;
+}
+
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 
@@ -204,6 +236,17 @@ const char *resolve_ref(const char *ref,
 		 * born.  It is NOT OK if we are resolving for
 		 * reading.
 		 */
+
+		if (!lstat_del_ref_file(ref, &st)) {
+			/* The ref has been deleted. */
+			if (flag)
+				*flag |= REF_ISDELETED;
+			if (reading)
+				return NULL;
+			hashclr(sha1);
+			return ref;
+		}
+
 		if (lstat(path, &st) < 0) {
 			struct ref_list *list = get_packed_refs();
 			while (list) {
@@ -541,12 +584,14 @@ static struct ref_lock *lock_ref_sha1_ba
 	struct ref_lock *lock;
 	struct stat st;
 	int last_errno = 0;
+	int default_flag;
+	int *ref_flag = flag ? flag : &default_flag;
 	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, flag);
+	ref = resolve_ref(ref, lock->old_sha1, mustexist, ref_flag);
 	if (!ref && errno == EISDIR) {
 		/* we are trying to lock foo but we used to
 		 * have foo/bar which now does not exist;
@@ -559,7 +604,7 @@ static struct ref_lock *lock_ref_sha1_ba
 			error("there are still refs under '%s'", orig_ref);
 			goto error_return;
 		}
-		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);
+		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, ref_flag);
 	}
 	if (!ref) {
 		last_errno = errno;
@@ -568,7 +613,8 @@ static struct ref_lock *lock_ref_sha1_ba
 		goto error_return;
 	}
 	if (is_null_sha1(lock->old_sha1)) {
-		/* The ref did not exist and we are creating it.
+		/* The ref did not exist (or has been deleted)
+		 * and we are (re)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.
@@ -580,15 +626,19 @@ static struct ref_lock *lock_ref_sha1_ba
 			int len = strlen(list->name);
 			int cmplen = (namlen < len) ? namlen : len;
 			const char *lead = (namlen < len) ? list->name : ref;
+			struct stat st;
 
 			if (!strncmp(ref, list->name, cmplen) &&
-			    lead[cmplen] == '/') {
+			    lead[cmplen] == '/' &&
+			    lstat_del_ref_file(list->name, &st)) {
 				error("'%s' exists; cannot create '%s'",
 				      list->name, ref);
 				goto error_return;
 			}
 			list = list->next;
 		}
+		if (*ref_flag & REF_ISDELETED)
+			lock->del_file = get_del_ref_file(ref);
 	}
 
 	lock->lk = xcalloc(1, sizeof(struct lock_file));
@@ -627,77 +677,60 @@ struct ref_lock *lock_any_ref_for_update
 	return lock_ref_sha1_basic(ref, old_sha1, NULL);
 }
 
-static struct lock_file packlock;
-
-static int repack_without_ref(const char *refname)
+/* Remove the ref "refs/$frotz" by creating "deleted-refs/$frotz~ref". */
+int delete_ref(const char *ref_name, unsigned char *sha1)
 {
-	struct ref_list *list, *packed_ref_list;
-	int fd;
-	int found = 0;
+	int flag = 0;
+	static char term = '\n';
+	struct ref_lock *lock = lock_ref_sha1_basic(ref_name, sha1, &flag);
 
-	packed_ref_list = get_packed_refs();
-	for (list = packed_ref_list; list; list = list->next) {
-		if (!strcmp(refname, list->name)) {
-			found = 1;
-			break;
-		}
+	if (!lock)
+		return -1;
+	if (flag & REF_ISDELETED) {
+		unlock_ref(lock);
+		return 0; /* Already deleted is ok. */
 	}
-	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);
+	if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
+	    write(lock->lock_fd, &term, 1) != 1
+		|| close(lock->lock_fd) < 0) {
+		error("Couldn't write %s", lock->lk->filename);
+		unlock_ref(lock);
+		return -1;
 	}
-	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;
+	invalidate_cached_refs();
 	if (!(flag & REF_ISPACKED)) {
 		/* loose */
-		i = strlen(lock->lk->filename) - 5; /* .lock */
+		int i = strlen(lock->lk->filename) - 5; /* .lock */
 		lock->lk->filename[i] = 0;
-		err = unlink(lock->lk->filename);
-		if (err) {
-			ret = 1;
+		if (unlink(lock->lk->filename)) {
 			error("unlink(%s) failed: %s",
 			      lock->lk->filename, strerror(errno));
+			unlock_ref(lock);
+			return -1;
 		}
 		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)
+	if (unlink(lock->log_file) && errno != ENOENT)
 		fprintf(stderr, "warning: unlink(%s) failed: %s",
 			lock->log_file, strerror(errno));
-	invalidate_cached_refs();
+	if (!lock->del_file)
+		lock->del_file = get_del_ref_file(ref_name);
+	if (safe_create_leading_directories(lock->del_file)) {
+		error("unable to create directory for %s: %s",
+		      lock->del_file, strerror(errno));
+		unlock_ref(lock);
+		return -1;
+	}
+	if (rename(lock->lk->filename, lock->del_file)) {
+		error("rename(%s -> %s) failed: %s",
+		      lock->lk->filename, lock->del_file,
+		      strerror(errno));
+		unlock_ref(lock);
+		return -1;
+	}
+	lock->lock_fd = -1;
 	unlock_ref(lock);
-	return ret;
+	return 0;
 }
 
 void unlock_ref(struct ref_lock *lock)
@@ -710,6 +743,7 @@ void unlock_ref(struct ref_lock *lock)
 	}
 	free(lock->ref_name);
 	free(lock->log_file);
+	free(lock->del_file);
 	free(lock);
 }
 
@@ -786,6 +820,12 @@ int write_ref_sha1(struct ref_lock *lock
 		unlock_ref(lock);
 		return -1;
 	}
+	if (lock->del_file && unlink(lock->del_file) && errno != ENOENT) {
+		error("unlink(%s) failed: %s",
+		      lock->del_file, strerror(errno));
+		unlock_ref(lock);
+		return -1;
+	}
 	if (commit_lock_file(lock->lk)) {
 		error("Couldn't set %s", lock->ref_name);
 		unlock_ref(lock);
diff --git a/refs.h b/refs.h
index a57d437..5aa7755 100644
--- a/refs.h
+++ b/refs.h
@@ -4,6 +4,7 @@ #define REFS_H
 struct ref_lock {
 	char *ref_name;
 	char *log_file;
+	char *del_file;
 	struct lock_file *lk;
 	unsigned char old_sha1[20];
 	int lock_fd;
@@ -16,6 +17,7 @@ struct ref_lock {
  */
 #define REF_ISSYMREF 01
 #define REF_ISPACKED 02
+#define REF_ISDELETED 04
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);
-- 
1.4.3.rc2.gbcf275-dirty
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]