git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Question about 'branch -d' safety

From
Clemens Buchacher <drizzd@aon.at>
Date
Jul 11, 2010, 13:37 UTC
Message-ID
<20100711133730.GA10338@localhost>
In-Reply-To
<201007110916.29567.jnareb@gmail.com>
On Sun, Jul 11, 2010 at 09:16:26AM +0200, Jakub Narebski wrote:
Show 7 quoted lines
> 
> The problem is, that when you have 'foo/bar' branch, then you have
> 'foo/bar' reflog.  When you delete branch 'foo/bar', but do not delete
> 'foo/bar' reflog (only add to it branch deletion event), and then you
> want to create 'foo' branch, git wouldn't be able to create reflog
> fo 'foo' because of directory / file (D/F) conflict: there is 'foo/'
> directory preventing file 'foo' from being created.
Right, of course. So how about this?
Clemens
-->o--
Date: Sun, 11 Jul 2010 12:37:06 +0200
Subject: [PATCH/RFC] graveyard for reflogs

Instead of removing reflogs together with refs, keep them around and reuse them as a starting point, if a ref of the same name is created again.

When a ref is deleted, a ~ is appended to each directory of the corresponding reflog entry, e.g., refs/heads/branch is renamed to refs~/heads~/branch. Since ~ must not be used for ref names, directory/file conflicts are thus avoided.

Known issues:
 - The reflog cannot be accessed while the ref does not exist.
 - Older git versions will not resurrect the reflog, and therefore
   leave the renamed reflog behind.
 - Breaks t7701, because git-expire tries to lock log entries,
   which fails because ~ is an illegal character for refs.
 - Breaks t9300.
Signed-off-by: Clemens Buchacher <drizzd@aon.at>
---
 reflog-walk.c   |    9 +++++--
 refs.c          |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 t/t1450-fsck.sh |    1 +
 3 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/reflog-walk.c b/reflog-walk.c
index 4879615..9415ac8 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -228,15 +228,18 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
 		return;
 	}
 
-	reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
 	info->last_commit_reflog = commit_reflog;
 	commit_reflog->recno--;
-	commit_info->commit = (struct commit *)parse_object(reflog->osha1);
-	if (!commit_info->commit) {
+	if (commit_reflog->recno < 0) {
 		commit->parents = NULL;
 		return;
 	}
 
+	reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
+	commit_info->commit = (struct commit *)parse_object(reflog->nsha1);
+	if (!commit_info->commit)
+		die("Invalid reflog entry");
+
 	commit->parents = xcalloc(sizeof(struct commit_list), 1);
 	commit->parents->item = commit_info->commit;
 	commit->object.flags &= ~(ADDED | SEEN | SHOWN);
diff --git a/refs.c b/refs.c
index b540067..78c48ad 100644
--- a/refs.c
+++ b/refs.c
@@ -1052,6 +1052,70 @@ static int repack_without_ref(const char *refname)
 	return commit_lock_file(&packlock);
 }
 
