Skip to content

Commit ae9af12

Browse files
pcloudsgitster
authored andcommitted
status: show progress bar if refreshing the index takes too long
Refreshing the index is usually very fast, but it can still take a long time sometimes. Cold cache is one. Or copying a repo to a new place (*). It's good to show something to let the user know "git status" is not hanging, it's just busy doing something. (*) In this case, all stat info in the index becomes invalid and git falls back to rehashing all file content to see if there's any difference between updating stat info in the index. This is quite expensive. Even with a repo as small as git.git, it takes 3 seconds. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1d4361b commit ae9af12

File tree

6 files changed

+72
-11
lines changed

6 files changed

+72
-11
lines changed

builtin/am.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2324,7 +2324,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
23242324
/* Ensure a valid committer ident can be constructed */
23252325
git_committer_info(IDENT_STRICT);
23262326

2327-
if (read_index_preload(&the_index, NULL) < 0)
2327+
if (read_index_preload(&the_index, NULL, 0) < 0)
23282328
die(_("failed to read the index"));
23292329

23302330
if (in_progress) {

builtin/commit.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
12951295
static int no_renames = -1;
12961296
static const char *rename_score_arg = (const char *)-1;
12971297
static struct wt_status s;
1298+
unsigned int progress_flag = 0;
12981299
int fd;
12991300
struct object_id oid;
13001301
static struct option builtin_status_options[] = {
@@ -1355,8 +1356,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)
13551356
PATHSPEC_PREFER_FULL,
13561357
prefix, argv);
13571358

1358-
read_cache_preload(&s.pathspec);
1359-
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
1359+
if (status_format != STATUS_FORMAT_PORCELAIN &&
1360+
status_format != STATUS_FORMAT_PORCELAIN_V2)
1361+
progress_flag = REFRESH_PROGRESS;
1362+
read_index_preload(&the_index, &s.pathspec, progress_flag);
1363+
refresh_index(&the_index,
1364+
REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
1365+
&s.pathspec, NULL, NULL);
13601366

13611367
if (use_optional_locks())
13621368
fd = hold_locked_index(&index_lock, 0);

cache.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ void validate_cache_entries(const struct index_state *istate);
410410

