| /* We need this macro to access core_apply_sparse_checkout */ |
| #define USE_THE_REPOSITORY_VARIABLE |
| |
| #include "builtin.h" |
| #include "git-compat-util.h" |
| #include "config.h" |
| #include "parse-options.h" |
| #include "repository.h" |
| #include "commit.h" |
| #include "dir.h" |
| #include "environment.h" |
| #include "hex.h" |
| #include "tree.h" |
| #include "tree-walk.h" |
| #include "object.h" |
| #include "odb.h" |
| #include "oid-array.h" |
| #include "oidset.h" |
| #include "promisor-remote.h" |
| #include "strmap.h" |
| #include "string-list.h" |
| #include "revision.h" |
| #include "trace2.h" |
| #include "progress.h" |
| #include "packfile.h" |
| #include "path-walk.h" |
| |
| static const char * const builtin_backfill_usage[] = { |
| N_("git backfill [--min-batch-size=<n>] [--[no-]sparse] [--[no-]include-edges] [<revision-range>]"), |
| NULL |
| }; |
| |
| struct backfill_context { |
| struct repository *repo; |
| struct oid_array current_batch; |
| size_t min_batch_size; |
| int sparse; |
| int include_edges; |
| struct rev_info revs; |
| }; |
| |
| static void backfill_context_clear(struct backfill_context *ctx) |
| { |
| oid_array_clear(&ctx->current_batch); |
| } |
| |
| static void download_batch(struct backfill_context *ctx) |
| { |
| promisor_remote_get_direct(ctx->repo, |
| ctx->current_batch.oid, |
| ctx->current_batch.nr); |
| oid_array_clear(&ctx->current_batch); |
| |
| /* |
| * We likely have a new packfile. Add it to the packed list to |
| * avoid possible duplicate downloads of the same objects. |
| */ |
| odb_reprepare(ctx->repo->objects); |
| } |
| |
| static int fill_missing_blobs(const char *path UNUSED, |
| struct oid_array *list, |
| enum object_type type, |
| void *data) |
| { |
| struct backfill_context *ctx = data; |
| |
| if (type != OBJ_BLOB) |
| return 0; |
| |
| for (size_t i = 0; i < list->nr; i++) { |
| if (!odb_has_object(ctx->repo->objects, &list->oid[i], 0)) |
| oid_array_append(&ctx->current_batch, &list->oid[i]); |
| } |
| |
| if (ctx->current_batch.nr >= ctx->min_batch_size) |
| download_batch(ctx); |
| |
| return 0; |
| } |
| |
| static void reject_unsupported_rev_list_options(struct rev_info *revs) |
| { |
| if (revs->diffopt.pickaxe) |
| die(_("'%s' cannot be used with 'git backfill'"), |
| (revs->diffopt.pickaxe_opts & DIFF_PICKAXE_REGEX) ? "-G" : "-S"); |
| if (revs->diffopt.filter || revs->diffopt.filter_not) |
| die(_("'%s' cannot be used with 'git backfill'"), |
| "--diff-filter"); |
| if (revs->diffopt.flags.follow_renames) |
| die(_("'%s' cannot be used with 'git backfill'"), |
| "--follow"); |
| if (revs->line_level_traverse) |
| die(_("'%s' cannot be used with 'git backfill'"), |
| "-L"); |
| if (revs->explicit_diff_merges) |
| die(_("'%s' cannot be used with 'git backfill'"), |
| "--diff-merges"); |
| if (!path_walk_filter_compatible(&revs->filter)) |
| die(_("cannot backfill with these filter options")); |
| if (revs->filter.blob_limit_value) |
| die(_("cannot backfill with blob size limits")); |
| } |
| |
| static int do_backfill(struct backfill_context *ctx) |
| { |
| struct path_walk_info info = PATH_WALK_INFO_INIT; |
| int ret; |
| |
| if (ctx->sparse) { |
| CALLOC_ARRAY(info.pl, 1); |
| info.pl_sparse_trees = 1; |
| if (get_sparse_checkout_patterns(info.pl)) { |
| path_walk_info_clear(&info); |
| return error(_("problem loading sparse-checkout")); |
| } |
| } |
| |
| /* Walk from HEAD if otherwise unspecified. */ |
| if (!ctx->revs.pending.nr) |
| add_head_to_pending(&ctx->revs); |
| if (ctx->include_edges) |
| ctx->revs.edge_hint = 1; |
| |
| info.blobs = 1; |
| info.tags = info.commits = info.trees = 0; |
| |
| info.revs = &ctx->revs; |
| info.path_fn = fill_missing_blobs; |
| info.path_fn_data = ctx; |
| |
| ret = walk_objects_by_path(&info); |
| |
| /* Download the objects that did not fill a batch. */ |
| if (!ret) |
| download_batch(ctx); |
| |
| path_walk_info_clear(&info); |
| return ret; |
| } |
| |
| int cmd_backfill(int argc, const char **argv, const char *prefix, struct repository *repo) |
| { |
| int result; |
| struct backfill_context ctx = { |
| .repo = repo, |
| .current_batch = OID_ARRAY_INIT, |
| .min_batch_size = 50000, |
| .sparse = -1, |
| .revs = REV_INFO_INIT, |
| .include_edges = 1, |
| }; |
| struct option options[] = { |
| OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size, |
| N_("Minimum number of objects to request at a time")), |
| OPT_BOOL(0, "sparse", &ctx.sparse, |
| N_("Restrict the missing objects to the current sparse-checkout")), |
| OPT_BOOL(0, "include-edges", &ctx.include_edges, |
| N_("Include blobs from boundary commits in the backfill")), |
| OPT_END(), |
| }; |
| struct repo_config_values *cfg = repo_config_values(the_repository); |
| |
| show_usage_with_options_if_asked(argc, argv, |
| builtin_backfill_usage, options); |
| |
| argc = parse_options(argc, argv, prefix, options, builtin_backfill_usage, |
| PARSE_OPT_KEEP_UNKNOWN_OPT | |
| PARSE_OPT_KEEP_ARGV0 | |
| PARSE_OPT_KEEP_DASHDASH); |
| |
| repo_init_revisions(repo, &ctx.revs, prefix); |
| argc = setup_revisions(argc, argv, &ctx.revs, NULL); |
| |
| if (argc > 1) |
| die(_("unrecognized argument: %s"), argv[1]); |
| reject_unsupported_rev_list_options(&ctx.revs); |
| |
| repo_config(repo, git_default_config, NULL); |
| |
| if (ctx.sparse < 0) |
| ctx.sparse = cfg->apply_sparse_checkout; |
| |
| result = do_backfill(&ctx); |
| backfill_context_clear(&ctx); |
| release_revisions(&ctx.revs); |
| return result; |
| } |