+static void pronounce_dead(struct strbuf *dead_ref, const char *ref)
+{
+	const char *p = ref;
+	while (*p) {
+		if (*p == '/') {
+			int len = p - ref;
+			strbuf_add(dead_ref, ref, len);
+			if (len > 0)
+				strbuf_addstr(dead_ref, "~/");
+			ref = p + 1;
+		}
+		p++;
+	}
+	strbuf_addstr(dead_ref, ref);
+}
+
+static int bury_log(const char *ref)
+{
+	struct strbuf dead_ref = STRBUF_INIT;
+	struct stat loginfo;
+
+	if (lstat(git_path("logs/%s", ref), &loginfo))
+		return 0;
+
+	pronounce_dead(&dead_ref, ref);
+
+	if (S_ISLNK(loginfo.st_mode))
+		return error("reflog for %s is a symlink", ref);
+
+	if (safe_create_leading_directories(git_path("logs/%s", dead_ref.buf)))
+		return error("unable to create directory for %s", dead_ref.buf);
+
+	if (rename(git_path("logs/%s", ref), git_path("logs/%s", dead_ref.buf)))
+		return error("unable to move logfile to logs/%s: %s",
+			dead_ref.buf, strerror(errno));
+
+	strbuf_release(&dead_ref);
+	return 0;
+}
+
+static int resurrect_log(const char *ref)
+{
+	struct strbuf dead_ref = STRBUF_INIT;
+	struct stat loginfo;
+
+	pronounce_dead(&dead_ref, ref);
+
+	if (lstat(git_path("logs/%s", dead_ref.buf), &loginfo))
+		return 0;
+
+	if (S_ISLNK(loginfo.st_mode))
+		return error("reflog for %s is a symlink", dead_ref.buf);
+
+	if (safe_create_leading_directories(git_path("logs/%s", ref)))
+		return error("unable to create directory for %s", ref);
+
+	if (rename(git_path("logs/%s", dead_ref.buf), git_path("logs/%s", ref)))
+		return error("unable to move logfile to logs/%s: %s",
+			ref, strerror(errno));
+
+	strbuf_release(&dead_ref);
+	return 0;
+}
+
 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 {
 	struct ref_lock *lock;
@@ -1084,7 +1148,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 	 */
 	ret |= repack_without_ref(refname);
 
-	unlink_or_warn(git_path("logs/%s", lock->ref_name));
+	bury_log(lock->ref_name);
 	invalidate_cached_refs();
 	unlock_ref(lock);
 	return ret;
@@ -1384,6 +1448,7 @@ int write_ref_sha1(struct ref_lock *lock,
 		unlock_ref(lock);
 		return -1;
 	}
+	resurrect_log(lock->ref_name);
 	invalidate_cached_refs();
 	if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
 	    (strcmp(lock->ref_name, lock->orig_ref_name) &&
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 759cf12..65f160e 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -55,6 +55,7 @@ test_expect_success 'object with bad sha1' '
 	grep "$sha.*corrupt" out &&
 	rm -f .git/objects/$new &&
 	git update-ref -d refs/heads/bogus &&
+	rm -f .git/logs/refs~/heads~/bogus &&
 	git read-tree -u --reset HEAD
 '
 
-- 
1.7.1.571.gba4d01
Previous: Julian PhillipsNext: Junio C Hamano
Message 13 of 42 in “Question about 'branch -d' safety”
  1. Nanako ShiraishiDec 29, 2009
  2. Nicolas SebrechtDec 29, 2009
  3. Nanako ShiraishiDec 30, 2009
  4. Junio C HamanoDec 30, 2009
  5. Nicolas SebrechtDec 30, 2009
  6. Clemens BuchacherJul 10, 2010
  7. Jonathan NiederJul 10, 2010
  8. Jakub NarebskiJul 10, 2010
  9. Jonathan NiederJul 10, 2010
  10. Clemens BuchacherJul 11, 2010
  11. Jakub NarebskiJul 11, 2010
  12. Julian PhillipsJul 11, 2010
  13. Clemens BuchacherJul 11, 2010
  14. Junio C HamanoJul 11, 2010
  15. Jakub NarebskiJul 11, 2010
  16. Will PalmerJul 11, 2010
  17. Clemens BuchacherJul 12, 2010
  18. Junio C HamanoJul 12, 2010
  19. Clemens BuchacherJul 13, 2010
  20. Will PalmerJul 13, 2010
  21. Johannes SixtJul 13, 2010
  22. Will PalmerJul 13, 2010
  23. Clemens BuchacherJul 13, 2010
  24. Clemens BuchacherJul 17, 2010
  25. Jonathan NiederJul 18, 2010
  26. Jakub NarebskiJul 18, 2010
  27. Will PalmerJul 18, 2010
  28. Jakub NarebskiJul 18, 2010
  29. Will PalmerJul 19, 2010
  30. Jakub NarebskiJul 19, 2010
  31. Joshua JensenJul 19, 2010
  32. Clemens BuchacherJul 19, 2010
  33. Will PalmerJul 19, 2010
  34. Jakub NarebskiJul 19, 2010
  35. Joshua JensenJul 20, 2010
  36. Will PalmerJul 20, 2010
  37. Jakub NarebskiJul 19, 2010
  38. Junio C HamanoJul 19, 2010
  39. Clemens BuchacherJul 19, 2010
  40. Jakub NarebskiJul 19, 2010
  41. Ævar Arnfjörð BjarmasonJul 20, 2010
  42. Matthieu MoyJul 20, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.