22
33#include "builtin.h"
44#include "config.h"
5+ #include "object.h"
6+ #include "odb.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,146 @@ static void survey_load_config(struct survey_context *ctx)
43158 repo_config (the_repository , 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 (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 (odb_read_object_info (ctx -> repo -> objects ,
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+
46271int cmd_survey (int argc , const char * * argv , const char * prefix , struct repository * repo )
47272{
48273 static struct survey_context ctx = {
49274 .opts = {
50275 .verbose = 0 ,
51276 .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 ,
52285 },
286+ .refs = STRVEC_INIT ,
53287 };
54288
55289 static struct option survey_options [] = {
56290 OPT__VERBOSE (& ctx .opts .verbose , N_ ("verbose output" )),
57291 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+
58301 OPT_END (),
59302 };
60303
@@ -71,5 +314,10 @@ int cmd_survey(int argc, const char **argv, const char *prefix, struct repositor
71314 if (ctx .opts .show_progress < 0 )
72315 ctx .opts .show_progress = isatty (2 );
73316
317+ fixup_refs_wanted (& ctx );
318+
319+ survey_phase_refs (& ctx );
320+
321+ clear_survey_context (& ctx );
74322 return 0 ;
75323}
0 commit comments