builtin/commit-graph.c: dereference tags in builtin
[git] / builtin / commit-graph.c
1 #include "builtin.h"
2 #include "config.h"
3 #include "dir.h"
4 #include "lockfile.h"
5 #include "parse-options.h"
6 #include "repository.h"
7 #include "commit-graph.h"
8 #include "object-store.h"
9 #include "progress.h"
10
11 static char const * const builtin_commit_graph_usage[] = {
12         N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
13         N_("git commit-graph write [--object-dir <objdir>] [--append] "
14            "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
15            "[--changed-paths] [--[no-]progress] <split options>"),
16         NULL
17 };
18
19 static const char * const builtin_commit_graph_verify_usage[] = {
20         N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
21         NULL
22 };
23
24 static const char * const builtin_commit_graph_write_usage[] = {
25         N_("git commit-graph write [--object-dir <objdir>] [--append] "
26            "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] "
27            "[--changed-paths] [--[no-]progress] <split options>"),
28         NULL
29 };
30
31 static struct opts_commit_graph {
32         const char *obj_dir;
33         int reachable;
34         int stdin_packs;
35         int stdin_commits;
36         int append;
37         int split;
38         int shallow;
39         int progress;
40         int enable_changed_paths;
41 } opts;
42
43 static struct object_directory *find_odb(struct repository *r,
44                                          const char *obj_dir)
45 {
46         struct object_directory *odb;
47         char *obj_dir_real = real_pathdup(obj_dir, 1);
48         struct strbuf odb_path_real = STRBUF_INIT;
49
50         prepare_alt_odb(r);
51         for (odb = r->objects->odb; odb; odb = odb->next) {
52                 strbuf_realpath(&odb_path_real, odb->path, 1);
53                 if (!strcmp(obj_dir_real, odb_path_real.buf))
54                         break;
55         }
56
57         free(obj_dir_real);
58         strbuf_release(&odb_path_real);
59
60         if (!odb)
61                 die(_("could not find object directory matching %s"), obj_dir);
62         return odb;
63 }
64
65 static int graph_verify(int argc, const char **argv)
66 {
67         struct commit_graph *graph = NULL;
68         struct object_directory *odb = NULL;
69         char *graph_name;
70         int open_ok;
71         int fd;
72         struct stat st;
73         int flags = 0;
74
75         static struct option builtin_commit_graph_verify_options[] = {
76                 OPT_STRING(0, "object-dir", &opts.obj_dir,
77                            N_("dir"),
78                            N_("The object directory to store the graph")),
79                 OPT_BOOL(0, "shallow", &opts.shallow,
80                          N_("if the commit-graph is split, only verify the tip file")),
81                 OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
82                 OPT_END(),
83         };
84
85         trace2_cmd_mode("verify");
86
87         opts.progress = isatty(2);
88         argc = parse_options(argc, argv, NULL,
89                              builtin_commit_graph_verify_options,
90                              builtin_commit_graph_verify_usage, 0);
91
92         if (!opts.obj_dir)
93                 opts.obj_dir = get_object_directory();
94         if (opts.shallow)
95                 flags |= COMMIT_GRAPH_VERIFY_SHALLOW;
96         if (opts.progress)
97                 flags |= COMMIT_GRAPH_WRITE_PROGRESS;
98
99         odb = find_odb(the_repository, opts.obj_dir);
100         graph_name = get_commit_graph_filename(odb);
101         open_ok = open_commit_graph(graph_name, &fd, &st);
102         if (!open_ok && errno != ENOENT)
103                 die_errno(_("Could not open commit-graph '%s'"), graph_name);
104
105         FREE_AND_NULL(graph_name);
106
107         if (open_ok)
108                 graph = load_commit_graph_one_fd_st(fd, &st, odb);
109         else
110                 graph = read_commit_graph_one(the_repository, odb);
111
112         /* Return failure if open_ok predicted success */
113         if (!graph)
114                 return !!open_ok;
115
116         UNLEAK(graph);
117         return verify_commit_graph(the_repository, graph, flags);
118 }
119
120 extern int read_replace_refs;
121 static struct split_commit_graph_opts split_opts;
122
123 static int write_option_parse_split(const struct option *opt, const char *arg,
124                                     int unset)
125 {
126         enum commit_graph_split_flags *flags = opt->value;
127
128         opts.split = 1;
129         if (!arg)
130                 return 0;
131
132         if (!strcmp(arg, "no-merge"))
133                 *flags = COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED;
134         else if (!strcmp(arg, "replace"))
135                 *flags = COMMIT_GRAPH_SPLIT_REPLACE;
136         else
137                 die(_("unrecognized --split argument, %s"), arg);
138
139         return 0;
140 }
141
142 static int read_one_commit(struct oidset *commits, struct progress *progress,
143                            const char *hash)
144 {
145         struct commit *result;
146         struct object_id oid;
147         const char *end;
148
149         if (parse_oid_hex(hash, &oid, &end))
150                 return error(_("unexpected non-hex object ID: %s"), hash);
151
152         result = lookup_commit_reference_gently(the_repository, &oid, 1);
153         if (result)
154                 oidset_insert(commits, &result->object.oid);
155         else
156                 return error(_("invalid commit object id: %s"), hash);
157
158         display_progress(progress, oidset_size(commits));
159
160         return 0;
161 }
162
163 static int graph_write(int argc, const char **argv)
164 {
165         struct string_list pack_indexes = STRING_LIST_INIT_NODUP;
166         struct strbuf buf = STRBUF_INIT;
167         struct oidset commits = OIDSET_INIT;
168         struct object_directory *odb = NULL;
169         int result = 0;
170         enum commit_graph_write_flags flags = 0;
171         struct progress *progress = NULL;
172
173         static struct option builtin_commit_graph_write_options[] = {
174                 OPT_STRING(0, "object-dir", &opts.obj_dir,
175                         N_("dir"),
176                         N_("The object directory to store the graph")),
177                 OPT_BOOL(0, "reachable", &opts.reachable,
178                         N_("start walk at all refs")),
179                 OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
180                         N_("scan pack-indexes listed by stdin for commits")),
181                 OPT_BOOL(0, "stdin-commits", &opts.stdin_commits,
182                         N_("start walk at commits listed by stdin")),
183                 OPT_BOOL(0, "append", &opts.append,
184                         N_("include all commits already in the commit-graph file")),
185                 OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths,
186                         N_("enable computation for changed paths")),
187                 OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
188                 OPT_CALLBACK_F(0, "split", &split_opts.flags, NULL,
189                         N_("allow writing an incremental commit-graph file"),
190                         PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
191                         write_option_parse_split),
192                 OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
193                         N_("maximum number of commits in a non-base split commit-graph")),
194                 OPT_INTEGER(0, "size-multiple", &split_opts.size_multiple,
195                         N_("maximum ratio between two levels of a split commit-graph")),
196                 OPT_EXPIRY_DATE(0, "expire-time", &split_opts.expire_time,
197                         N_("only expire files older than a given date-time")),
198                 OPT_END(),
199         };
200
201         opts.progress = isatty(2);
202         split_opts.size_multiple = 2;
203         split_opts.max_commits = 0;
204         split_opts.expire_time = 0;
205
206         trace2_cmd_mode("write");
207
208         argc = parse_options(argc, argv, NULL,
209                              builtin_commit_graph_write_options,
210                              builtin_commit_graph_write_usage, 0);
211
212         if (opts.reachable + opts.stdin_packs + opts.stdin_commits > 1)
213                 die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
214         if (!opts.obj_dir)
215                 opts.obj_dir = get_object_directory();
216         if (opts.append)
217                 flags |= COMMIT_GRAPH_WRITE_APPEND;
218         if (opts.split)
219                 flags |= COMMIT_GRAPH_WRITE_SPLIT;
220         if (opts.progress)
221                 flags |= COMMIT_GRAPH_WRITE_PROGRESS;
222         if (opts.enable_changed_paths ||
223             git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
224                 flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
225
226         read_replace_refs = 0;
227         odb = find_odb(the_repository, opts.obj_dir);
228
229         if (opts.reachable) {
230                 if (write_commit_graph_reachable(odb, flags, &split_opts))
231                         return 1;
232                 return 0;
233         }
234
235         if (opts.stdin_packs) {
236                 while (strbuf_getline(&buf, stdin) != EOF)
237                         string_list_append(&pack_indexes,
238                                            strbuf_detach(&buf, NULL));
239         } else if (opts.stdin_commits) {
240                 oidset_init(&commits, 0);
241                 flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS;
242                 if (opts.progress)
243                         progress = start_delayed_progress(
244                                 _("Collecting commits from input"), 0);
245
246                 while (strbuf_getline(&buf, stdin) != EOF) {
247                         if (read_one_commit(&commits, progress, buf.buf)) {
248                                 result = 1;
249                                 goto cleanup;
250                         }
251                 }
252
253
254         }
255
256         if (write_commit_graph(odb,
257                                opts.stdin_packs ? &pack_indexes : NULL,
258                                opts.stdin_commits ? &commits : NULL,
259                                flags,
260                                &split_opts))
261                 result = 1;
262
263 cleanup:
264         string_list_clear(&pack_indexes, 0);
265         strbuf_release(&buf);
266         if (progress)
267                 stop_progress(&progress);
268         return result;
269 }
270
271 int cmd_commit_graph(int argc, const char **argv, const char *prefix)
272 {
273         static struct option builtin_commit_graph_options[] = {
274                 OPT_STRING(0, "object-dir", &opts.obj_dir,
275                         N_("dir"),
276                         N_("The object directory to store the graph")),
277                 OPT_END(),
278         };
279
280         if (argc == 2 && !strcmp(argv[1], "-h"))
281                 usage_with_options(builtin_commit_graph_usage,
282                                    builtin_commit_graph_options);
283
284         git_config(git_default_config, NULL);
285         argc = parse_options(argc, argv, prefix,
286                              builtin_commit_graph_options,
287                              builtin_commit_graph_usage,
288                              PARSE_OPT_STOP_AT_NON_OPTION);
289
290         save_commit_buffer = 0;
291
292         if (argc > 0) {
293                 if (!strcmp(argv[0], "verify"))
294                         return graph_verify(argc, argv);
295                 if (!strcmp(argv[0], "write"))
296                         return graph_write(argc, argv);
297         }
298
299         usage_with_options(builtin_commit_graph_usage,
300                            builtin_commit_graph_options);
301 }