Skip to content

Commit ad25723

Browse files
committed
Merge branch 'jk/ref-cache-non-repository-optim'
The underlying machinery used by "ls-files -o" and other commands have been taught not to create empty submodule ref cache for a directory that is not a submodule. This removes a ton of wasted CPU cycles. * jk/ref-cache-non-repository-optim: resolve_gitlink_ref: ignore non-repository paths clean: make is_git_repository a public function
2 parents 48c39e9 + a2d5156 commit ad25723

File tree

6 files changed

+80
-40
lines changed

6 files changed

+80
-40
lines changed

builtin/clean.c

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -147,30 +147,6 @@ static int exclude_cb(const struct option *opt, const char *arg, int unset)
147147
return 0;
148148
}
149149

150-
/*
151-
* Return 1 if the given path is the root of a git repository or
152-
* submodule else 0. Will not return 1 for bare repositories with the
153-
* exception of creating a bare repository in "foo/.git" and calling
154-
* is_git_repository("foo").
155-
*/
156-
static int is_git_repository(struct strbuf *path)
157-
{
158-
int ret = 0;
159-
int gitfile_error;
160-
size_t orig_path_len = path->len;
161-
assert(orig_path_len != 0);
162-
strbuf_complete(path, '/');
163-
strbuf_addstr(path, ".git");
164-
if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))
165-
ret = 1;
166-
if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED ||
167-
gitfile_error == READ_GITFILE_ERR_READ_FAILED)
168-
ret = 1; /* This could be a real .git file, take the
169-
* safe option and avoid cleaning */
170-
strbuf_setlen(path, orig_path_len);
171-
return ret;
172-
}
173-
174150
static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
175151
int dry_run, int quiet, int *dir_gone)
176152
{
@@ -182,7 +158,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
182158

183159
*dir_gone = 1;
184160

185-
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_git_repository(path)) {
161+
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
186162
if (!quiet) {
187163
quote_path_relative(path->buf, prefix, &quoted);
188164
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),

cache.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,6 @@ extern char *git_work_tree_cfg;
458458
extern int is_inside_work_tree(void);
459459
extern const char *get_git_dir(void);
460460
extern const char *get_git_common_dir(void);
461-
extern int is_git_directory(const char *path);
462461
extern char *get_object_directory(void);
463462
extern char *get_index_file(void);
464463
extern char *get_graft_file(void);
@@ -469,6 +468,25 @@ extern const char *get_git_namespace(void);
469468
extern const char *strip_namespace(const char *namespaced_ref);
470469
extern const char *get_git_work_tree(void);
471470

471+
/*
472+
* Return true if the given path is a git directory; note that this _just_
473+
* looks at the directory itself. If you want to know whether "foo/.git"
474+
* is a repository, you must feed that path, not just "foo".
475+
*/
476+
extern int is_git_directory(const char *path);
477+
478+
/*
479+
* Return 1 if the given path is the root of a git repository or
480+
* submodule, else 0. Will not return 1 for bare repositories with the
481+
* exception of creating a bare repository in "foo/.git" and calling
482+
* is_git_repository("foo").
483+
*
484+
* If we run into read errors, we err on the side of saying "yes, it is",
485+
* as we usually consider sub-repos precious, and would prefer to err on the
486+
* side of not disrupting or deleting them.
487+
*/
488+
extern int is_nonbare_repository_dir(struct strbuf *path);
489+
472490
#define READ_GITFILE_ERR_STAT_FAILED 1
473491
#define READ_GITFILE_ERR_NOT_A_FILE 2
474492
#define READ_GITFILE_ERR_OPEN_FAILED 3

refs/files-backend.c

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,10 @@ static void clear_loose_ref_cache(struct ref_cache *refs)
933933
}
934934
}
935935

936+
/*
937+
* Create a new submodule ref cache and add it to the internal
938+
* set of caches.
939+
*/
936940
static struct ref_cache *create_ref_cache(const char *submodule)
937941
{
938942
int len;
@@ -942,16 +946,12 @@ static struct ref_cache *create_ref_cache(const char *submodule)
942946
len = strlen(submodule) + 1;
943947
refs = xcalloc(1, sizeof(struct ref_cache) + len);
944948
memcpy(refs->name, submodule, len);
949+
refs->next = submodule_ref_caches;
950+
submodule_ref_caches = refs;
945951
return refs;
946952
}
947953

