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