Subject: [PATCH 2/4] reachable.c: mark reachable objects in index from all worktrees

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 1 Jun 2016 17:45:17 +0700

Current mark_reachable_objects() only marks objects from index from
_current_ worktree as reachable instead of all worktrees. Because this
function is used for pruning, there is a chance that objects referenced
by other worktrees may be deleted. Fix that.

Small behavior change in "one worktree" case, the index is read again
from file. In the current implementation, if the_index is already
loaded, the index file will not be read from file again. This adds some
more cost to this operation, hopefully insignificant because
reachability test is usually very expensive already.

Reported-by: Johannes Sixt <[email-masked]>
Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 reachable.c      | 33 +++++++++++++++++++++++++++++----
 t/t5304-prune.sh |  9 +++++++++
 2 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/reachable.c b/reachable.c
index 15dbe60..8f67242 100644
--- a/reachable.c
+++ b/reachable.c
@@ -9,6 +9,7 @@
 #include "cache-tree.h"
 #include "progress.h"
 #include "list-objects.h"
+#include "worktree.h"
 
 struct connectivity_progress {
 	struct progress *progress;
@@ -155,6 +156,32 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
 				      FOR_EACH_OBJECT_LOCAL_ONLY);
 }
 
+static void add_objects_from_worktree(struct rev_info *revs)
+{
+	struct worktree **worktrees, **p;
+
+	worktrees = get_worktrees();
+	for (p = worktrees; *p; p++) {
+		struct worktree *wt = *p;
+		struct index_state istate;
+
+		memset(&istate, 0, sizeof(istate));
+		if (read_index_from(&istate,
+				    worktree_git_path(wt, "index")) > 0)
+			add_index_objects_to_pending(revs, 0, &istate);
+		discard_index(&istate);
+	}
+	free_worktrees(worktrees);
+
+	/*
+	 * this is in case the index is already updated but not
+	 * written down in file yet, then add_index_... in the above
+	 * loop will miss new objects that are just created or
+	 * referenced.
+	 */
+	add_index_objects_to_pending(revs, 0, &the_index);
+}
+
 void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 			    unsigned long mark_recent,
 			    struct progress *progress)
@@ -169,10 +196,6 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 	revs->blob_objects = 1;
 	revs->tree_objects = 1;
 
-	/* Add all refs from the index file */
-	read_cache();
-	add_index_objects_to_pending(revs, 0, &the_index);
-
 	/* Add all external refs */
 	for_each_ref(add_one_ref, revs);
 
@@ -183,6 +206,8 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 	if (mark_reflog)
 		add_reflogs_to_pending(revs, 0);
 
+	add_objects_from_worktree(revs);
+
 	cp.progress = progress;
 	cp.count = 0;
 
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 133b584..cba45c7 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -283,4 +283,13 @@ test_expect_success 'prune: handle alternate object database' '
 	git -C B prune
 '
 
+test_expect_success 'prune: handle index in multiple worktrees' '
+	git worktree add second-worktree &&
+	echo "new blob for second-worktree" >second-worktree/blob &&
+	git -C second-worktree add blob &&
+	git prune --expire=now &&
+	git -C second-worktree show :blob >actual &&
+	test_cmp second-worktree/blob actual
+'
+
 test_done
-- 
2.8.2.524.g6ff3d78