948-
/*
949-
* Return a pointer to a ref_cache for the specified submodule. For
950-
* the main repository, use submodule==NULL. The returned structure
951-
* will be allocated and initialized but not necessarily populated; it
952-
* should not be freed.
953-
*/
954-
static struct ref_cache *get_ref_cache(const char *submodule)
954+
static struct ref_cache *lookup_ref_cache(const char *submodule)
955955
{
956956
struct ref_cache *refs;
957957

@@ -961,10 +961,20 @@ static struct ref_cache *get_ref_cache(const char *submodule)
961961
for (refs = submodule_ref_caches; refs; refs = refs->next)
962962
if (!strcmp(submodule, refs->name))
963963
return refs;
964+
return NULL;
965+
}
964966

965-
refs = create_ref_cache(submodule);
966-
refs->next = submodule_ref_caches;
967-
submodule_ref_caches = refs;
967+
/*
968+
* Return a pointer to a ref_cache for the specified submodule. For
969+
* the main repository, use submodule==NULL. The returned structure
970+
* will be allocated and initialized but not necessarily populated; it
971+
* should not be freed.
972+
*/
973+
static struct ref_cache *get_ref_cache(const char *submodule)
974+
{
975+
struct ref_cache *refs = lookup_ref_cache(submodule);
976+
if (!refs)
977+
refs = create_ref_cache(submodule);
968978
return refs;
969979
}
970980

@@ -1336,16 +1346,24 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
13361346
int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
13371347
{
13381348
int len = strlen(path), retval;
1339-
char *submodule;
1349+
struct strbuf submodule = STRBUF_INIT;
13401350
struct ref_cache *refs;
13411351

13421352
while (len && path[len-1] == '/')
13431353
len--;
13441354
if (!len)
13451355
return -1;
1346-
submodule = xstrndup(path, len);
1347-
refs = get_ref_cache(submodule);
1348-
free(submodule);
1356+
1357+
strbuf_add(&submodule, path, len);
1358+
refs = lookup_ref_cache(submodule.buf);
1359+
if (!refs) {
1360+
if (!is_nonbare_repository_dir(&submodule)) {
1361+
strbuf_release(&submodule);
1362+
return -1;
1363+
}
1364+
refs = create_ref_cache(submodule.buf);
1365+
}
1366+
strbuf_release(&submodule);
13491367

13501368
retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
13511369
return retval;

setup.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,23 @@ int is_git_directory(const char *suspect)
312312
return ret;
313313
}
314314

315+
int is_nonbare_repository_dir(struct strbuf *path)
316+
{
317+
int ret = 0;
318+
int gitfile_error;
319+
size_t orig_path_len = path->len;
320+
assert(orig_path_len != 0);
321+
strbuf_complete(path, '/');
322+
strbuf_addstr(path, ".git");
323+
if (read_gitfile_gently(path->buf, &gitfile_error) || is_git_directory(path->buf))
324+
ret = 1;
325+
if (gitfile_error == READ_GITFILE_ERR_OPEN_FAILED ||
326+
gitfile_error == READ_GITFILE_ERR_READ_FAILED)
327+
ret = 1;
328+
strbuf_setlen(path, orig_path_len);
329+
return ret;
330+
}
331+
315332
int is_inside_git_dir(void)
316333
{
317334
if (inside_git_dir < 0)

t/perf/p7300-clean.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ test_perf 'clean many untracked sub dirs, ignore nested git' '
2828
git clean -n -q -f -f -d 100000_sub_dirs/
2929
'
3030

31+
test_perf 'ls-files -o' '
32+
git ls-files -o
33+
'
34+
3135
test_done

t/t3000-ls-files-others.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ test_expect_success '--no-empty-directory hides empty directory' '
6565
test_cmp expected3 output
6666
'
6767

68+
test_expect_success 'ls-files --others handles non-submodule .git' '
69+
mkdir not-a-submodule &&
70+
echo foo >not-a-submodule/.git &&
71+
git ls-files -o >output &&
72+
test_cmp expected1 output
73+
'
74+
6875
test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '
6976
git init super &&
7077
git init sub &&

0 commit comments

Comments
 (0)