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 <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
Sep 15, 2018 at 19:56 UTC
ae9af12287b2c37512f12c137173dde7ea5192a0
6 files changed
+72
-11
builtin/am.c
+1
-1
@@ -2324,7 +2324,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
2324
/* Ensure a valid committer ident can be constructed */
2325
git_committer_info(IDENT_STRICT);
2326
2327
- if (read_index_preload(&the_index, NULL) < 0)
2327
+ if (read_index_preload(&the_index, NULL, 0) < 0)
2328
die(_("failed to read the index"));
2329
2330
if (in_progress) {
builtin/commit.c
+8
-2
@@ -1295,6 +1295,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
1295
static int no_renames = -1;
1296
static const char *rename_score_arg = (const char *)-1;
1297
static struct wt_status s;
1298
+ unsigned int progress_flag = 0;
1299
int fd;
1300
struct object_id oid;
1301
static struct option builtin_status_options[] = {
@@ -1355,8 +1356,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)
1356
PATHSPEC_PREFER_FULL,
1357
prefix, argv);
1358
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);
1366
1367
if (use_optional_locks())
1368
fd = hold_locked_index(&index_lock, 0);
cache.h
+5
-2
@@ -410,7 +410,7 @@ void validate_cache_entries(const struct index_state *istate);
410
411
#define read_cache() read_index(&the_index)
412
#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)
414
#define is_cache_unborn() is_index_unborn(&the_index)
415
#define read_cache_unmerged() read_index_unmerged(&the_index)
416
#define discard_cache() discard_index(&the_index)
@@ -659,7 +659,9 @@ extern int daemonize(void);
659
/* Initialize and use the cache information */
660
struct lock_file;
661
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);
665
extern int do_read_index(struct index_state *istate, const char *path,
666
int must_exist); /* for testting only! */
667
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);
816
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
817
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
818
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
819
+#define REFRESH_PROGRESS 0x0040 /* show progress bar if stderr is tty */
820
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
821
extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
822
preload-index.c
+45
-5
@@ -5,10 +5,12 @@
5
#include "pathspec.h"
6
#include "dir.h"
7
#include "fsmonitor.h"
8
+#include "progress.h"
9
10
#ifdef NO_PTHREADS
11
static void preload_index(struct index_state *index,
11
- const struct pathspec *pathspec)
12
+ const struct pathspec *pathspec,
13
+ unsigned int refresh_flags)
14
{
15
; /* nothing */
16
}
@@ -25,16 +27,23 @@ static void preload_index(struct index_state *index,
27
#define MAX_PARALLEL (20)
28
#define THREAD_COST (500)
29
30
+struct progress_data {
31
+ unsigned long n;
32
+ struct progress *progress;
33
+ pthread_mutex_t mutex;
34
+};
35
+
36
struct thread_data {
37
pthread_t pthread;
38
struct index_state *index;
39
struct pathspec pathspec;
40
+ struct progress_data *progress;
41
int offset, nr;
42
};
43
44
static void *preload_thread(void *_data)
45
{
37
- int nr;
46
+ int nr, last_nr;
47
struct thread_data *p = _data;
48
struct index_state *index = p->index;
49
struct cache_entry **cep = index->cache + p->offset;
@@ -43,6 +52,7 @@ static void *preload_thread(void *_data)
52
nr = p->nr;
53
if (nr + p->offset > index->cache_nr)
54
nr = index->cache_nr - p->offset;
55
+ last_nr = nr;
56
57
do {
58
struct cache_entry *ce = *cep++;
@@ -58,6 +68,15 @@ static void *preload_thread(void *_data)
68
continue;
69
if (ce->ce_flags & CE_FSMONITOR_VALID)
70
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
+ }
80
if (!ce_path_match(index, ce, &p->pathspec, NULL))
81
continue;
82
if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
@@ -69,16 +88,25 @@ static void *preload_thread(void *_data)
88
ce_mark_uptodate(ce);
89
mark_fsmonitor_valid(ce);
90
} 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
+ }
98
cache_def_clear(&cache);
99
return NULL;
100
}
101
102
static void preload_index(struct index_state *index,
77
- const struct pathspec *pathspec)
103
+ const struct pathspec *pathspec,
104
+ unsigned int refresh_flags)
105
{
106
int threads, i, work, offset;
107
struct thread_data data[MAX_PARALLEL];
108
uint64_t start = getnanotime();
109
+ struct progress_data pd;
110
111
if (!core_preload_index)
112
return;
@@ -93,6 +121,13 @@ static void preload_index(struct index_state *index,
121
offset = 0;
122
work = DIV_ROUND_UP(index->cache_nr, threads);
123
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
+
131
for (i = 0; i < threads; i++) {
132
struct thread_data *p = data+i;
133
p->index = index;
@@ -100,6 +135,8 @@ static void preload_index(struct index_state *index,
135
copy_pathspec(&p->pathspec, pathspec);
136
p->offset = offset;
137
p->nr = work;
138
+ if (pd.progress)
139
+ p->progress = &pd;
140
offset += work;
141
if (pthread_create(&p->pthread, NULL, preload_thread, p))
142
die("unable to create threaded lstat");
@@ -109,15 +146,18 @@ static void preload_index(struct index_state *index,
146
if (pthread_join(p->pthread, NULL))
147
die("unable to join threaded lstat");
148
}
149
+ stop_progress(&pd.progress);
150
+
151
trace_performance_since(start, "preload index");
152
}
153
#endif
154
155
int read_index_preload(struct index_state *index,
117
- const struct pathspec *pathspec)
156
+ const struct pathspec *pathspec,
157
+ unsigned int refresh_flags)
158
{
159
int retval = read_index(index);
160
121
- preload_index(index, pathspec);
161
+ preload_index(index, pathspec, refresh_flags);
162
return retval;
163
}
read-cache.c
+12
@@ -23,6 +23,7 @@
23
#include "split-index.h"
24
#include "utf8.h"
25
#include "fsmonitor.h"
26
+#include "progress.h"
27
28
/* Mask for the name length in ce_flags in the on-disk index */
29
@@ -1477,6 +1478,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,
1478
const char *added_fmt;
1479
const char *unmerged_fmt;
1480
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);
1486
1487
modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
1488
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,
1522
new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
1523
if (new_entry == ce)
1524
continue;
1525
+ if (progress)
1526
+ display_progress(progress, i);
1527
if (!new_entry) {
1528
const char *fmt;
1529
@@ -1547,6 +1555,10 @@ int refresh_index(struct index_state *istate, unsigned int flags,
1555
1556
replace_index_entry(istate, i, new_entry);
1557
}
1558
+ if (progress) {
1559
+ display_progress(progress, istate->cache_nr);
1560
+ stop_progress(&progress);
1561
+ }
1562
trace_performance_since(start, "refresh index");
1563
return has_errors;
1564
}
sequencer.c
+1
-1
@@ -1909,7 +1909,7 @@ static int read_and_refresh_cache(struct replay_opts *opts)
1909
{
1910
struct lock_file index_lock = LOCK_INIT;
1911
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) {
1913
rollback_lock_file(&index_lock);
1914
return error(_("git %s: failed to read the index"),
1915
_(action_name(opts)));