Teach Git to respect skip-worktree bit (reading part)
grep: turn on --cached for files that is marked skip-worktree ls-files: do not check for deleted file that is marked skip-worktree update-index: ignore update request if it's skip-worktree, while still allows removing diff*: skip worktree version Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									44a3691362
								
							
						
					
					
						commit
						b4d1690df1
					
				|  | @ -180,6 +180,11 @@ static void add_remove_files(struct string_list *list) | |||
| 	for (i = 0; i < list->nr; i++) { | ||||
| 		struct stat st; | ||||
| 		struct string_list_item *p = &(list->items[i]); | ||||
| 		int pos = index_name_pos(&the_index, p->string, strlen(p->string)); | ||||
| 		struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos]; | ||||
|  | ||||
| 		if (ce && ce_skip_worktree(ce)) | ||||
| 			continue; | ||||
|  | ||||
| 		if (!lstat(p->string, &st)) { | ||||
| 			if (add_to_cache(p->string, &st, 0)) | ||||
|  |  | |||
|  | @ -517,7 +517,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached, | |||
| 		 * are identical, even if worktree file has been modified, so use | ||||
| 		 * cache version instead | ||||
| 		 */ | ||||
| 		if (cached || (ce->ce_flags & CE_VALID)) { | ||||
| 		if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) { | ||||
| 			if (ce_stage(ce)) | ||||
| 				continue; | ||||
| 			hit |= grep_sha1(opt, ce->sha1, ce->name, 0); | ||||
|  |  | |||
|  | @ -194,6 +194,8 @@ static void show_files(struct dir_struct *dir, const char *prefix) | |||
| 				continue; | ||||
| 			if (ce->ce_flags & CE_UPDATE) | ||||
| 				continue; | ||||
| 			if (ce_skip_worktree(ce)) | ||||
| 				continue; | ||||
| 			err = lstat(ce->name, &st); | ||||
| 			if (show_deleted && err) | ||||
| 				show_ce_entry(tag_removed, ce); | ||||
|  |  | |||
|  | @ -172,29 +172,29 @@ static int process_directory(const char *path, int len, struct stat *st) | |||
| 	return error("%s: is a directory - add files inside instead", path); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Process a regular file | ||||
|  */ | ||||
| static int process_file(const char *path, int len, struct stat *st) | ||||
| { | ||||
| 	int pos = cache_name_pos(path, len); | ||||
| 	struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos]; | ||||
|  | ||||
| 	if (ce && S_ISGITLINK(ce->ce_mode)) | ||||
| 		return error("%s is already a gitlink, not replacing", path); | ||||
|  | ||||
| 	return add_one_path(ce, path, len, st); | ||||
| } | ||||
|  | ||||
| static int process_path(const char *path) | ||||
| { | ||||
| 	int len; | ||||
| 	int pos, len; | ||||
| 	struct stat st; | ||||
| 	struct cache_entry *ce; | ||||
|  | ||||
| 	len = strlen(path); | ||||
| 	if (has_symlink_leading_path(path, len)) | ||||
| 		return error("'%s' is beyond a symbolic link", path); | ||||
|  | ||||
| 	pos = cache_name_pos(path, len); | ||||
| 	ce = pos < 0 ? NULL : active_cache[pos]; | ||||
| 	if (ce && ce_skip_worktree(ce)) { | ||||
| 		/* | ||||
| 		 * working directory version is assumed "good" | ||||
| 		 * so updating it does not make sense. | ||||
| 		 * On the other hand, removing it from index should work | ||||
| 		 */ | ||||
| 		if (allow_remove && remove_file_from_cache(path)) | ||||
| 			return error("%s: cannot remove from the index", path); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * First things first: get the stat information, to decide | ||||
| 	 * what to do about the pathname! | ||||
|  | @ -205,7 +205,13 @@ static int process_path(const char *path) | |||
| 	if (S_ISDIR(st.st_mode)) | ||||
| 		return process_directory(path, len, &st); | ||||
|  | ||||
| 	return process_file(path, len, &st); | ||||
| 	/* | ||||
| 	 * Process a regular file | ||||
| 	 */ | ||||
| 	if (ce && S_ISGITLINK(ce->ce_mode)) | ||||
| 		return error("%s is already a gitlink, not replacing", path); | ||||
|  | ||||
| 	return add_one_path(ce, path, len, &st); | ||||
| } | ||||
|  | ||||
| static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, | ||||
|  |  | |||
|  | @ -159,7 +159,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) | |||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		if (ce_uptodate(ce)) | ||||
| 		if (ce_uptodate(ce) || ce_skip_worktree(ce)) | ||||
| 			continue; | ||||
|  | ||||
| 		/* If CE_VALID is set, don't look at workdir for file removal */ | ||||
|  | @ -339,7 +339,8 @@ static void do_oneway_diff(struct unpack_trees_options *o, | |||
| 	int match_missing, cached; | ||||
|  | ||||
| 	/* if the entry is not checked out, don't examine work tree */ | ||||
| 	cached = o->index_only || (idx && (idx->ce_flags & CE_VALID)); | ||||
| 	cached = o->index_only || | ||||
| 		(idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx))); | ||||
| 	/* | ||||
| 	 * Backward compatibility wart - "diff-index -m" does | ||||
| 	 * not mean "do not ignore merges", but "match_missing". | ||||
|  |  | |||
							
								
								
									
										2
									
								
								diff.c
								
								
								
								
							
							
						
						
									
										2
									
								
								diff.c
								
								
								
								
							|  | @ -1805,7 +1805,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int | |||
| 	 * If ce is marked as "assume unchanged", there is no | ||||
| 	 * guarantee that work tree matches what we are looking for. | ||||
| 	 */ | ||||
| 	if (ce->ce_flags & CE_VALID) | ||||
| 	if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* | ||||
|  |  | |||
|  | @ -265,7 +265,7 @@ int ie_match_stat(const struct index_state *istate, | |||
| 	 * If it's marked as always valid in the index, it's | ||||
| 	 * valid whatever the checked-out copy says. | ||||
| 	 */ | ||||
| 	if (!ignore_valid && (ce->ce_flags & CE_VALID)) | ||||
| 	if (!ignore_valid && ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))) | ||||
| 		return 0; | ||||
|  | ||||
| 	/* | ||||
|  | @ -1004,11 +1004,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, | |||
| 	if (ce_uptodate(ce)) | ||||
| 		return ce; | ||||
|  | ||||
| 	/* | ||||
| 	 * CE_VALID means the user promised us that the change to | ||||
| 	 * the work tree does not matter and told us not to worry. | ||||
| 	 */ | ||||
| 	if (!ignore_valid && (ce->ce_flags & CE_VALID)) { | ||||
| 	if (!ignore_valid && ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))) { | ||||
| 		ce_mark_uptodate(ce); | ||||
| 		return ce; | ||||
| 	} | ||||
|  |  | |||
|  | @ -0,0 +1,163 @@ | |||
| #!/bin/sh | ||||
| # | ||||
| # Copyright (c) 2008 Nguyễn Thái Ngọc Duy | ||||
| # | ||||
|  | ||||
| test_description='skip-worktree bit test' | ||||
|  | ||||
| . ./test-lib.sh | ||||
|  | ||||
| cat >expect.full <<EOF | ||||
| H 1 | ||||
| H 2 | ||||
| H init.t | ||||
| H sub/1 | ||||
| H sub/2 | ||||
| EOF | ||||
|  | ||||
| cat >expect.skip <<EOF | ||||
| S 1 | ||||
| H 2 | ||||
| H init.t | ||||
| S sub/1 | ||||
| H sub/2 | ||||
| EOF | ||||
|  | ||||
| NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 | ||||
| ZERO_SHA0=0000000000000000000000000000000000000000 | ||||
| setup_absent() { | ||||
| 	test -f 1 && rm 1 | ||||
| 	git update-index --remove 1 && | ||||
| 	git update-index --add --cacheinfo 100644 $NULL_SHA1 1 && | ||||
| 	git update-index --skip-worktree 1 | ||||
| } | ||||
|  | ||||
| test_absent() { | ||||
| 	echo "100644 $NULL_SHA1 0	1" > expected && | ||||
| 	git ls-files --stage 1 > result && | ||||
| 	test_cmp expected result && | ||||
| 	test ! -f 1 | ||||
| } | ||||
|  | ||||
| setup_dirty() { | ||||
| 	git update-index --force-remove 1 && | ||||
| 	echo dirty > 1 && | ||||
| 	git update-index --add --cacheinfo 100644 $NULL_SHA1 1 && | ||||
| 	git update-index --skip-worktree 1 | ||||
| } | ||||
|  | ||||
| test_dirty() { | ||||
| 	echo "100644 $NULL_SHA1 0	1" > expected && | ||||
| 	git ls-files --stage 1 > result && | ||||
| 	test_cmp expected result && | ||||
| 	echo dirty > expected | ||||
| 	test_cmp expected 1 | ||||
| } | ||||
|  | ||||
| test_expect_success 'setup' ' | ||||
| 	test_commit init && | ||||
| 	mkdir sub && | ||||
| 	touch ./1 ./2 sub/1 sub/2 && | ||||
| 	git add 1 2 sub/1 sub/2 && | ||||
| 	git update-index --skip-worktree 1 sub/1 && | ||||
| 	git ls-files -t > result && | ||||
| 	test_cmp expect.skip result | ||||
| ' | ||||
|  | ||||
| test_expect_success 'update-index' ' | ||||
| 	setup_absent && | ||||
| 	git update-index 1 && | ||||
| 	test_absent | ||||
| ' | ||||
|  | ||||
| test_expect_success 'update-index' ' | ||||
| 	setup_dirty && | ||||
| 	git update-index 1 && | ||||
| 	test_dirty | ||||
| ' | ||||
|  | ||||
| test_expect_success 'update-index --remove' ' | ||||
| 	setup_absent && | ||||
| 	git update-index --remove 1 && | ||||
| 	test -z "$(git ls-files 1)" && | ||||
| 	test ! -f 1 | ||||
| ' | ||||
|  | ||||
| test_expect_success 'update-index --remove' ' | ||||
| 	setup_dirty && | ||||
| 	git update-index --remove 1 && | ||||
| 	test -z "$(git ls-files 1)" && | ||||
| 	echo dirty > expected && | ||||
| 	test_cmp expected 1 | ||||
| ' | ||||
|  | ||||
| test_expect_success 'ls-files --delete' ' | ||||
| 	setup_absent && | ||||
| 	test -z "$(git ls-files -d)" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'ls-files --delete' ' | ||||
| 	setup_dirty && | ||||
| 	test -z "$(git ls-files -d)" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'ls-files --modified' ' | ||||
| 	setup_absent && | ||||
| 	test -z "$(git ls-files -m)" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'ls-files --modified' ' | ||||
| 	setup_dirty && | ||||
| 	test -z "$(git ls-files -m)" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'grep with skip-worktree file' ' | ||||
| 	git update-index --no-skip-worktree 1 && | ||||
| 	echo test > 1 && | ||||
| 	git update-index 1 && | ||||
| 	git update-index --skip-worktree 1 && | ||||
| 	rm 1 && | ||||
| 	test "$(git grep --no-ext-grep test)" = "1:test" | ||||
| ' | ||||
|  | ||||
| echo ":000000 100644 $ZERO_SHA0 $NULL_SHA1 A	1" > expected | ||||
| test_expect_success 'diff-index does not examine skip-worktree absent entries' ' | ||||
| 	setup_absent && | ||||
| 	git diff-index HEAD -- 1 > result && | ||||
| 	test_cmp expected result | ||||
| ' | ||||
|  | ||||
| test_expect_success 'diff-index does not examine skip-worktree dirty entries' ' | ||||
| 	setup_dirty && | ||||
| 	git diff-index HEAD -- 1 > result && | ||||
| 	test_cmp expected result | ||||
| ' | ||||
|  | ||||
| test_expect_success 'diff-files does not examine skip-worktree absent entries' ' | ||||
| 	setup_absent && | ||||
| 	test -z "$(git diff-files -- one)" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'diff-files does not examine skip-worktree dirty entries' ' | ||||
| 	setup_dirty && | ||||
| 	test -z "$(git diff-files -- one)" | ||||
| ' | ||||
|  | ||||
| test_expect_success 'git-rm succeeds on skip-worktree absent entries' ' | ||||
| 	setup_absent && | ||||
| 	git rm 1 | ||||
| ' | ||||
|  | ||||
| test_expect_failure 'commit on skip-worktree absent entries' ' | ||||
| 	git reset && | ||||
| 	setup_absent && | ||||
| 	test_must_fail git commit -m null 1 | ||||
| ' | ||||
|  | ||||
| test_expect_failure 'commit on skip-worktree dirty entries' ' | ||||
| 	git reset && | ||||
| 	setup_dirty && | ||||
| 	test_must_fail git commit -m null 1 | ||||
| ' | ||||
|  | ||||
| test_done | ||||
		Loading…
	
		Reference in New Issue
	
	 Nguyễn Thái Ngọc Duy
						Nguyễn Thái Ngọc Duy