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,146 @@ 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 (ctx -> repo ,
196
+ _ ("Scanning refs..." ), 0 );
197
+ }
198
+
199
+ filter_refs (ref_array , & filter , FILTER_REFS_KIND_MASK );
200
+
201
+ if (ctx -> opts .show_progress ) {
202
+ ctx -> progress_total = ref_array -> nr ;
203
+ display_progress (ctx -> progress , ctx -> progress_total );
204
+ }
205
+
206
+ ref_array_sort (sorting , ref_array );
207
+
208
+ stop_progress (& ctx -> progress );
209
+ ref_filter_clear (& filter );
210
+ ref_sorting_release (sorting );
211
+ }
212
+
213
+ /*
214
+ * The REFS phase:
215
+ *
216
+ * Load the set of requested refs and assess them for scalablity problems.
217
+ * Use that set to start a treewalk to all reachable objects and assess
218
+ * them.
219
+ *
220
+ * This data will give us insights into the repository itself (the number
221
+ * of refs, the size and shape of the DAG, the number and size of the
222
+ * objects).
223
+ *
224
+ * Theoretically, this data is independent of the on-disk representation
225
+ * (e.g. independent of packing concerns).
226
+ */
227
+ static void survey_phase_refs (struct survey_context * ctx )
228
+ {
229
+ struct ref_array ref_array = { 0 };
230
+
231
+ trace2_region_enter ("survey" , "phase/refs" , ctx -> repo );
232
+ do_load_refs (ctx , & ref_array );
233
+
234
+ ctx -> report .refs .refs_nr = ref_array .nr ;
235
+ for (int i = 0 ; i < ref_array .nr ; i ++ ) {
236
+ unsigned long size ;
237
+ struct ref_array_item * item = ref_array .items [i ];
238
+
239
+ switch (item -> kind ) {
240
+ case FILTER_REFS_TAGS :
241
+ ctx -> report .refs .tags_nr ++ ;
242
+ if (oid_object_info (ctx -> repo ,
243
+ & item -> objectname ,
244
+ & size ) == OBJ_TAG )
245
+ ctx -> report .refs .tags_annotated_nr ++ ;
246
+ break ;
247
+
248
+ case FILTER_REFS_BRANCHES :
249
+ ctx -> report .refs .branches_nr ++ ;
250
+ break ;
251
+
252
+ case FILTER_REFS_REMOTES :
253
+ ctx -> report .refs .remote_refs_nr ++ ;
254
+ break ;
255
+
256
+ case FILTER_REFS_OTHERS :
257
+ ctx -> report .refs .others_nr ++ ;
258
+ break ;
259
+
260
+ default :
261
+ ctx -> report .refs .unknown_nr ++ ;
262
+ break ;
263
+ }
264
+ }
265
+
266
+ trace2_region_leave ("survey" , "phase/refs" , ctx -> repo );
267
+
268
+ ref_array_clear (& ref_array );
269
+ }
270
+
46
271
int cmd_survey (int argc , const char * * argv , const char * prefix , struct repository * repo )
47
272
{
48
273
static struct survey_context ctx = {
49
274
.opts = {
50
275
.verbose = 0 ,
51
276
.show_progress = -1 , /* defaults to isatty(2) */
277
+
278
+ .refs .want_all_refs = -1 ,
279
+
280
+ .refs .want_branches = -1 , /* default these to undefined */
281
+ .refs .want_tags = -1 ,
282
+ .refs .want_remotes = -1 ,
283
+ .refs .want_detached = -1 ,
284
+ .refs .want_other = -1 ,
52
285
},
286
+ .refs = STRVEC_INIT ,
53
287
};
54
288
55
289
static struct option survey_options [] = {
56
290
OPT__VERBOSE (& ctx .opts .verbose , N_ ("verbose output" )),
57
291
OPT_BOOL (0 , "progress" , & ctx .opts .show_progress , N_ ("show progress" )),
292
+
293
+ OPT_BOOL_F (0 , "all-refs" , & ctx .opts .refs .want_all_refs , N_ ("include all refs" ), PARSE_OPT_NONEG ),
294
+
295
+ OPT_BOOL_F (0 , "branches" , & ctx .opts .refs .want_branches , N_ ("include branches" ), PARSE_OPT_NONEG ),
296
+ OPT_BOOL_F (0 , "tags" , & ctx .opts .refs .want_tags , N_ ("include tags" ), PARSE_OPT_NONEG ),
297
+ OPT_BOOL_F (0 , "remotes" , & ctx .opts .refs .want_remotes , N_ ("include all remotes refs" ), PARSE_OPT_NONEG ),
298
+ OPT_BOOL_F (0 , "detached" , & ctx .opts .refs .want_detached , N_ ("include detached HEAD" ), PARSE_OPT_NONEG ),
299
+ OPT_BOOL_F (0 , "other" , & ctx .opts .refs .want_other , N_ ("include notes and stashes" ), PARSE_OPT_NONEG ),
300
+
58
301
OPT_END (),
59
302
};
60
303
@@ -71,5 +314,10 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
71
314
if (ctx .opts .show_progress < 0 )
72
315
ctx .opts .show_progress = isatty (2 );
73
316
317
+ fixup_refs_wanted (& ctx );
318
+
319
+ survey_phase_refs (& ctx );
320
+
321
+ clear_survey_context (& ctx );
74
322
return 0 ;
75
323
}
0 commit comments