refs.c: remove extra git_path calls from read_loose_refs

In iterating over the loose refs in "refs/foo/", we keep a running strbuf with "refs/foo/one", "refs/foo/two", etc. But we also need to access these files in the filesystem, as ".git/refs/foo/one", etc. For this latter purpose, we make a series of independent calls to git_path(). These are safe (we only use the result to call stat()), but assigning the result of git_path is a suspicious pattern that we'd rather avoid. This patch keeps a running buffer with ".git/refs/foo/", and we can just append/reset each directory element as we loop. This matches how we handle the refnames. It should also be more efficient, as we do not keep formatting the same ".git/refs/foo" prefix (which can be arbitrarily deep). Technically we are dropping a call to strbuf_cleanup() on each generated filename, but that's OK; it wasn't doing anything, as we are putting in single-level names we read from the filesystem (so it could not possibly be cleaning up cruft like "./" in this instance). A clever reader may also note that the running refname buffer ("refs/foo/") is actually a subset of the filesystem path buffer (".git/refs/foo/"). We could get by with one buffer, indexing the length of $GIT_DIR when we want the refname. However, having tried this, the resulting code actually ends up a little more confusing, and the efficiency improvement is tiny (and almost certainly dwarfed by the system calls we are making). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Aug 10, 2015 at 05:36 UTC f5b2dec1657e09a22f8b2aefa25d022988e3e467
1 file changed +13 -10
refs.c
+13 -10
@@ -1352,19 +1352,23 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
1352 {
1353 struct ref_cache *refs = dir->ref_cache;
1354 DIR *d;
1355 - const char *path;
1355 struct dirent *de;
1356 int dirnamelen = strlen(dirname);
1357 struct strbuf refname;
1358 + struct strbuf path = STRBUF_INIT;
1359 + size_t path_baselen;
1360
1361 if (*refs->name)
1361 - path = git_path_submodule(refs->name, "%s", dirname);
1362 + strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
1363 else
1363 - path = git_path("%s", dirname);
1364 + strbuf_git_path(&path, "%s", dirname);
1365 + path_baselen = path.len;
1366
1365 - d = opendir(path);
1366 - if (!d)
1367 + d = opendir(path.buf);
1368 + if (!d) {
1369 + strbuf_release(&path);
1370 return;
1371 + }
1372
1373 strbuf_init(&refname, dirnamelen + 257);
1374 strbuf_add(&refname, dirname, dirnamelen);
@@ -1373,17 +1377,14 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
1377 unsigned char sha1[20];
1378 struct stat st;
1379 int flag;
1376 - const char *refdir;
1380
1381 if (de->d_name[0] == '.')
1382 continue;
1383 if (ends_with(de->d_name, ".lock"))
1384 continue;
1385 strbuf_addstr(&refname, de->d_name);
1383 - refdir = *refs->name
1384 - ? git_path_submodule(refs->name, "%s", refname.buf)
1385 - : git_path("%s", refname.buf);
1386 - if (stat(refdir, &st) < 0) {
1386 + strbuf_addstr(&path, de->d_name);
1387 + if (stat(path.buf, &st) < 0) {
1388 ; /* silently ignore */
1389 } else if (S_ISDIR(st.st_mode)) {
1390 strbuf_addch(&refname, '/');
@@ -1430,8 +1431,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
1431 create_ref_entry(refname.buf, sha1, flag, 0));
1432 }
1433 strbuf_setlen(&refname, dirnamelen);
1434 + strbuf_setlen(&path, path_baselen);
1435 }
1436 strbuf_release(&refname);
1437 + strbuf_release(&path);
1438 closedir(d);
1439 }
1440