411411
#define read_cache() read_index(&the_index)
412412
#define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
413-
#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
413+
#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
414414
#define is_cache_unborn() is_index_unborn(&the_index)
415415
#define read_cache_unmerged() read_index_unmerged(&the_index)
416416
#define discard_cache() discard_index(&the_index)
@@ -659,7 +659,9 @@ extern int daemonize(void);
659659
/* Initialize and use the cache information */
660660
struct lock_file;
661661
extern int read_index(struct index_state *);
662-
extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
662+
extern int read_index_preload(struct index_state *,
663+
const struct pathspec *pathspec,
664+
unsigned int refresh_flags);
663665
extern int do_read_index(struct index_state *istate, const char *path,
664666
int must_exist); /* for testting only! */
665667
extern int read_index_from(struct index_state *, const char *path,
@@ -814,6 +816,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
814816
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
815817
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
816818
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
819+
#define REFRESH_PROGRESS 0x0040 /* show progress bar if stderr is tty */
817820
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
818821
extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
819822

preload-index.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
#include "pathspec.h"
66
#include "dir.h"
77
#include "fsmonitor.h"
8+
#include "progress.h"
89

910
#ifdef NO_PTHREADS
1011
static void preload_index(struct index_state *index,
11-
const struct pathspec *pathspec)
12+
const struct pathspec *pathspec,
13+
unsigned int refresh_flags)
1214
{
1315
; /* nothing */
1416
}
@@ -25,16 +27,23 @@ static void preload_index(struct index_state *index,
2527
#define MAX_PARALLEL (20)
2628
#define THREAD_COST (500)
2729

30+
struct progress_data {
31+
unsigned long n;
32+
struct progress *progress;
33+
pthread_mutex_t mutex;
34+
};
35+
2836
struct thread_data {
2937
pthread_t pthread;
3038
struct index_state *index;
3139
struct pathspec pathspec;
40+
struct progress_data *progress;
3241
int offset, nr;
3342
};
3443

3544
static void *preload_thread(void *_data)
3645
{
37-
int nr;
46+
int nr, last_nr;
3847
struct thread_data *p = _data;
3948
struct index_state *index = p->index;
4049
struct cache_entry **cep = index->cache + p->offset;
@@ -43,6 +52,7 @@ static void *preload_thread(void *_data)
4352
nr = p->nr;
4453
if (nr + p->offset > index->cache_nr)
4554
nr = index->cache_nr - p->offset;
55+
last_nr = nr;
4656

4757
do {
4858
struct cache_entry *ce = *cep++;
@@ -58,6 +68,15 @@ static void *preload_thread(void *_data)
5868
continue;
5969
if (ce->ce_flags & CE_FSMONITOR_VALID)
6070
continue;
71+
if (p->progress && !(nr & 31)) {
72+
struct progress_data *pd = p->progress;
73+
74+
pthread_mutex_lock(&pd->mutex);
75+
pd->n += last_nr - nr;
76+
display_progress(pd->progress, pd->n);
77+
pthread_mutex_unlock(&pd->mutex);
78+
last_nr = nr;
79+
}
6180
if (!ce_path_match(index, ce, &p->pathspec, NULL))
6281
continue;
6382
if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
@@ -69,16 +88,25 @@ static void *preload_thread(void *_data)
6988
ce_mark_uptodate(ce);
7089
mark_fsmonitor_valid(ce);
7190
} while (--nr > 0);
91+
if (p->progress) {
92+
struct progress_data *pd = p->progress;
93+
94+
pthread_mutex_lock(&pd->mutex);
95+
display_progress(pd->progress, pd->n + last_nr);
96+
pthread_mutex_unlock(&pd->mutex);
97+
}
7298
cache_def_clear(&cache);
7399
return NULL;
74100
}
75101

76102
static void preload_index(struct index_state *index,
77-
const struct pathspec *pathspec)
103+
const struct pathspec *pathspec,
104+
unsigned int refresh_flags)
78105
{
79106
int threads, i, work, offset;
80107
struct thread_data data[MAX_PARALLEL];
81108
uint64_t start = getnanotime();
109+
struct progress_data pd;
82110

83111
if (!core_preload_index)
84112
return;
@@ -93,13 +121,22 @@ static void preload_index(struct index_state *index,
93121
offset = 0;
94122
work = DIV_ROUND_UP(index->cache_nr, threads);
95123
memset(&data, 0, sizeof(data));
124+
125+
memset(&pd, 0, sizeof(pd));
126+
if (refresh_flags & REFRESH_PROGRESS && isatty(2)) {
127+
pd.progress = start_delayed_progress(_("Refreshing index"), index->cache_nr);
128+
pthread_mutex_init(&pd.mutex, NULL);
129+
}
130+
96131
for (i = 0; i < threads; i++) {
97132
struct thread_data *p = data+i;
98133
p->index = index;
99134
if (pathspec)
100135
copy_pathspec(&p->pathspec, pathspec);
101136
p->offset = offset;
102137
p->nr = work;
138+
if (pd.progress)
139+
p->progress = &pd;
103140
offset += work;
104141
if (pthread_create(&p->pthread, NULL, preload_thread, p))
105142
die("unable to create threaded lstat");
@@ -109,15 +146,18 @@ static void preload_index(struct index_state *index,
109146
if (pthread_join(p->pthread, NULL))
110147
die("unable to join threaded lstat");
111148
}
149+
stop_progress(&pd.progress);
150+
112151
trace_performance_since(start, "preload index");
113152
}
114153
#endif
115154

116155
int read_index_preload(struct index_state *index,
117-
const struct pathspec *pathspec)
156+
const struct pathspec *pathspec,
157+
unsigned int refresh_flags)
118158
{
119159
int retval = read_index(index);
120160

121-
preload_index(index, pathspec);
161+
preload_index(index, pathspec, refresh_flags);
122162
return retval;
123163
}

read-cache.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "split-index.h"
2424
#include "utf8.h"
2525
#include "fsmonitor.h"
26+
#include "progress.h"
2627

2728
/* Mask for the name length in ce_flags in the on-disk index */
2829

@@ -1477,6 +1478,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,
14771478
const char *added_fmt;
14781479
const char *unmerged_fmt;
14791480
uint64_t start = getnanotime();
1481+
struct progress *progress = NULL;
1482+
1483+
if (flags & REFRESH_PROGRESS && isatty(2))
1484+
progress = start_delayed_progress(_("Refresh index"),
1485+
istate->cache_nr);
14801486

14811487
modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
14821488
deleted_fmt = (in_porcelain ? "D\t%s\n" : "%s: needs update\n");
@@ -1516,6 +1522,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
15161522
new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
15171523
if (new_entry == ce)
15181524
continue;
1525+
if (progress)
1526+
display_progress(progress, i);
15191527
if (!new_entry) {
15201528
const char *fmt;
15211529

@@ -1547,6 +1555,10 @@ int refresh_index(struct index_state *istate, unsigned int flags,
15471555

15481556
replace_index_entry(istate, i, new_entry);
15491557
}
1558+
if (progress) {
1559+
display_progress(progress, istate->cache_nr);
1560+
stop_progress(&progress);
1561+
}
15501562
trace_performance_since(start, "refresh index");
15511563
return has_errors;
15521564
}

sequencer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1909,7 +1909,7 @@ static int read_and_refresh_cache(struct replay_opts *opts)
19091909
{
19101910
struct lock_file index_lock = LOCK_INIT;
19111911
int index_fd = hold_locked_index(&index_lock, 0);
1912-
if (read_index_preload(&the_index, NULL) < 0) {
1912+
if (read_index_preload(&the_index, NULL, 0) < 0) {
19131913
rollback_lock_file(&index_lock);
19141914
return error(_("git %s: failed to read the index"),
19151915
_(action_name(opts)));

0 commit comments

Comments
 (0)