Skip to content

Commit 6b1e225

Browse files
committed
Merge branch 'vd/loose-ref-iteration-optimization'
The code to iterate over loose references have been optimized to reduce the number of lstat() system calls. * vd/loose-ref-iteration-optimization: files-backend.c: avoid stat in 'loose_fill_ref_dir' dir.[ch]: add 'follow_symlink' arg to 'get_dtype' dir.[ch]: expose 'get_dtype' ref-cache.c: fix prefix matching in ref iteration
2 parents c662038 + 2cdb796 commit 6b1e225

File tree

7 files changed

+112
-49
lines changed

7 files changed

+112
-49
lines changed

diagnose.c

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -71,42 +71,6 @@ static int dir_file_stats(struct object_directory *object_dir, void *data)
7171
return 0;
7272
}
7373

74-
/*
75-
* Get the d_type of a dirent. If the d_type is unknown, derive it from
76-
* stat.st_mode.
77-
*
78-
* Note that 'path' is assumed to have a trailing slash. It is also modified
79-
* in-place during the execution of the function, but is then reverted to its
80-
* original value before returning.
81-
*/
82-
static unsigned char get_dtype(struct dirent *e, struct strbuf *path)
83-
{
84-
struct stat st;
85-
unsigned char dtype = DTYPE(e);
86-
size_t base_path_len;
87-
88-
if (dtype != DT_UNKNOWN)
89-
return dtype;
90-
91-
/* d_type unknown in dirent, try to fall back on lstat results */
92-
base_path_len = path->len;
93-
strbuf_addstr(path, e->d_name);
94-
if (lstat(path->buf, &st))
95-
goto cleanup;
96-
97-
/* determine d_type from st_mode */
98-
if (S_ISREG(st.st_mode))
99-
dtype = DT_REG;
100-
else if (S_ISDIR(st.st_mode))
101-
dtype = DT_DIR;
102-
else if (S_ISLNK(st.st_mode))
103-
dtype = DT_LNK;
104-
105-
cleanup:
106-
strbuf_setlen(path, base_path_len);
107-
return dtype;
108-
}
109-
11074
static int count_files(struct strbuf *path)
11175
{
11276
DIR *dir = opendir(path->buf);
@@ -117,7 +81,7 @@ static int count_files(struct strbuf *path)
11781
return 0;
11882

11983
while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
120-
if (get_dtype(e, path) == DT_REG)
84+
if (get_dtype(e, path, 0) == DT_REG)
12185
count++;
12286

12387
closedir(dir);
@@ -146,7 +110,7 @@ static void loose_objs_stats(struct strbuf *buf, const char *path)
146110
base_path_len = count_path.len;
147111

148112
while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
149-
if (get_dtype(e, &count_path) == DT_DIR &&
113+
if (get_dtype(e, &count_path, 0) == DT_DIR &&
150114
strlen(e->d_name) == 2 &&
151115
!hex_to_bytes(&c, e->d_name, 1)) {
152116
strbuf_setlen(&count_path, base_path_len);
@@ -191,7 +155,7 @@ static int add_directory_to_archiver(struct strvec *archiver_args,
191155

192156
strbuf_add_absolute_path(&abspath, at_root ? "." : path);
193157
strbuf_addch(&abspath, '/');
194-
dtype = get_dtype(e, &abspath);
158+
dtype = get_dtype(e, &abspath, 0);
195159

196160
strbuf_setlen(&buf, len);
197161
strbuf_addstr(&buf, e->d_name);

dir.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,39 @@ static int get_index_dtype(struct index_state *istate,
22352235
return DT_UNKNOWN;
22362236
}
22372237

2238+
unsigned char get_dtype(struct dirent *e, struct strbuf *path,
2239+
int follow_symlink)
2240+
{
2241+
struct stat st;
2242+
unsigned char dtype = DTYPE(e);
2243+
size_t base_path_len;
2244+
2245+
if (dtype != DT_UNKNOWN && !(follow_symlink && dtype == DT_LNK))
2246+
return dtype;
2247+
2248+
/*
2249+
* d_type unknown or unfollowed symlink, try to fall back on [l]stat
2250+
* results. If [l]stat fails, explicitly set DT_UNKNOWN.
2251+
*/
2252+
base_path_len = path->len;
2253+
strbuf_addstr(path, e->d_name);
2254+
if ((follow_symlink && stat(path->buf, &st)) ||
2255+
(!follow_symlink && lstat(path->buf, &st)))
2256+
goto cleanup;
2257+
2258+
/* determine d_type from st_mode */
2259+
if (S_ISREG(st.st_mode))
2260+
dtype = DT_REG;
2261+
else if (S_ISDIR(st.st_mode))
2262+
dtype = DT_DIR;
2263+
else if (S_ISLNK(st.st_mode))
2264+
dtype = DT_LNK;
2265+
2266+
cleanup:
2267+
strbuf_setlen(path, base_path_len);
2268+
return dtype;
2269+
}
2270+
22382271
static int resolve_dtype(int dtype, struct index_state *istate,
22392272
const char *path, int len)
22402273
{

dir.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,22 @@ struct dir_struct {
363363

364364
struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp);
365365

366+
/*
367+
* Get the d_type of a dirent. If the d_type is unknown, derive it from
368+
* stat.st_mode using the path to the dirent's containing directory (path) and
369+
* the name of the dirent itself.
370+
*
371+
* If 'follow_symlink' is 1, this function will attempt to follow DT_LNK types
372+
* using 'stat'. Links are *not* followed recursively, so a symlink pointing
373+
* to another symlink will still resolve to 'DT_LNK'.
374+
*
375+
* Note that 'path' is assumed to have a trailing slash. It is also modified
376+
* in-place during the execution of the function, but is then reverted to its
377+
* original value before returning.
378+
*/
379+
unsigned char get_dtype(struct dirent *e, struct strbuf *path,
380+
int follow_symlink);
381+
366382
/*Count the number of slashes for string s*/
367383
int count_slashes(const char *s);
368384

refs/files-backend.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,8 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
246246
int dirnamelen = strlen(dirname);
247247
struct strbuf refname;
248248
struct strbuf path = STRBUF_INIT;
249-
size_t path_baselen;
250249

251250
files_ref_path(refs, &path, dirname);
252-
path_baselen = path.len;
253251

254252
d = opendir(path.buf);
255253
if (!d) {
@@ -262,23 +260,22 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
262260

263261
while ((de = readdir(d)) != NULL) {
264262
struct object_id oid;
265-
struct stat st;
266263
int flag;
264+
unsigned char dtype;
267265

268266
if (de->d_name[0] == '.')
269267
continue;
270268
if (ends_with(de->d_name, ".lock"))
271269
continue;
272270
strbuf_addstr(&refname, de->d_name);
273-
strbuf_addstr(&path, de->d_name);
274-
if (stat(path.buf, &st) < 0) {
275-
; /* silently ignore */
276-
} else if (S_ISDIR(st.st_mode)) {
271+
272+
dtype = get_dtype(de, &path, 1);
273+
if (dtype == DT_DIR) {
277274
strbuf_addch(&refname, '/');
278275
add_entry_to_dir(dir,
279276
create_dir_entry(dir->cache, refname.buf,
280277
refname.len));
281-
} else {
278+
} else if (dtype == DT_REG) {
282279
if (!refs_resolve_ref_unsafe(&refs->base,
283280
refname.buf,
284281
RESOLVE_REF_READING,
@@ -308,7 +305,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
308305
create_ref_entry(refname.buf, &oid, flag));
309306
}
310307
strbuf_setlen(&refname, dirnamelen);
311-
strbuf_setlen(&path, path_baselen);
312308
}
313309
strbuf_release(&refname);
314310
strbuf_release(&path);

refs/ref-cache.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
412412

413413
if (level->prefix_state == PREFIX_WITHIN_DIR) {
414414
entry_prefix_state = overlaps_prefix(entry->name, iter->prefix);
415-
if (entry_prefix_state == PREFIX_EXCLUDES_DIR)
415+
if (entry_prefix_state == PREFIX_EXCLUDES_DIR ||
416+
(entry_prefix_state == PREFIX_WITHIN_DIR && !(entry->flag & REF_DIR)))
416417
continue;
417418
} else {
418419
entry_prefix_state = level->prefix_state;

t/t1500-rev-parse.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,27 @@ test_expect_success 'rev-parse --since= unsqueezed ordering' '
264264
test_cmp expect actual
265265
'
266266

267+
test_expect_success 'rev-parse --bisect includes bad, excludes good' '
268+
test_commit_bulk 6 &&
269+
270+
git update-ref refs/bisect/bad-1 HEAD~1 &&
271+
git update-ref refs/bisect/b HEAD~2 &&
272+
git update-ref refs/bisect/bad-3 HEAD~3 &&
273+
git update-ref refs/bisect/good-3 HEAD~3 &&
274+
git update-ref refs/bisect/bad-4 HEAD~4 &&
275+
git update-ref refs/bisect/go HEAD~4 &&
276+
277+
# Note: refs/bisect/b and refs/bisect/go should be ignored because they
278+
# do not match the refs/bisect/bad or refs/bisect/good prefixes.
279+
cat >expect <<-EOF &&
280+
refs/bisect/bad-1
281+
refs/bisect/bad-3
282+
refs/bisect/bad-4
283+
^refs/bisect/good-3
284+
EOF
285+
286+
git rev-parse --symbolic-full-name --bisect >actual &&
287+
test_cmp expect actual
288+
'
289+
267290
test_done

t/t4205-log-pretty-formats.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,36 @@ test_expect_success '%S in git log --format works with other placeholders (part
956956
test_cmp expect actual
957957
'
958958

959+
test_expect_success 'setup more commits for %S with --bisect' '
960+
test_commit four &&
961+
test_commit five &&
962+
963+
head1=$(git rev-parse --verify HEAD~0) &&
964+
head2=$(git rev-parse --verify HEAD~1) &&
965+
head3=$(git rev-parse --verify HEAD~2) &&
966+
head4=$(git rev-parse --verify HEAD~3)
967+
'
968+
969+
test_expect_success '%S with --bisect labels commits with refs/bisect/bad ref' '
970+
git update-ref refs/bisect/bad-$head1 $head1 &&
971+
git update-ref refs/bisect/go $head1 &&
972+
git update-ref refs/bisect/bad-$head2 $head2 &&
973+
git update-ref refs/bisect/b $head3 &&
974+
git update-ref refs/bisect/bad-$head4 $head4 &&
975+
git update-ref refs/bisect/good-$head4 $head4 &&
976+
977+
# We expect to see the range of commits betwee refs/bisect/good-$head4
978+
# and refs/bisect/bad-$head1. The "source" ref is the nearest bisect ref
979+
# from which the commit is reachable.
980+
cat >expect <<-EOF &&
981+
$head1 refs/bisect/bad-$head1
982+
$head2 refs/bisect/bad-$head2
983+
$head3 refs/bisect/bad-$head2
984+
EOF
985+
git log --bisect --format="%H %S" >actual &&
986+
test_cmp expect actual
987+
'
988+
959989
test_expect_success 'log --pretty=reference' '
960990
git log --pretty="tformat:%h (%s, %as)" >expect &&
961991
git log --pretty=reference >actual &&

0 commit comments

Comments
 (0)