2
2
3
3
#include "builtin.h"
4
4
#include "config.h"
5
+ #include "object.h"
6
+ #include "object-store-ll.h"
5
7
#include "parse-options.h"
8
+ #include "progress.h"
9
+ #include "ref-filter.h"
10
+ #include "strvec.h"
11
+ #include "trace2.h"
6
12
7
13
static const char * const survey_usage [] = {
8
14
N_ ("(EXPERIMENTAL!) git survey <options>" ),
9
15
NULL ,
10
16
};
11
17
18
+ struct survey_refs_wanted {
19
+ int want_all_refs ; /* special override */
20
+
21
+ int want_branches ;
22
+ int want_tags ;
23
+ int want_remotes ;
24
+ int want_detached ;
25
+ int want_other ; /* see FILTER_REFS_OTHERS -- refs/notes/, refs/stash/ */
26
+ };
27
+
28
+ static struct survey_refs_wanted default_ref_options = {
29
+ .want_all_refs = 1 ,
30
+ };
31
+
12
32
struct survey_opts {
13
33
int verbose ;
14
34
int show_progress ;
35
+ struct survey_refs_wanted refs ;
36
+ };
37
+
38
+ struct survey_report_ref_summary {
39
+ size_t refs_nr ;
40
+ size_t branches_nr ;
41
+ size_t remote_refs_nr ;
42
+ size_t tags_nr ;
43
+ size_t tags_annotated_nr ;
44
+ size_t others_nr ;
45
+ size_t unknown_nr ;
46
+ };
47
+
48
+ /**
49
+ * This struct contains all of the information that needs to be printed
50
+ * at the end of the exploration of the repository and its references.
51
+ */
52
+ struct survey_report {
53
+ struct survey_report_ref_summary refs ;
15
54
};
16
55
17
56
struct survey_context {
18
57
struct repository * repo ;
19
58
20
59
/* Options that control what is done. */
21
60
struct survey_opts opts ;
61
+
62
+ /* Info for output only. */
63
+ struct survey_report report ;
64
+
65
+ /*
66
+ * The rest of the members are about enabling the activity
67
+ * of the 'git survey' command, including ref listings, object
68
+ * pointers, and progress.
69
+ */
70
+
71
+ struct progress * progress ;
72
+ size_t progress_nr ;
73
+ size_t progress_total ;
74
+
75
+ struct strvec refs ;
22
76
};
23
77
78
+ static void clear_survey_context (struct survey_context * ctx )
79
+ {
80
+ strvec_clear (& ctx -> refs );
81
+ }
82
+
83
+ /*
84
+ * After parsing the command line arguments, figure out which refs we
85
+ * should scan.
86
+ *
87
+ * If ANY were given in positive sense, then we ONLY include them and
88
+ * do not use the builtin values.
89
+ */
90
+ static void fixup_refs_wanted (struct survey_context * ctx )
91
+ {
92
+ struct survey_refs_wanted * rw = & ctx -> opts .refs ;
93
+
94
+ /*
95
+ * `--all-refs` overrides and enables everything.
96
+ */
97
+ if (rw -> want_all_refs == 1 ) {
98
+ rw -> want_branches = 1 ;
99
+ rw -> want_tags = 1 ;
100
+ rw -> want_remotes = 1 ;
101
+ rw -> want_detached = 1 ;
102
+ rw -> want_other = 1 ;
103
+ return ;
104
+ }
105
+
106
+ /*
107
+ * If none of the `--<ref-type>` were given, we assume all
108
+ * of the builtin unspecified values.
109
+ */
110
+ if (rw -> want_branches == -1 &&
111
+ rw -> want_tags == -1 &&
112
+ rw -> want_remotes == -1 &&
113
+ rw -> want_detached == -1 &&
114
+ rw -> want_other == -1 ) {
115
+ * rw = default_ref_options ;
116
+ return ;
117
+ }
118
+
119
+ /*
120
+ * Since we only allow positive boolean values on the command
121
+ * line, we will only have true values where they specified
122
+ * a `--<ref-type>`.
123
+ *
124
+ * So anything that still has an unspecified value should be
125
+ * set to false.
126
+ */
127
+ if (rw -> want_branches == -1 )
128
+ rw -> want_branches = 0 ;
129
+ if (rw -> want_tags == -1 )
130
+ rw -> want_tags = 0 ;
131
+ if (rw -> want_remotes == -1 )
132
+ rw -> want_remotes = 0 ;
133
+ if (rw -> want_detached == -1 )
134
+ rw -> want_detached = 0 ;
135
+ if (rw -> want_other == -1 )
136
+ rw -> want_other = 0 ;
137
+ }
138
+
24
139
static int survey_load_config_cb (const char * var , const char * value ,
25
140
const struct config_context * cctx , void * pvoid )
26
141
{
@@ -43,18 +158,145 @@ static void survey_load_config(struct survey_context *ctx)
43
158
git_config (survey_load_config_cb , ctx );
44
159
}
45
160
161
+ static void do_load_refs (struct survey_context * ctx ,
162
+ struct ref_array * ref_array )
163
+ {
164
+ struct ref_filter filter = REF_FILTER_INIT ;
165
+ struct ref_sorting * sorting ;
166
+ struct string_list sorting_options = STRING_LIST_INIT_DUP ;
167
+
168
+ string_list_append (& sorting_options , "objectname" );
169
+ sorting = ref_sorting_options (& sorting_options );
170
+
171
+ if (ctx -> opts .refs .want_detached )
172
+ strvec_push (& ctx -> refs , "HEAD" );
173
+
174
+ if (ctx -> opts .refs .want_all_refs ) {
175
+ strvec_push (& ctx -> refs , "refs/" );
176
+ } else {
177
+ if (ctx -> opts .refs .want_branches )
178
+ strvec_push (& ctx -> refs , "refs/heads/" );
179
+ if (ctx -> opts .refs .want_tags )
180
+ strvec_push (& ctx -> refs , "refs/tags/" );
181
+ if (ctx -> opts .refs .want_remotes )
182
+ strvec_push (& ctx -> refs , "refs/remotes/" );
183
+ if (ctx -> opts .refs .want_other ) {
184
+ strvec_push (& ctx -> refs , "refs/notes/" );
185
+ strvec_push (& ctx -> refs , "refs/stash/" );
186
+ }
187
+ }
188
+
189
+ filter .name_patterns = ctx -> refs .v ;
190
+ filter .ignore_case = 0 ;
191
+ filter .match_as_path = 1 ;
192
+
193
+ if (ctx -> opts .show_progress ) {
194
+ ctx -> progress_total = 0 ;
195
+ ctx -> progress = start_progress (_ ("Scanning refs..." ), 0 );
196
+ }
197
+
198
+ filter_refs (ref_array , & filter , FILTER_REFS_KIND_MASK );
199
+
200
+ if (ctx -> opts .show_progress ) {
201
+ ctx -> progress_total = ref_array -> nr ;
202
+ display_progress (ctx -> progress , ctx -> progress_total );
203
+ }
204
+
205
+ ref_array_sort (sorting , ref_array );
206
+
207
+ stop_progress (& ctx -> progress );
208
+ ref_filter_clear (& filter );
209
+ ref_sorting_release (sorting );
210
+ }
211
+
212
+ /*
213
+ * The REFS phase:
214
+ *
215
+ * Load the set of requested refs and assess them for scalablity problems.
216
+ * Use that set to start a treewalk to all reachable objects and assess
217
+ * them.
218
+ *
219
+ * This data will give us insights into the repository itself (the number
220
+ * of refs, the size and shape of the DAG, the number and size of the
221
+ * objects).
222
+ *
223
+ * Theoretically, this data is independent of the on-disk representation
224
+ * (e.g. independent of packing concerns).
225
+ */
226
+ static void survey_phase_refs (struct survey_context * ctx )
227
+ {
228
+ struct ref_array ref_array = { 0 };
229
+
230
+ trace2_region_enter ("survey" , "phase/refs" , ctx -> repo );
231
+ do_load_refs (ctx , & ref_array );
232
+
233
+ ctx -> report .refs .refs_nr = ref_array .nr ;
234
+ for (int i = 0 ; i < ref_array .nr ; i ++ ) {
235
+ unsigned long size ;
236
+ struct ref_array_item * item = ref_array .items [i ];
237
+
238
+ switch (item -> kind ) {
239
+ case FILTER_REFS_TAGS :
240
+ ctx -> report .refs .tags_nr ++ ;
241
+ if (oid_object_info (ctx -> repo ,
242
+ & item -> objectname ,
243
+ & size ) == OBJ_TAG )
244
+ ctx -> report .refs .tags_annotated_nr ++ ;
245
+ break ;
246
+
247
+ case FILTER_REFS_BRANCHES :
248
+ ctx -> report .refs .branches_nr ++ ;
249
+ break ;
250
+
251
+ case FILTER_REFS_REMOTES :
252
+ ctx -> report .refs .remote_refs_nr ++ ;
253
+ break ;
254
+
255
+ case FILTER_REFS_OTHERS :
256
+ ctx -> report .refs .others_nr ++ ;
257
+ break ;
258
+
259
+ default :
260
+ ctx -> report .refs .unknown_nr ++ ;
261
+ break ;
262
+ }
263
+ }
264
+
265
+ trace2_region_leave ("survey" , "phase/refs" , ctx -> repo );
266
+
267
+ ref_array_clear (& ref_array );
268
+ }
269
+
46
270
int cmd_survey (int argc , const char * * argv , const char * prefix , struct repository * repo )
47
271
{
48
272
static struct survey_context ctx = {
49
273
.opts = {
50
274
.verbose = 0 ,
51
275
.show_progress = -1 , /* defaults to isatty(2) */
276
+
277
+ .refs .want_all_refs = -1 ,
278
+
279
+ .refs .want_branches = -1 , /* default these to undefined */
280
+ .refs .want_tags = -1 ,
281
+ .refs .want_remotes = -1 ,
282
+ .refs .want_detached = -1 ,
283
+ .refs .want_other = -1 ,
52
284
},
285
+ .refs = STRVEC_INIT ,
53
286
};
54
287
55
288
static struct option survey_options [] = {
56
289
OPT__VERBOSE (& ctx .opts .verbose , N_ ("verbose output" )),
57
290
OPT_BOOL (0 , "progress" , & ctx .opts .show_progress , N_ ("show progress" )),
291
+
292
+ OPT_BOOL_F (0 , "all-refs" , & ctx .opts .refs .want_all_refs , N_ ("include all refs" ), PARSE_OPT_NONEG ),
293
+
294
+ OPT_BOOL_F (0 , "branches" , & ctx .opts .refs .want_branches , N_ ("include branches" ), PARSE_OPT_NONEG ),
295
+ OPT_BOOL_F (0 , "tags" , & ctx .opts .refs .want_tags , N_ ("include tags" ), PARSE_OPT_NONEG ),
296
+ OPT_BOOL_F (0 , "remotes" , & ctx .opts .refs .want_remotes , N_ ("include all remotes refs" ), PARSE_OPT_NONEG ),
297
+ OPT_BOOL_F (0 , "detached" , & ctx .opts .refs .want_detached , N_ ("include detached HEAD" ), PARSE_OPT_NONEG ),
298
+ OPT_BOOL_F (0 , "other" , & ctx .opts .refs .want_other , N_ ("include notes and stashes" ), PARSE_OPT_NONEG ),
299
+
58
300
OPT_END (),
59
301
};
60
302
@@ -71,5 +313,10 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
71
313
if (ctx .opts .show_progress < 0 )
72
314
ctx .opts .show_progress = isatty (2 );
73
315
316
+ fixup_refs_wanted (& ctx );
317
+
318
+ survey_phase_refs (& ctx );
319
+
320
+ clear_survey_context (& ctx );
74
321
return 0 ;
75
322
}
0 commit comments