Skip to content

Commit f429f2c

Browse files
Ben PeartBen Peart
authored andcommitted
PR 150171: add support for recursive directory matches in hashmap implementation of sparse-checkout
Update the GVFS specific hashmap implementation of sparse-checkout to treat "/a/" as a request to match all files in the directory "/a/" as well as all paths underneath it. This matches our updated GVFS requirements and also matches git's definition of what "/a/" does. Related work items: #686383
1 parent 0e11020 commit f429f2c

File tree

2 files changed

+47
-28
lines changed

2 files changed

+47
-28
lines changed

t/t1091-sparse-checkout-hashmap.sh

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,33 +52,40 @@ test_expect_success 'return to full checkout of master' '
5252
test_expect_success 'perform sparse checkout of master with a directory' '
5353
mkdir one &&
5454
mkdir one/two &&
55+
mkdir one/two/three &&
5556
echo "initial" >one/a &&
5657
echo "initial" >one/b &&
5758
echo "initial" >one/two/a &&
5859
echo "initial" >one/two/b &&
59-
git add one/a one/b one/two/a one/two/b &&
60+
echo "initial" >one/two/three/a &&
61+
echo "initial" >one/two/three/b &&
62+
git add --all &&
6063
git commit -m "directory commit" &&
61-
echo "/one/" >.git/info/sparse-checkout &&
64+
echo "/one/two/" >.git/info/sparse-checkout &&
6265
git checkout master &&
63-
test_path_is_file one/a &&
64-
test_path_is_file one/b &&
65-
test_path_is_missing one/two/a &&
66-
test_path_is_missing one/two/a &&
6766
test_path_is_missing a &&
6867
test_path_is_missing b &&
69-
test_path_is_missing c
68+
test_path_is_missing c &&
69+
test_path_is_missing one/a &&
70+
test_path_is_missing one/b &&
71+
test_path_is_file one/two/a &&
72+
test_path_is_file one/two/b &&
73+
test_path_is_file one/two/three/a &&
74+
test_path_is_file one/two/three/b
7075
'
7176

7277
test_expect_success 'perform sparse checkout of master with a shell glob' '
7378
echo "a" >>.git/info/sparse-checkout &&
7479
git checkout master &&
75-
test_path_is_file one/a &&
76-
test_path_is_file one/b &&
77-
test_path_is_file one/two/a &&
78-
test_path_is_missing one/two/b &&
7980
test_path_is_file a &&
8081
test_path_is_missing b &&
81-
test_path_is_missing c
82+
test_path_is_missing c &&
83+
test_path_is_file one/a &&
84+
test_path_is_missing one/b &&
85+
test_path_is_file one/two/a &&
86+
test_path_is_file one/two/b &&
87+
test_path_is_file one/two/three/a &&
88+
test_path_is_file one/two/three/b
8289
'
8390

8491
test_expect_success 'perform sparse checkout of master with a directory and files' '
@@ -87,13 +94,15 @@ test_expect_success 'perform sparse checkout of master with a directory and file
8794
echo "/c" >>.git/info/sparse-checkout &&
8895
echo "/one/two/" >>.git/info/sparse-checkout &&
8996
git checkout master &&
97+
test_path_is_file a &&
98+
test_path_is_file b &&
99+
test_path_is_file c &&
90100
test_path_is_file one/a &&
91-
test_path_is_file one/b &&
101+
test_path_is_missing one/b &&
92102
test_path_is_file one/two/a &&
93103
test_path_is_file one/two/b &&
94-
test_path_is_file a &&
95-
test_path_is_file b &&
96-
test_path_is_file c
104+
test_path_is_file one/two/three/a &&
105+
test_path_is_file one/two/three/b
97106
'
98107

99108
test_done

unpack-trees.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,26 +1005,35 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
10051005
continue;
10061006
}
10071007

1008-
/* check to see if it matches a directory (ie /dir/) */
1009-
/* note, the excludes logic trims off any trailing '/' */
1010-
slash = strrchr(ce->name, '/');
1008+
/*
1009+
* Check to see if it matches a directory or any path
1010+
* underneath it. In other words, /foo/ will match a
1011+
* directory /foo and all paths underneath it.
1012+
*
1013+
* Note, the excludes logic trims off any trailing '/'
1014+
*/
10111015
strbuf_reset(&sb);
10121016
strbuf_addch(&sb, '/');
1017+
slash = strrchr(ce->name, '/');
10131018
if (slash)
10141019
strbuf_add(&sb, ce->name, slash - ce->name);
1015-
hashmap_entry_init(&e, memhash(sb.buf, sb.len));
1016-
e.pattern = sb.buf;
1017-
e.patternlen = sb.len;
1018-
if (hashmap_get(&el->pattern_hash, &e, NULL))
1019-
{
1020-
/* TODO optimize here by looping through all other cache entries in this directory */
1021-
ce->ce_flags &= ~clear_mask;
1022-
cache++;
1023-
continue;
1020+
while (sb.len) {
1021+
hashmap_entry_init(&e, memhash(sb.buf, sb.len));
1022+
e.pattern = sb.buf;
1023+
e.patternlen = sb.len;
1024+
if (hashmap_get(&el->pattern_hash, &e, NULL))
1025+
{
1026+
ce->ce_flags &= ~clear_mask;
1027+
goto next;
1028+
}
1029+
1030+
slash = strrchr(sb.buf, '/');
1031+
strbuf_setlen(&sb, slash - sb.buf);
10241032
}
10251033

10261034
/* check for shell globs with no wild cards (.gitignore, .gitattributes) */
10271035
strbuf_reset(&sb);
1036+
slash = strrchr(ce->name, '/');
10281037
if (slash)
10291038
strbuf_addstr(&sb, slash+1);
10301039
else
@@ -1035,6 +1044,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
10351044
if (hashmap_get(&el->pattern_hash, &e, NULL))
10361045
ce->ce_flags &= ~clear_mask;
10371046

1047+
next:
10381048
cache++;
10391049
continue;
10401050
}

0 commit comments

Comments
 (0)