]> git.ipfire.org Git - thirdparty/git.git/blobdiff - commit-graph.c
oidset: introduce 'oidset_size'
[thirdparty/git.git] / commit-graph.c
index 79bc5641cbc0c39db05faef0d7ae5d362062794c..36e1d6f9b36cd5566d7830b51330033a61ff1c28 100644 (file)
 /* Remember to update object flag allocation in object.h */
 #define REACHABLE       (1u<<15)
 
-char *get_commit_graph_filename(const char *obj_dir)
+char *get_commit_graph_filename(struct object_directory *odb)
 {
-       char *filename = xstrfmt("%s/info/commit-graph", obj_dir);
-       char *normalized = xmalloc(strlen(filename) + 1);
-       normalize_path_copy(normalized, filename);
-       free(filename);
-       return normalized;
+       return xstrfmt("%s/info/commit-graph", odb->path);
 }
 
-static char *get_split_graph_filename(const char *obj_dir,
+static char *get_split_graph_filename(struct object_directory *odb,
                                      const char *oid_hex)
 {
-       char *filename = xstrfmt("%s/info/commit-graphs/graph-%s.graph",
-                                obj_dir,
-                                oid_hex);
-       char *normalized = xmalloc(strlen(filename) + 1);
-       normalize_path_copy(normalized, filename);
-       free(filename);
-       return normalized;
+       return xstrfmt("%s/info/commit-graphs/graph-%s.graph", odb->path,
+                      oid_hex);
 }
 
-static char *get_chain_filename(const char *obj_dir)
+static char *get_chain_filename(struct object_directory *odb)
 {
-       return xstrfmt("%s/info/commit-graphs/commit-graph-chain", obj_dir);
+       return xstrfmt("%s/info/commit-graphs/commit-graph-chain", odb->path);
 }
 
 static uint8_t oid_version(void)
@@ -117,7 +108,8 @@ int open_commit_graph(const char *graph_file, int *fd, struct stat *st)
        return 1;
 }
 
-struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st)
+struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st,
+                                                struct object_directory *odb)
 {
        void *graph_map;
        size_t graph_size;
@@ -133,7 +125,9 @@ struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st)
        graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0);
        ret = parse_commit_graph(graph_map, fd, graph_size);
 
-       if (!ret) {
+       if (ret)
+               ret->odb = odb;
+       else {
                munmap(graph_map, graph_size);
                close(fd);
        }
@@ -308,7 +302,8 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
        return graph;
 }
 
-static struct commit_graph *load_commit_graph_one(const char *graph_file)
+static struct commit_graph *load_commit_graph_one(const char *graph_file,
+                                                 struct object_directory *odb)
 {
 
        struct stat st;
@@ -319,7 +314,7 @@ static struct commit_graph *load_commit_graph_one(const char *graph_file)
        if (!open_ok)
                return NULL;
 
-       g = load_commit_graph_one_fd_st(fd, &st);
+       g = load_commit_graph_one_fd_st(fd, &st, odb);
 
        if (g)
                g->filename = xstrdup(graph_file);
@@ -327,15 +322,13 @@ static struct commit_graph *load_commit_graph_one(const char *graph_file)
        return g;
 }
 
-static struct commit_graph *load_commit_graph_v1(struct repository *r, const char *obj_dir)
+static struct commit_graph *load_commit_graph_v1(struct repository *r,
+                                                struct object_directory *odb)
 {
-       char *graph_name = get_commit_graph_filename(obj_dir);
-       struct commit_graph *g = load_commit_graph_one(graph_name);
+       char *graph_name = get_commit_graph_filename(odb);
+       struct commit_graph *g = load_commit_graph_one(graph_name, odb);
        free(graph_name);
 
-       if (g)
-               g->obj_dir = obj_dir;
-
        return g;
 }
 
@@ -372,14 +365,15 @@ static int add_graph_to_chain(struct commit_graph *g,
        return 1;
 }
 
