Subject: [PATCH v2 19/21] refs: detach split repos' HEAD when the linked ref is updated/deleted

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Sat, 14 Dec 2013 17:55:05 +0700

This is effective when a ref is updated from the super repository as
well as all linked repositories because "repos" directory is shared
between all repos.

We could even forbid a ref update if it's some repo's HEAD. But I'd
rather see a generic, permanent ref locking mechanism in place first
and make use of it.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 refs.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/refs.c b/refs.c
index 3691ef5..d856b1a 100644
--- a/refs.c
+++ b/refs.c
@@ -2462,6 +2462,41 @@ static int repack_without_ref(const char *refname)
 	return repack_without_refs(&refname, 1);
 }
 
+static void detach_repos_refs(const char *refname, const unsigned char *sha1)
+{
+	struct dirent *d;
+	DIR *dir;
+
+	if (!strncmp(refname, "repos/", 6) ||
+	    !strchr(refname, '/') ||
+	    (dir = opendir(git_path("repos"))) == NULL)
+		return;
+
+	while ((d = readdir(dir)) != NULL) {
+		struct strbuf sb_ref = STRBUF_INIT;
+		unsigned char ref_sha1[20];
+		const char *ref;
+		int flags;
+
+		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			continue;
+		strbuf_addf(&sb_ref, "repos/%s/HEAD", d->d_name);
+		ref = resolve_ref_unsafe(sb_ref.buf, ref_sha1, 1, &flags);
+		if (flags & REF_ISSYMREF && ref && !strcmp(ref, refname)) {
+			struct strbuf msg = STRBUF_INIT;
+			strbuf_addf(&msg, "repos: detach from %s by %s",
+				    ref, real_path(get_git_dir()));
+			update_ref(msg.buf, sb_ref.buf, sha1, NULL,
+				   REF_NODEREF, DIE_ON_ERR);
+			strbuf_release(&msg);
+			warning(_("detach HEAD of linked repository %s from %s"),
+				d->d_name, refname);
+		}
+		strbuf_release(&sb_ref);
+	}
+	closedir(dir);
+}
+
 static int delete_ref_loose(struct ref_lock *lock, int flag)
 {
 	if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
@@ -2485,6 +2520,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 	lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag);
 	if (!lock)
 		return 1;
+	detach_repos_refs(lock->ref_name, lock->old_sha1);
 	ret |= delete_ref_loose(lock, flag);
 
 	/* removing the loose one could have resurrected an earlier
@@ -2790,6 +2826,7 @@ int write_ref_sha1(struct ref_lock *lock,
 		unlock_ref(lock);
 		return -1;
 	}
+	detach_repos_refs(lock->ref_name, lock->old_sha1);
 	if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
 	    write_in_full(lock->lock_fd, &term, 1) != 1
 		|| close_ref(lock) < 0) {
-- 
1.8.5.1.77.g42c48fa