зеркало из https://github.com/microsoft/git.git
Merge branch 'dt/untracked-subdir'
The experimental untracked-cache feature were buggy when paths with a few levels of subdirectories are involved. * dt/untracked-subdir: untracked cache: fix entry invalidation untracked-cache: fix subdirectory handling
This commit is contained in:
Коммит
038226ebc6
82
dir.c
82
dir.c
|
@ -1297,7 +1297,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
|
|||
*/
|
||||
static enum path_treatment treat_directory(struct dir_struct *dir,
|
||||
struct untracked_cache_dir *untracked,
|
||||
const char *dirname, int len, int exclude,
|
||||
const char *dirname, int len, int baselen, int exclude,
|
||||
const struct path_simplify *simplify)
|
||||
{
|
||||
/* The "len-1" is to strip the final '/' */
|
||||
|
@ -1324,7 +1324,8 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
|
|||
if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
|
||||
return exclude ? path_excluded : path_untracked;
|
||||
|
||||
untracked = lookup_untracked(dir->untracked, untracked, dirname, len);
|
||||
untracked = lookup_untracked(dir->untracked, untracked,
|
||||
dirname + baselen, len - baselen);
|
||||
return read_directory_recursive(dir, dirname, len,
|
||||
untracked, 1, simplify);
|
||||
}
|
||||
|
@ -1444,6 +1445,7 @@ static int get_dtype(struct dirent *de, const char *path, int len)
|
|||
static enum path_treatment treat_one_path(struct dir_struct *dir,
|
||||
struct untracked_cache_dir *untracked,
|
||||
struct strbuf *path,
|
||||
int baselen,
|
||||
const struct path_simplify *simplify,
|
||||
int dtype, struct dirent *de)
|
||||
{
|
||||
|
@ -1495,8 +1497,8 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
|
|||
return path_none;
|
||||
case DT_DIR:
|
||||
strbuf_addch(path, '/');
|
||||
return treat_directory(dir, untracked, path->buf, path->len, exclude,
|
||||
simplify);
|
||||
return treat_directory(dir, untracked, path->buf, path->len,
|
||||
baselen, exclude, simplify);
|
||||
case DT_REG:
|
||||
case DT_LNK:
|
||||
return exclude ? path_excluded : path_untracked;
|
||||
|
@ -1557,7 +1559,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
|
|||
return path_none;
|
||||
|
||||
dtype = DTYPE(de);
|
||||
return treat_one_path(dir, untracked, path, simplify, dtype, de);
|
||||
return treat_one_path(dir, untracked, path, baselen, simplify, dtype, de);
|
||||
}
|
||||
|
||||
static void add_untracked(struct untracked_cache_dir *dir, const char *name)
|
||||
|
@ -1827,7 +1829,7 @@ static int treat_leading_path(struct dir_struct *dir,
|
|||
break;
|
||||
if (simplify_away(sb.buf, sb.len, simplify))
|
||||
break;
|
||||
if (treat_one_path(dir, NULL, &sb, simplify,
|
||||
if (treat_one_path(dir, NULL, &sb, baselen, simplify,
|
||||
DT_DIR, NULL) == path_none)
|
||||
break; /* do not recurse into it */
|
||||
if (len <= baselen) {
|
||||
|
@ -2616,23 +2618,67 @@ done2:
|
|||
return uc;
|
||||
}
|
||||
|
||||
static void invalidate_one_directory(struct untracked_cache *uc,
|
||||
struct untracked_cache_dir *ucd)
|
||||
{
|
||||
uc->dir_invalidated++;
|
||||
ucd->valid = 0;
|
||||
ucd->untracked_nr = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normally when an entry is added or removed from a directory,
|
||||
* invalidating that directory is enough. No need to touch its
|
||||
* ancestors. When a directory is shown as "foo/bar/" in git-status
|
||||
* however, deleting or adding an entry may have cascading effect.
|
||||
*
|
||||
* Say the "foo/bar/file" has become untracked, we need to tell the
|
||||
* untracked_cache_dir of "foo" that "bar/" is not an untracked
|
||||
* directory any more (because "bar" is managed by foo as an untracked
|
||||
* "file").
|
||||
*
|
||||
* Similarly, if "foo/bar/file" moves from untracked to tracked and it
|
||||
* was the last untracked entry in the entire "foo", we should show
|
||||
* "foo/" instead. Which means we have to invalidate past "bar" up to
|
||||
* "foo".
|
||||
*
|
||||
* This function traverses all directories from root to leaf. If there
|
||||
* is a chance of one of the above cases happening, we invalidate back
|
||||
* to root. Otherwise we just invalidate the leaf. There may be a more
|
||||
* sophisticated way than checking for SHOW_OTHER_DIRECTORIES to
|
||||
* detect these cases and avoid unnecessary invalidation, for example,
|
||||
* checking for the untracked entry named "bar/" in "foo", but for now
|
||||
* stick to something safe and simple.
|
||||
*/
|
||||
static int invalidate_one_component(struct untracked_cache *uc,
|
||||
struct untracked_cache_dir *dir,
|
||||
const char *path, int len)
|
||||
{
|
||||
const char *rest = strchr(path, '/');
|
||||
|
||||
if (rest) {
|
||||
int component_len = rest - path;
|
||||
struct untracked_cache_dir *d =
|
||||
lookup_untracked(uc, dir, path, component_len);
|
||||
int ret =
|
||||
invalidate_one_component(uc, d, rest + 1,
|
||||
len - (component_len + 1));
|
||||
if (ret)
|
||||
invalidate_one_directory(uc, dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
invalidate_one_directory(uc, dir);
|
||||
return uc->dir_flags & DIR_SHOW_OTHER_DIRECTORIES;
|
||||
}
|
||||
|
||||
void untracked_cache_invalidate_path(struct index_state *istate,
|
||||
const char *path)
|
||||
{
|
||||
const char *sep;
|
||||
struct untracked_cache_dir *d;
|
||||
if (!istate->untracked || !istate->untracked->root)
|
||||
return;
|
||||
sep = strrchr(path, '/');
|
||||
if (sep)
|
||||
d = lookup_untracked(istate->untracked,
|
||||
istate->untracked->root,
|
||||
path, sep - path);
|
||||
else
|
||||
d = istate->untracked->root;
|
||||
istate->untracked->dir_invalidated++;
|
||||
d->valid = 0;
|
||||
d->untracked_nr = 0;
|
||||
invalidate_one_component(istate->untracked, istate->untracked->root,
|
||||
path, strlen(path));
|
||||
}
|
||||
|
||||
void untracked_cache_remove_from_index(struct index_state *istate,
|
||||
|
|
|
@ -375,7 +375,7 @@ EOF
|
|||
node creation: 0
|
||||
gitignore invalidation: 0
|
||||
directory invalidation: 0
|
||||
opendir: 1
|
||||
opendir: 2
|
||||
EOF
|
||||
test_cmp ../trace.expect ../trace
|
||||
'
|
||||
|
@ -408,7 +408,8 @@ test_expect_success 'set up sparse checkout' '
|
|||
test_path_is_file done/one
|
||||
'
|
||||
|
||||
test_expect_success 'create files, some of which are gitignored' '
|
||||
test_expect_success 'create/modify files, some of which are gitignored' '
|
||||
echo two bis >done/two &&
|
||||
echo three >done/three && # three is gitignored
|
||||
echo four >done/four && # four is gitignored at a higher level
|
||||
echo five >done/five # five is not gitignored
|
||||
|
@ -420,6 +421,7 @@ test_expect_success 'test sparse status with untracked cache' '
|
|||
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
|
||||
git status --porcelain >../status.actual &&
|
||||
cat >../status.expect <<EOF &&
|
||||
M done/two
|
||||
?? .gitignore
|
||||
?? done/five
|
||||
?? dtwo/
|
||||
|
@ -459,6 +461,7 @@ test_expect_success 'test sparse status again with untracked cache' '
|
|||
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
|
||||
git status --porcelain >../status.actual &&
|
||||
cat >../status.expect <<EOF &&
|
||||
M done/two
|
||||
?? .gitignore
|
||||
?? done/five
|
||||
?? dtwo/
|
||||
|
@ -473,4 +476,97 @@ EOF
|
|||
test_cmp ../trace.expect ../trace
|
||||
'
|
||||
|
||||
test_expect_success 'set up for test of subdir and sparse checkouts' '
|
||||
mkdir done/sub &&
|
||||
mkdir done/sub/sub &&
|
||||
echo "sub" > done/sub/sub/file
|
||||
'
|
||||
|
||||
test_expect_success 'test sparse status with untracked cache and subdir' '
|
||||
avoid_racy &&
|
||||
: >../trace &&
|
||||
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
|
||||
git status --porcelain >../status.actual &&
|
||||
cat >../status.expect <<EOF &&
|
||||
M done/two
|
||||
?? .gitignore
|
||||
?? done/five
|
||||
?? done/sub/
|
||||
?? dtwo/
|
||||
EOF
|
||||
test_cmp ../status.expect ../status.actual &&
|
||||
cat >../trace.expect <<EOF &&
|
||||
node creation: 2
|
||||
gitignore invalidation: 0
|
||||
directory invalidation: 1
|
||||
opendir: 3
|
||||
EOF
|
||||
test_cmp ../trace.expect ../trace
|
||||
'
|
||||
|
||||
test_expect_success 'verify untracked cache dump (sparse/subdirs)' '
|
||||
test-dump-untracked-cache >../actual &&
|
||||
cat >../expect <<EOF &&
|
||||
info/exclude 13263c0978fb9fad16b2d580fb800b6d811c3ff0
|
||||
core.excludesfile 0000000000000000000000000000000000000000
|
||||
exclude_per_dir .gitignore
|
||||
flags 00000006
|
||||
/ e6fcc8f2ee31bae321d66afd183fcb7237afae6e recurse valid
|
||||
.gitignore
|
||||
dtwo/
|
||||
/done/ 1946f0437f90c5005533cbe1736a6451ca301714 recurse valid
|
||||
five
|
||||
sub/
|
||||
/done/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
|
||||
sub/
|
||||
/done/sub/sub/ 0000000000000000000000000000000000000000 recurse check_only valid
|
||||
file
|
||||
/dthree/ 0000000000000000000000000000000000000000 recurse check_only valid
|
||||
/dtwo/ 0000000000000000000000000000000000000000 recurse check_only valid
|
||||
two
|
||||
EOF
|
||||
test_cmp ../expect ../actual
|
||||
'
|
||||
|
||||
test_expect_success 'test sparse status again with untracked cache and subdir' '
|
||||
avoid_racy &&
|
||||
: >../trace &&
|
||||
GIT_TRACE_UNTRACKED_STATS="$TRASH_DIRECTORY/trace" \
|
||||
git status --porcelain >../status.actual &&
|
||||
test_cmp ../status.expect ../status.actual &&
|
||||
cat >../trace.expect <<EOF &&
|
||||
node creation: 0
|
||||
gitignore invalidation: 0
|
||||
directory invalidation: 0
|
||||
opendir: 0
|
||||
EOF
|
||||
test_cmp ../trace.expect ../trace
|
||||
'
|
||||
|
||||
test_expect_success 'move entry in subdir from untracked to cached' '
|
||||
git add dtwo/two &&
|
||||
git status --porcelain >../status.actual &&
|
||||
cat >../status.expect <<EOF &&
|
||||
M done/two
|
||||
A dtwo/two
|
||||
?? .gitignore
|
||||
?? done/five
|
||||
?? done/sub/
|
||||
EOF
|
||||
test_cmp ../status.expect ../status.actual
|
||||
'
|
||||
|
||||
test_expect_success 'move entry in subdir from cached to untracked' '
|
||||
git rm --cached dtwo/two &&
|
||||
git status --porcelain >../status.actual &&
|
||||
cat >../status.expect <<EOF &&
|
||||
M done/two
|
||||
?? .gitignore
|
||||
?? done/five
|
||||
?? done/sub/
|
||||
?? dtwo/
|
||||
EOF
|
||||
test_cmp ../status.expect ../status.actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
Загрузка…
Ссылка в новой задаче