[PATCH 6/9] Add support for narrow checkout in unpack_trees()

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

 



This patch teaches unpack_trees() to checkout/remove entries
on working directories appropriately when narrow area is
changed. There are three kind of changes:

 - new_narrow_path: reset workdir to a new narrow checkout
 - add_narrow_path: keep current narrow areas and add more entries
 - remove_narrow_path: remove some entries from current narrow areas

CE_WD_REMOVE is introduced to remove entries from working directories,
but still keep them in index

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx>
---
 cache.h        |    3 ++
 unpack-trees.c |  106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 unpack-trees.h |    4 ++
 3 files changed, 113 insertions(+), 0 deletions(-)

diff --git a/cache.h b/cache.h
index 86288b6..f502b28 100644
--- a/cache.h
+++ b/cache.h
@@ -139,6 +139,9 @@ struct cache_entry {
 #define CE_HASHED    (0x100000)
 #define CE_UNHASHED  (0x200000)
 
+/* Only remove in work directory, not index */
+#define CE_WD_REMOVE (0x400000)
+
 /* "Assume unchanged" mask */
 #define CE_VALID_MASK (CE_VALID | CE_NO_CHECKOUT)
 
diff --git a/unpack-trees.c b/unpack-trees.c
index cba0aca..022c643 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -96,6 +96,21 @@ static int check_updates(struct unpack_trees_options *o)
 	if (o->update && o->verbose_update) {
 		for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
 			struct cache_entry *ce = index->cache[cnt];
+
+			/*
+			 * Special case: CE_WD_REMOVE may be set along with CE_NO_CHECKOUT
+			 * when some files are going to leave narrow checkout, so it must
+			 * go before ce_no_checkout() check
+			 */
+			if (ce->ce_flags & CE_WD_REMOVE) {
+				total++;
+				continue;
+			}
+
+			/* Now if there is any update outside narrow, ignore it */
+			if (ce_no_checkout(ce))
+				continue;
+
 			if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
 				total++;
 		}
@@ -108,6 +123,18 @@ static int check_updates(struct unpack_trees_options *o)
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
+		/* As stated earlier, CE_WD_REMOVE must bypass ce_no_checkout() check */
+		if (ce->ce_flags & CE_WD_REMOVE) {
+			display_progress(progress, ++cnt);
+			if (o->update)
+				unlink_entry(ce);
+			continue;
+		}
+
+		/* Now if there is any update outside narrow, ignore it */
+		if (ce_no_checkout(ce))
+			continue;
+
 		if (ce->ce_flags & CE_REMOVE) {
 			display_progress(progress, ++cnt);
 			if (o->update)
@@ -121,6 +148,9 @@ static int check_updates(struct unpack_trees_options *o)
 	for (i = 0; i < index->cache_nr; i++) {
 		struct cache_entry *ce = index->cache[i];
 
+		if (ce_no_checkout(ce))
+			continue;
+
 		if (ce->ce_flags & CE_UPDATE) {
 			display_progress(progress, ++cnt);
 			ce->ce_flags &= ~CE_UPDATE;
@@ -725,6 +755,58 @@ static void show_stage_entry(FILE *o,
 }
 #endif
 
+int match_narrow_spec(const char *spec_, const char *path)
+{
+	int match = 0;
+	char *spec, *cur_spec;
+
+	if (!spec_)
+		return 1; /* always match if spec_ is NULL */
+	spec = cur_spec = xstrdup(spec_);
+
+	while (!match) {
+		char *next_spec = strchr(cur_spec, ':');
+		if (!next_spec) {
+			if (!fnmatch(cur_spec, path, 0))
+				match = 1;
+			break;
+		}
+		*next_spec = '\0';
+		if (!fnmatch(cur_spec, path, 0))
+			match = 1;
+		cur_spec = next_spec+1;
+	}
+	free(spec);
+	return match;
+}
+
+static int apply_narrow_checkout(struct cache_entry *ce, struct cache_entry *old_ce, struct unpack_trees_options *o)
+{
+	int checkout;
+	int was_no_checkout = old_ce && ce_no_checkout(old_ce);
+
+	if (!(o->new_narrow_path | o->add_narrow_path | o->remove_narrow_path))
+		return 0;
+
+	checkout = match_narrow_spec(o->narrow_spec, ce->name);
+
+	/*
+	 * The logic is a bit twisted here, when we expand checkout (add_narrow_path)
+	 * we narrow no_checkout area. Similarly when we narrow checkout
+	 * (remove_narrow_path), we expand no_checkout area. So there are three cases:
+	 *
+	 * [1] New narrow spec: do not care about old_ce
+	 * [2] Expand spec: mark no_checkout if no_checkout previously _and_ now too
+	 * [3] Narrow spec: mark no_checkout if previously no_checkout _or_ now checkout
+	 */
+	if ((o->new_narrow_path && !checkout) /* [1] */
+	    || (o->add_narrow_path && !checkout && was_no_checkout) /* [2] */
+	    || (o->remove_narrow_path && (checkout || was_no_checkout))) /* [3] */
+		ce_mark_no_checkout(ce);
+
+	return 1;
+}
+
 int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
 {
 	struct cache_entry *index;
@@ -899,6 +981,7 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 	struct cache_entry *current = src[0];
 	struct cache_entry *oldtree = src[1];
 	struct cache_entry *newtree = src[2];
+	int has_narrow_checkout = 0;
 
 	if (o->merge_size != 2)
 		return error("Cannot do a twoway merge of %d trees",
@@ -909,6 +992,9 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 	if (newtree == o->df_conflict_entry)
 		newtree = NULL;
 
+	if (newtree)
+		has_narrow_checkout = apply_narrow_checkout(newtree, current, o);
+
 	if (current) {
 		if ((!oldtree && !newtree) || /* 4 and 5 */
 		    (!oldtree && newtree &&
@@ -918,6 +1004,21 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 		    (oldtree && newtree &&
 		     !same(oldtree, newtree) && /* 18 and 19 */
 		     same(current, newtree))) {
+			if (has_narrow_checkout) {
+				/* enter narrow checkout, keep entry and add to workdir */
+				if (ce_no_checkout(current) && ce_checkout(newtree)) {
+					add_entry(o, current, CE_UPDATE, CE_NO_CHECKOUT);
+					return 1;
+				}
+
+				/* leave narrow checkout, keep entry and remove from workdir */
+				if (ce_checkout(current) && ce_no_checkout(newtree)) {
+					if (verify_uptodate(current, o))
+						return -1;
+					add_entry(o, current, CE_WD_REMOVE | CE_NO_CHECKOUT, 0);
+					return 1;
+				}
+			}
 			return keep_entry(current, o);
 		}
 		else if (oldtree && !newtree && same(current, oldtree)) {
@@ -927,6 +1028,9 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 		else if (oldtree && newtree &&
 			 same(current, oldtree) && !same(current, newtree)) {
 			/* 20 or 21 */
+			/* enter narrow checkout, make sure always add  */
+			if (has_narrow_checkout && ce_no_checkout(current) && ce_checkout(newtree))
+				newtree->ce_flags |= CE_UPDATE;
 			return merged_entry(newtree, current, o);
 		}
 		else {
@@ -987,6 +1091,8 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 	if (!a)
 		return deleted_entry(old, old, o);
 
+	apply_narrow_checkout(a, NULL, o);
+
 	if (old && same(old, a)) {
 		int update = 0;
 		if (o->reset) {
diff --git a/unpack-trees.h b/unpack-trees.h
index 94e5672..880cef8 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -26,6 +26,9 @@ struct unpack_trees_options {
 		     verbose_update:1,
 		     aggressive:1,
 		     skip_unmerged:1,
+		     new_narrow_path:1,
+		     add_narrow_path:2,
+		     remove_narrow_path:2,
 		     gently:1;
 	const char *prefix;
 	int pos;
@@ -37,6 +40,7 @@ struct unpack_trees_options {
 	int merge_size;
 
 	struct cache_entry *df_conflict_entry;
+	const char *narrow_spec;
 	void *unpack_data;
 
 	struct index_state *dst_index;
-- 
1.6.0.rc3.250.g8dd0
--
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]

  Powered by Linux