5 #include "parse-options.h"
6 #include "commit-graph.h"
8 static char const * const builtin_commit_graph_usage[] = {
9 N_("git commit-graph [--object-dir <objdir>]"),
10 N_("git commit-graph read [--object-dir <objdir>] [--file=<hash>]"),
11 N_("git commit-graph write [--object-dir <objdir>] [--set-latest] [--delete-expired] [--stdin-packs|--stdin-commits]"),
15 static const char * const builtin_commit_graph_read_usage[] = {
16 N_("git commit-graph read [--object-dir <objdir>] [--file=<hash>]"),
20 static const char * const builtin_commit_graph_write_usage[] = {
21 N_("git commit-graph write [--object-dir <objdir>] [--set-latest] [--delete-expired] [--stdin-packs|--stdin-commits]"),
25 static struct opts_commit_graph {
27 const char *graph_file;
34 static int graph_read(int argc, const char **argv)
36 struct commit_graph *graph = 0;
37 struct strbuf full_path = STRBUF_INIT;
39 static struct option builtin_commit_graph_read_options[] = {
40 { OPTION_STRING, 'o', "object-dir", &opts.obj_dir,
42 N_("The object directory to store the graph") },
43 { OPTION_STRING, 'H', "file", &opts.graph_file,
45 N_("The filename for a specific commit graph file in the object directory."),
46 PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
50 argc = parse_options(argc, argv, NULL,
51 builtin_commit_graph_read_options,
52 builtin_commit_graph_read_usage, 0);
55 opts.obj_dir = get_object_directory();
58 die("no graph hash specified");
60 strbuf_addf(&full_path, "%s/info/%s", opts.obj_dir, opts.graph_file);
61 graph = load_commit_graph_one(full_path.buf);
64 die("graph file %s does not exist", full_path.buf);
66 printf("header: %08x %d %d %d %d\n",
67 ntohl(*(uint32_t*)graph->data),
68 *(unsigned char*)(graph->data + 4),
69 *(unsigned char*)(graph->data + 5),
70 *(unsigned char*)(graph->data + 6),
71 *(unsigned char*)(graph->data + 7));
72 printf("num_commits: %u\n", graph->num_commits);
75 if (graph->chunk_oid_fanout)
76 printf(" oid_fanout");
77 if (graph->chunk_oid_lookup)
78 printf(" oid_lookup");
79 if (graph->chunk_commit_data)
80 printf(" commit_metadata");
81 if (graph->chunk_large_edges)
82 printf(" large_edges");
88 static void set_latest_file(const char *obj_dir, const char *graph_file)
91 struct lock_file lk = LOCK_INIT;
92 char *latest_fname = get_graph_latest_filename(obj_dir);
94 fd = hold_lock_file_for_update(&lk, latest_fname, LOCK_DIE_ON_ERROR);
95 FREE_AND_NULL(latest_fname);
98 die_errno("unable to open graph-head");
100 write_in_full(fd, graph_file, strlen(graph_file));
101 commit_lock_file(&lk);
105 * To avoid race conditions and deleting graph files that are being
106 * used by other processes, look inside a pack directory for all files
107 * of the form "graph-<hash>.graph" that do not match the old or new
108 * graph hashes and delete them.
110 static void do_delete_expired(const char *obj_dir,
111 const char *old_graph_name,
112 const char *new_graph_name)
117 struct strbuf path = STRBUF_INIT;
119 strbuf_addf(&path, "%s/info", obj_dir);
120 dir = opendir(path.buf);
123 error_errno("unable to open object pack directory: %s",
128 strbuf_addch(&path, '/');
129 dirnamelen = path.len;
130 while ((de = readdir(dir)) != NULL) {
133 if (is_dot_or_dotdot(de->d_name))
136 strbuf_setlen(&path, dirnamelen);
137 strbuf_addstr(&path, de->d_name);
140 if (strip_suffix_mem(path.buf, &base_len, ".graph") &&
141 strcmp(new_graph_name, de->d_name) &&
142 (!old_graph_name || strcmp(old_graph_name, de->d_name)) &&
143 remove_path(path.buf))
144 die("failed to remove path %s", path.buf);
147 strbuf_release(&path);
150 static int graph_write(int argc, const char **argv)
153 char *old_graph_name;
154 const char **pack_indexes = NULL;
156 const char **commit_hex = NULL;
158 const char **lines = NULL;
162 static struct option builtin_commit_graph_write_options[] = {
163 { OPTION_STRING, 'o', "object-dir", &opts.obj_dir,
165 N_("The object directory to store the graph") },
166 OPT_BOOL('u', "set-latest", &opts.set_latest,
167 N_("update graph-head to written graph file")),
168 OPT_BOOL('d', "delete-expired", &opts.delete_expired,
169 N_("delete expired head graph file")),
170 OPT_BOOL('s', "stdin-packs", &opts.stdin_packs,
171 N_("only scan packfiles listed by stdin")),
172 OPT_BOOL('C', "stdin-commits", &opts.stdin_commits,
173 N_("start walk at commits listed by stdin")),
177 argc = parse_options(argc, argv, NULL,
178 builtin_commit_graph_write_options,
179 builtin_commit_graph_write_usage, 0);
181 if (opts.stdin_packs && opts.stdin_commits)
182 die(_("cannot use both --stdin-commits and --stdin-packs"));
184 opts.obj_dir = get_object_directory();
186 old_graph_name = get_graph_latest_contents(opts.obj_dir);
188 if (opts.stdin_packs || opts.stdin_commits) {
189 struct strbuf buf = STRBUF_INIT;
192 ALLOC_ARRAY(lines, alloc_lines);
194 while (strbuf_getline(&buf, stdin) != EOF) {
195 ALLOC_GROW(lines, nr_lines + 1, alloc_lines);
196 lines[nr_lines++] = buf.buf;
197 strbuf_detach(&buf, NULL);
200 if (opts.stdin_packs) {
201 pack_indexes = lines;
204 if (opts.stdin_commits) {
206 nr_commits = nr_lines;
210 graph_name = write_commit_graph(opts.obj_dir,
218 set_latest_file(opts.obj_dir, graph_name);
220 if (opts.delete_expired)
221 do_delete_expired(opts.obj_dir,
225 printf("%s\n", graph_name);
226 FREE_AND_NULL(graph_name);
232 int cmd_commit_graph(int argc, const char **argv, const char *prefix)
234 static struct option builtin_commit_graph_options[] = {
235 { OPTION_STRING, 'o', "object-dir", &opts.obj_dir,
237 N_("The object directory to store the graph") },
241 if (argc == 2 && !strcmp(argv[1], "-h"))
242 usage_with_options(builtin_commit_graph_usage,
243 builtin_commit_graph_options);
245 git_config(git_default_config, NULL);
246 argc = parse_options(argc, argv, prefix,
247 builtin_commit_graph_options,
248 builtin_commit_graph_usage,
249 PARSE_OPT_STOP_AT_NON_OPTION);
252 if (!strcmp(argv[0], "read"))
253 return graph_read(argc, argv);
254 if (!strcmp(argv[0], "write"))
255 return graph_write(argc, argv);
258 usage_with_options(builtin_commit_graph_usage,
259 builtin_commit_graph_options);