22
33#include "builtin.h"
44#include "config.h"
5+ #include "object.h"
6+ #include "object-store-ll.h"
57#include "parse-options.h"
8+ #include "progress.h"
9+ #include "ref-filter.h"
10+ #include "strvec.h"
11+ #include "trace2.h"
612
713static const char * const survey_usage [] = {
814 N_ ("(EXPERIMENTAL!) git survey <options>" ),
915 NULL ,
1016};
1117
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+
1232struct survey_opts {
1333 int verbose ;
1434 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 ;
1554};
1655
1756struct survey_context {
1857 struct repository * repo ;
1958
2059 /* Options that control what is done. */
2160 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 ;
2276};
2377
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+
24139static int survey_load_config_cb (const char * var , const char * value ,
25140 const struct config_context * cctx , void * pvoid )
26141{
@@ -43,18 +158,145 @@ static void survey_load_config(struct survey_context *ctx)
43158 git_config (survey_load_config_cb , ctx );
44159}
45160
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+
46270int cmd_survey (int argc , const char * * argv , const char * prefix , struct repository * repo )
47271{
48272 static struct survey_context ctx = {
49273 .opts = {
50274 .verbose = 0 ,
51275 .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 ,
52284 },
285+ .refs = STRVEC_INIT ,
53286 };
54287
55288 static struct option survey_options [] = {
56289 OPT__VERBOSE (& ctx .opts .verbose , N_ ("verbose output" )),
57290 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+
58300 OPT_END (),
59301 };
60302
@@ -71,5 +313,10 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
71313 if (ctx .opts .show_progress < 0 )
72314 ctx .opts .show_progress = isatty (2 );
73315
316+ fixup_refs_wanted (& ctx );
317+
318+ survey_phase_refs (& ctx );
319+
320+ clear_survey_context (& ctx );
74321 return 0 ;
75322}
0 commit comments