-static struct commit_graph *load_commit_graph_chain(struct repository *r, const char *obj_dir)
+static struct commit_graph *load_commit_graph_chain(struct repository *r,
+                                                   struct object_directory *odb)
 {
        struct commit_graph *graph_chain = NULL;
        struct strbuf line = STRBUF_INIT;
        struct stat st;
        struct object_id *oids;
        int i = 0, valid = 1, count;
-       char *chain_name = get_chain_filename(obj_dir);
+       char *chain_name = get_chain_filename(odb);
        FILE *fp;
        int stat_res;
 
@@ -412,14 +406,12 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r, const
 
                valid = 0;
                for (odb = r->objects->odb; odb; odb = odb->next) {
-                       char *graph_name = get_split_graph_filename(odb->path, line.buf);
-                       struct commit_graph *g = load_commit_graph_one(graph_name);
+                       char *graph_name = get_split_graph_filename(odb, line.buf);
+                       struct commit_graph *g = load_commit_graph_one(graph_name, odb);
 
                        free(graph_name);
 
                        if (g) {
-                               g->obj_dir = odb->path;
-
                                if (add_graph_to_chain(g, graph_chain, oids, i)) {
                                        graph_chain = g;
                                        valid = 1;
@@ -442,23 +434,25 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r, const
        return graph_chain;
 }
 
-struct commit_graph *read_commit_graph_one(struct repository *r, const char *obj_dir)
+struct commit_graph *read_commit_graph_one(struct repository *r,
+                                          struct object_directory *odb)
 {
-       struct commit_graph *g = load_commit_graph_v1(r, obj_dir);
+       struct commit_graph *g = load_commit_graph_v1(r, odb);
 
        if (!g)
-               g = load_commit_graph_chain(r, obj_dir);
+               g = load_commit_graph_chain(r, odb);
 
        return g;
 }
 
-static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
+static void prepare_commit_graph_one(struct repository *r,
+                                    struct object_directory *odb)
 {
 
        if (r->objects->commit_graph)
                return;
 
-       r->objects->commit_graph = read_commit_graph_one(r, obj_dir);
+       r->objects->commit_graph = read_commit_graph_one(r, odb);
 }
 
 /*
@@ -505,7 +499,7 @@ static int prepare_commit_graph(struct repository *r)
        for (odb = r->objects->odb;
             !r->objects->commit_graph && odb;
             odb = odb->next)
-               prepare_commit_graph_one(r, odb->path);
+               prepare_commit_graph_one(r, odb);
        return !!r->objects->commit_graph;
 }
 
@@ -772,7 +766,7 @@ struct packed_oid_list {
 
 struct write_commit_graph_context {
        struct repository *r;
-       char *obj_dir;
+       struct object_directory *odb;
        char *graph_name;
        struct packed_oid_list oids;
        struct packed_commit_list commits;
@@ -872,7 +866,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
 
                        if (edge_value >= 0)
                                edge_value += ctx->new_num_commits_in_base;
-                       else {
+                       else if (ctx->new_base_graph) {
                                uint32_t pos;
                                if (find_commit_in_graph(parent->item,
                                                         ctx->new_base_graph,
@@ -903,7 +897,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
 
                        if (edge_value >= 0)
                                edge_value += ctx->new_num_commits_in_base;
-                       else {
+                       else if (ctx->new_base_graph) {
                                uint32_t pos;
                                if (find_commit_in_graph(parent->item,
                                                         ctx->new_base_graph,
@@ -970,7 +964,7 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
 
                        if (edge_value >= 0)
                                edge_value += ctx->new_num_commits_in_base;
-                       else {
+                       else if (ctx->new_base_graph) {
                                uint32_t pos;
                                if (find_commit_in_graph(parent->item,
                                                         ctx->new_base_graph,
@@ -1043,6 +1037,8 @@ static void close_reachable(struct write_commit_graph_context *ctx)
 {
        int i;
        struct commit *commit;
+       enum commit_graph_split_flags flags = ctx->split_opts ?
+               ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
 
        if (ctx->report_progress)
                ctx->progress = start_delayed_progress(
@@ -1072,8 +1068,9 @@ static void close_reachable(struct write_commit_graph_context *ctx)
                if (!commit)
                        continue;
                if (ctx->split) {
-                       if (!parse_commit(commit) &&
-                           commit->graph_pos == COMMIT_NOT_FROM_GRAPH)
+                       if ((!parse_commit(commit) &&
+                            commit->graph_pos == COMMIT_NOT_FROM_GRAPH) ||
+                           flags == COMMIT_GRAPH_SPLIT_REPLACE)
                                add_missing_parents(ctx, commit);
                } else if (!parse_commit_no_graph(commit))
                        add_missing_parents(ctx, commit);
@@ -1100,7 +1097,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
        struct commit_list *list = NULL;
 
        if (ctx->report_progress)
-               ctx->progress = start_progress(
+               ctx->progress = start_delayed_progress(
                                        _("Computing commit graph generation numbers"),
                                        ctx->commits.nr);
        for (i = 0; i < ctx->commits.nr; i++) {
@@ -1149,7 +1146,7 @@ static int add_ref_to_list(const char *refname,
        return 0;
 }
 
-int write_commit_graph_reachable(const char *obj_dir,
+int write_commit_graph_reachable(struct object_directory *odb,
                                 enum commit_graph_write_flags flags,
                                 const struct split_commit_graph_opts *split_opts)
 {
@@ -1157,7 +1154,7 @@ int write_commit_graph_reachable(const char *obj_dir,
        int result;
 
        for_each_ref(add_ref_to_list, &list);
-       result = write_commit_graph(obj_dir, NULL, &list,
+       result = write_commit_graph(odb, NULL, &list,
                                    flags, split_opts);
 
        string_list_clear(&list, 0);
@@ -1172,7 +1169,7 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
        struct strbuf packname = STRBUF_INIT;
        int dirlen;
 
-       strbuf_addf(&packname, "%s/pack/", ctx->obj_dir);
+       strbuf_addf(&packname, "%s/pack/", ctx->odb->path);
        dirlen = packname.len;
        if (ctx->report_progress) {
                strbuf_addf(&progress_title,
@@ -1293,6 +1290,8 @@ static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
 static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
 {
        uint32_t i;
+       enum commit_graph_split_flags flags = ctx->split_opts ?
+               ctx->split_opts->flags : COMMIT_GRAPH_SPLIT_UNSPECIFIED;
 
        ctx->num_extra_edges = 0;
        if (ctx->report_progress)
@@ -1309,11 +1308,14 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
                ALLOC_GROW(ctx->commits.list, ctx->commits.nr + 1, ctx->commits.alloc);
                ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
 
-               if (ctx->split &&
+               if (ctx->split && flags != COMMIT_GRAPH_SPLIT_REPLACE &&
                    ctx->commits.list[ctx->commits.nr]->graph_pos != COMMIT_NOT_FROM_GRAPH)
                        continue;
 
-               parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
+               if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
+                       parse_commit(ctx->commits.list[ctx->commits.nr]);
+               else
+                       parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
 
                num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
                if (num_parents > 2)
@@ -1368,10 +1370,10 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 
                strbuf_addf(&tmp_file,
                            "%s/info/commit-graphs/tmp_graph_XXXXXX",
-                           ctx->obj_dir);
+                           ctx->odb->path);
                ctx->graph_name = strbuf_detach(&tmp_file, NULL);
        } else {
-               ctx->graph_name = get_commit_graph_filename(ctx->obj_dir);
+               ctx->graph_name = get_commit_graph_filename(ctx->odb);
        }
 
        if (safe_create_leading_directories(ctx->graph_name)) {
@@ -1382,7 +1384,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
        }
 
        if (ctx->split) {
-               char *lock_name = get_chain_filename(ctx->obj_dir);
+               char *lock_name = get_chain_filename(ctx->odb);
 
                hold_lock_file_for_update(&lk, lock_name, LOCK_DIE_ON_ERROR);
 
@@ -1470,7 +1472,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
 
        if (ctx->split && ctx->base_graph_name && ctx->num_commit_graphs_after > 1) {
                char *new_base_hash = xstrdup(oid_to_hex(&ctx->new_base_graph->oid));
-               char *new_base_name = get_split_graph_filename(ctx->new_base_graph->obj_dir, new_base_hash);
+               char *new_base_name = get_split_graph_filename(ctx->new_base_graph->odb, new_base_hash);
 
                free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 2]);
                free(ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 2]);
@@ -1494,8 +1496,12 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                }
 
                if (ctx->base_graph_name) {
-                       const char *dest = ctx->commit_graph_filenames_after[
-                                               ctx->num_commit_graphs_after - 2];
+                       const char *dest;
+                       int idx = ctx->num_commit_graphs_after - 1;
+                       if (ctx->num_commit_graphs_after > 1)
+                               idx--;
+
+                       dest = ctx->commit_graph_filenames_after[idx];
 
                        if (strcmp(ctx->base_graph_name, dest)) {
                                result = rename(ctx->base_graph_name, dest);
@@ -1506,12 +1512,12 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                                }
                        }
                } else {
-                       char *graph_name = get_commit_graph_filename(ctx->obj_dir);
+                       char *graph_name = get_commit_graph_filename(ctx->odb);
                        unlink(graph_name);
                }
 
                ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(oid_to_hex(&file_hash));
-               final_graph_name = get_split_graph_filename(ctx->obj_dir,
+               final_graph_name = get_split_graph_filename(ctx->odb,
                                        ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
                ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;
 
@@ -1535,6 +1541,7 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
 {
        struct commit_graph *g;
        uint32_t num_commits;
+       enum commit_graph_split_flags flags = COMMIT_GRAPH_SPLIT_UNSPECIFIED;
        uint32_t i;
 
        int max_commits = 0;
@@ -1542,31 +1549,45 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
 
        if (ctx->split_opts) {
                max_commits = ctx->split_opts->max_commits;
-               size_mult = ctx->split_opts->size_multiple;
+
+               if (ctx->split_opts->size_multiple)
+                       size_mult = ctx->split_opts->size_multiple;
+
+               flags = ctx->split_opts->flags;
        }
 
        g = ctx->r->objects->commit_graph;
        num_commits = ctx->commits.nr;
-       ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
-
-       while (g && (g->num_commits <= size_mult * num_commits ||
-                   (max_commits && num_commits > max_commits))) {
-               if (strcmp(g->obj_dir, ctx->obj_dir))
-                       break;
+       if (flags == COMMIT_GRAPH_SPLIT_REPLACE)
+               ctx->num_commit_graphs_after = 1;
+       else
+               ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
+
+       if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED &&
+           flags != COMMIT_GRAPH_SPLIT_REPLACE) {
+               while (g && (g->num_commits <= size_mult * num_commits ||
+                           (max_commits && num_commits > max_commits))) {
+                       if (g->odb != ctx->odb)
+                               break;
 
-               num_commits += g->num_commits;
-               g = g->base_graph;
+                       num_commits += g->num_commits;
+                       g = g->base_graph;
 
-               ctx->num_commit_graphs_after--;
+                       ctx->num_commit_graphs_after--;
+               }
        }
 
-       ctx->new_base_graph = g;
+       if (flags != COMMIT_GRAPH_SPLIT_REPLACE)
+               ctx->new_base_graph = g;
+       else if (ctx->num_commit_graphs_after != 1)
+               BUG("split_graph_merge_strategy: num_commit_graphs_after "
+                   "should be 1 with --split=replace");
 
        if (ctx->num_commit_graphs_after == 2) {
-               char *old_graph_name = get_commit_graph_filename(g->obj_dir);
+               char *old_graph_name = get_commit_graph_filename(g->odb);
 
                if (!strcmp(g->filename, old_graph_name) &&
-                   strcmp(g->obj_dir, ctx->obj_dir)) {
+                   g->odb != ctx->odb) {
                        ctx->num_commit_graphs_after = 1;
                        ctx->new_base_graph = NULL;
                }
@@ -1717,13 +1738,13 @@ static void expire_commit_graphs(struct write_commit_graph_context *ctx)
        if (ctx->split_opts && ctx->split_opts->expire_time)
                expire_time -= ctx->split_opts->expire_time;
        if (!ctx->split) {
-               char *chain_file_name = get_chain_filename(ctx->obj_dir);
+               char *chain_file_name = get_chain_filename(ctx->odb);
                unlink(chain_file_name);
                free(chain_file_name);
                ctx->num_commit_graphs_after = 0;
        }
 
-       strbuf_addstr(&path, ctx->obj_dir);
+       strbuf_addstr(&path, ctx->odb->path);
        strbuf_addstr(&path, "/info/commit-graphs");
        dir = opendir(path.buf);
 
@@ -1762,7 +1783,7 @@ out:
        strbuf_release(&path);
 }
 
-int write_commit_graph(const char *obj_dir,
+int write_commit_graph(struct object_directory *odb,
                       struct string_list *pack_indexes,
                       struct string_list *commit_hex,
                       enum commit_graph_write_flags flags,
@@ -1770,22 +1791,15 @@ int write_commit_graph(const char *obj_dir,
 {
        struct write_commit_graph_context *ctx;
        uint32_t i, count_distinct = 0;
-       size_t len;
        int res = 0;
+       int replace = 0;
 
        if (!commit_graph_compatible(the_repository))
                return 0;
 
        ctx = xcalloc(1, sizeof(struct write_commit_graph_context));
        ctx->r = the_repository;
-
-       /* normalize object dir with no trailing slash */
-       ctx->obj_dir = xmallocz(strlen(obj_dir) + 1);
-       normalize_path_copy(ctx->obj_dir, obj_dir);
-       len = strlen(ctx->obj_dir);
-       if (len && ctx->obj_dir[len - 1] == '/')
-               ctx->obj_dir[len - 1] = 0;
-
+       ctx->odb = odb;
        ctx->append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0;
        ctx->report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0;
        ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
@@ -1813,6 +1827,9 @@ int write_commit_graph(const char *obj_dir,
                                g = g->base_graph;
                        }
                }
+
+               if (ctx->split_opts)
+                       replace = ctx->split_opts->flags & COMMIT_GRAPH_SPLIT_REPLACE;
        }
 
        ctx->approx_nr_objects = approximate_object_count();
@@ -1822,7 +1839,7 @@ int write_commit_graph(const char *obj_dir,
                ctx->oids.alloc = split_opts->max_commits;
 
        if (ctx->append) {
-               prepare_commit_graph_one(ctx->r, ctx->obj_dir);
+               prepare_commit_graph_one(ctx->r, ctx->odb);
                if (ctx->r->objects->commit_graph)
                        ctx->oids.alloc += ctx->r->objects->commit_graph->num_commits;
        }
@@ -1873,13 +1890,14 @@ int write_commit_graph(const char *obj_dir,
                goto cleanup;
        }
 
-       if (!ctx->commits.nr)
+       if (!ctx->commits.nr && !replace)
                goto cleanup;
 
        if (ctx->split) {
                split_graph_merge_strategy(ctx);
 
-               merge_commit_graphs(ctx);
+               if (!replace)
+                       merge_commit_graphs(ctx);
        } else
                ctx->num_commit_graphs_after = 1;
 
@@ -1896,7 +1914,6 @@ cleanup:
        free(ctx->graph_name);
        free(ctx->commits.list);
        free(ctx->oids.list);
-       free(ctx->obj_dir);
 
        if (ctx->commit_graph_filenames_after) {
                for (i = 0; i < ctx->num_commit_graphs_after; i++) {