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]"),
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]"),
25 static struct opts_commit_graph {
27 const char *graph_file;
33 static int graph_read(int argc, const char **argv)
35 struct commit_graph *graph = 0;
36 struct strbuf full_path = STRBUF_INIT;
38 static struct option builtin_commit_graph_read_options[] = {
39 { OPTION_STRING, 'o', "object-dir", &opts.obj_dir,
41 N_("The object directory to store the graph") },
42 { OPTION_STRING, 'H', "file", &opts.graph_file,
44 N_("The filename for a specific commit graph file in the object directory."),
45 PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
49 argc = parse_options(argc, argv, NULL,
50 builtin_commit_graph_read_options,
51 builtin_commit_graph_read_usage, 0);
54 opts.obj_dir = get_object_directory();
57 die("no graph hash specified");
59 strbuf_addf(&full_path, "%s/info/%s", opts.obj_dir, opts.graph_file);
60 graph = load_commit_graph_one(full_path.buf);
63 die("graph file %s does not exist", full_path.buf);
65 printf("header: %08x %d %d %d %d\n",
66 ntohl(*(uint32_t*)graph->data),
67 *(unsigned char*)(graph->data + 4),
68 *(unsigned char*)(graph->data + 5),
69 *(unsigned char*)(graph->data + 6),
70 *(unsigned char*)(graph->data + 7));
71 printf("num_commits: %u\n", graph->num_commits);
74 if (graph->chunk_oid_fanout)
75 printf(" oid_fanout");
76 if (graph->chunk_oid_lookup)
77 printf(" oid_lookup");
78 if (graph->chunk_commit_data)
79 printf(" commit_metadata");
80 if (graph->chunk_large_edges)
81 printf(" large_edges");
87 static void set_latest_file(const char *obj_dir, const char *graph_file)
90 struct lock_file lk = LOCK_INIT;
91 char *latest_fname = get_graph_latest_filename(obj_dir);
93 fd = hold_lock_file_for_update(&lk, latest_fname, LOCK_DIE_ON_ERROR);
94 FREE_AND_NULL(latest_fname);
97 die_errno("unable to open graph-head");
99 write_in_full(fd, graph_file, strlen(graph_file));
100 commit_lock_file(&lk);
104 * To avoid race conditions and deleting graph files that are being
105 * used by other processes, look inside a pack directory for all files
106 * of the form "graph-<hash>.graph" that do not match the old or new
107 * graph hashes and delete them.
109 static void do_delete_expired(const char *obj_dir,
110 const char *old_graph_name,
111 const char *new_graph_name)
116 struct strbuf path = STRBUF_INIT;
118 strbuf_addf(&path, "%s/info", obj_dir);
119 dir = opendir(path.buf);
122 error_errno("unable to open object pack directory: %s",
127 strbuf_addch(&path, '/');
128 dirnamelen = path.len;
129 while ((de = readdir(dir)) != NULL) {
132 if (is_dot_or_dotdot(de->d_name))
135 strbuf_setlen(&path, dirnamelen);
136 strbuf_addstr(&path, de->d_name);
139 if (strip_suffix_mem(path.buf, &base_len, ".graph") &&
140 strcmp(new_graph_name, de->d_name) &&
141 (!old_graph_name || strcmp(old_graph_name, de->d_name)) &&
142 remove_path(path.buf))
143 die("failed to remove path %s", path.buf);
146 strbuf_release(&path);
149 static int graph_write(int argc, const char **argv)
152 char *old_graph_name;
153 const char **pack_indexes = NULL;
155 const char **lines = NULL;
159 static struct option builtin_commit_graph_write_options[] = {
160 { OPTION_STRING, 'o', "object-dir", &opts.obj_dir,
162 N_("The object directory to store the graph") },
163 OPT_BOOL('u', "set-latest", &opts.set_latest,
164 N_("update graph-head to written graph file")),
165 OPT_BOOL('d', "delete-expired", &opts.delete_expired,
166 N_("delete expired head graph file")),
167 OPT_BOOL('s', "stdin-packs", &opts.stdin_packs,
168 N_("only scan packfiles listed by stdin")),
172 argc = parse_options(argc, argv, NULL,
173 builtin_commit_graph_write_options,
174 builtin_commit_graph_write_usage, 0);
177 opts.obj_dir = get_object_directory();
179 old_graph_name = get_graph_latest_contents(opts.obj_dir);
181 if (opts.stdin_packs) {
182 struct strbuf buf = STRBUF_INIT;
185 ALLOC_ARRAY(lines, alloc_lines);
187 while (strbuf_getline(&buf, stdin) != EOF) {
188 ALLOC_GROW(lines, nr_lines + 1, alloc_lines);
189 lines[nr_lines++] = buf.buf;
190 strbuf_detach(&buf, NULL);
193 pack_indexes = lines;
197 graph_name = write_commit_graph(opts.obj_dir,
203 set_latest_file(opts.obj_dir, graph_name);
205 if (opts.delete_expired)
206 do_delete_expired(opts.obj_dir,
210 printf("%s\n", graph_name);
211 FREE_AND_NULL(graph_name);
217 int cmd_commit_graph(int argc, const char **argv, const char *prefix)
219 static struct option builtin_commit_graph_options[] = {
220 { OPTION_STRING, 'o', "object-dir", &opts.obj_dir,
222 N_("The object directory to store the graph") },
226 if (argc == 2 && !strcmp(argv[1], "-h"))
227 usage_with_options(builtin_commit_graph_usage,
228 builtin_commit_graph_options);
230 git_config(git_default_config, NULL);
231 argc = parse_options(argc, argv, prefix,
232 builtin_commit_graph_options,
233 builtin_commit_graph_usage,
234 PARSE_OPT_STOP_AT_NON_OPTION);
237 if (!strcmp(argv[0], "read"))
238 return graph_read(argc, argv);
239 if (!strcmp(argv[0], "write"))
240 return graph_write(argc, argv);
243 usage_with_options(builtin_commit_graph_usage,
244 builtin_commit_graph_options);