| 1 | #define USE_THE_REPOSITORY_VARIABLE |
| 2 | #define DISABLE_SIGN_COMPARE_WARNINGS |
| 3 | |
| 4 | #include "builtin.h" |
| 5 | #include "config.h" |
| 6 | #include "environment.h" |
| 7 | #include "parse-options.h" |
| 8 | #include "path.h" |
| 9 | #include "run-command.h" |
| 10 | #include "server-info.h" |
| 11 | #include "string-list.h" |
| 12 | #include "midx.h" |
| 13 | #include "packfile.h" |
| 14 | #include "prune-packed.h" |
| 15 | #include "promisor-remote.h" |
| 16 | #include "repack.h" |
| 17 | #include "shallow.h" |
| 18 | |
| 19 | #define ALL_INTO_ONE 1 |
| 20 | #define LOOSEN_UNREACHABLE 2 |
| 21 | #define PACK_CRUFT 4 |
| 22 | |
| 23 | #define DELETE_PACK 1 |
| 24 | #define RETAIN_PACK 2 |
| 25 | |
| 26 | static int pack_everything; |
| 27 | static int write_bitmaps = -1; |
| 28 | static int use_delta_islands; |
| 29 | static int run_update_server_info = 1; |
| 30 | static char *packdir, *packtmp_name, *packtmp; |
| 31 | static int midx_must_contain_cruft = 1; |
| 32 | |
| 33 | static const char *const git_repack_usage[] = { |
| 34 | N_("git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" |
| 35 | "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" |
| 36 | "[--write-midx[=<mode>]] [--name-hash-version=<n>] [--path-walk]"), |
| 37 | NULL |
| 38 | }; |
| 39 | |
| 40 | static const char incremental_bitmap_conflict_error[] = N_( |
| 41 | "Incremental repacks are incompatible with bitmap indexes. Use\n" |
| 42 | "--no-write-bitmap-index or disable the pack.writeBitmaps configuration." |
| 43 | ); |
| 44 | |
| 45 | #define DEFAULT_MIDX_SPLIT_FACTOR 2 |
| 46 | #define DEFAULT_MIDX_NEW_LAYER_THRESHOLD 8 |
| 47 | |
| 48 | struct repack_config_ctx { |
| 49 | struct pack_objects_args *po_args; |
| 50 | struct pack_objects_args *cruft_po_args; |
| 51 | int midx_split_factor; |
| 52 | int midx_new_layer_threshold; |
| 53 | }; |
| 54 | |
| 55 | static int repack_config(const char *var, const char *value, |
| 56 | const struct config_context *ctx, void *cb) |
| 57 | { |
| 58 | struct repack_config_ctx *repack_ctx = cb; |
| 59 | struct pack_objects_args *po_args = repack_ctx->po_args; |
| 60 | struct pack_objects_args *cruft_po_args = repack_ctx->cruft_po_args; |
| 61 | if (!strcmp(var, "repack.usedeltabaseoffset")) { |
| 62 | po_args->delta_base_offset = git_config_bool(var, value); |
| 63 | return 0; |
| 64 | } |
| 65 | if (!strcmp(var, "repack.packkeptobjects")) { |
| 66 | po_args->pack_kept_objects = git_config_bool(var, value); |
| 67 | return 0; |
| 68 | } |
| 69 | if (!strcmp(var, "repack.writebitmaps") || |
| 70 | !strcmp(var, "pack.writebitmaps")) { |
| 71 | write_bitmaps = git_config_bool(var, value); |
| 72 | return 0; |
| 73 | } |
| 74 | if (!strcmp(var, "repack.usedeltaislands")) { |
| 75 | use_delta_islands = git_config_bool(var, value); |
| 76 | return 0; |
| 77 | } |
| 78 | if (strcmp(var, "repack.updateserverinfo") == 0) { |
| 79 | run_update_server_info = git_config_bool(var, value); |
| 80 | return 0; |
| 81 | } |
| 82 | if (!strcmp(var, "repack.cruftwindow")) { |
| 83 | free(cruft_po_args->window); |
| 84 | return git_config_string(&cruft_po_args->window, var, value); |
| 85 | } |
| 86 | if (!strcmp(var, "repack.cruftwindowmemory")) { |
| 87 | free(cruft_po_args->window_memory); |
| 88 | return git_config_string(&cruft_po_args->window_memory, var, value); |
| 89 | } |
| 90 | if (!strcmp(var, "repack.cruftdepth")) { |
| 91 | free(cruft_po_args->depth); |
| 92 | return git_config_string(&cruft_po_args->depth, var, value); |
| 93 | } |
| 94 | if (!strcmp(var, "repack.cruftthreads")) { |
| 95 | free(cruft_po_args->threads); |
| 96 | return git_config_string(&cruft_po_args->threads, var, value); |
| 97 | } |
| 98 | if (!strcmp(var, "repack.midxmustcontaincruft")) { |
| 99 | midx_must_contain_cruft = git_config_bool(var, value); |
| 100 | return 0; |
| 101 | } |
| 102 | if (!strcmp(var, "repack.midxsplitfactor")) { |
| 103 | repack_ctx->midx_split_factor = git_config_int(var, value, |
| 104 | ctx->kvi); |
| 105 | return 0; |
| 106 | } |
| 107 | if (!strcmp(var, "repack.midxnewlayerthreshold")) { |
| 108 | repack_ctx->midx_new_layer_threshold = git_config_int(var, value, |
| 109 | ctx->kvi); |
| 110 | return 0; |
| 111 | } |
| 112 | return git_default_config(var, value, ctx, cb); |
| 113 | } |
| 114 | |
| 115 | static int option_parse_write_midx(const struct option *opt, const char *arg, |
| 116 | int unset) |
| 117 | { |
| 118 | enum repack_write_midx_mode *cfg = opt->value; |
| 119 | |
| 120 | if (unset) { |
| 121 | *cfg = REPACK_WRITE_MIDX_NONE; |
| 122 | return 0; |
| 123 | } |
| 124 | |
| 125 | if (!arg || !*arg) |
| 126 | *cfg = REPACK_WRITE_MIDX_DEFAULT; |
| 127 | else if (!strcmp(arg, "incremental")) |
| 128 | *cfg = REPACK_WRITE_MIDX_INCREMENTAL; |
| 129 | else |
| 130 | return error(_("unknown value for %s: %s"), opt->long_name, arg); |
| 131 | |
| 132 | return 0; |
| 133 | } |
| 134 | |
| 135 | int cmd_repack(int argc, |
| 136 | const char **argv, |
| 137 | const char *prefix, |
| 138 | struct repository *repo) |
| 139 | { |
| 140 | struct child_process cmd = CHILD_PROCESS_INIT; |
| 141 | struct string_list_item *item; |
| 142 | struct string_list names = STRING_LIST_INIT_DUP; |
| 143 | struct existing_packs existing = EXISTING_PACKS_INIT; |
| 144 | struct pack_geometry geometry = { 0 }; |
| 145 | struct tempfile *refs_snapshot = NULL; |
| 146 | int i, ret; |
| 147 | int show_progress; |
| 148 | |
| 149 | /* variables to be filled by option parsing */ |
| 150 | struct repack_config_ctx config_ctx; |
| 151 | int delete_redundant = 0; |
| 152 | const char *unpack_unreachable = NULL; |
| 153 | int keep_unreachable = 0; |
| 154 | struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; |
| 155 | struct pack_objects_args po_args = PACK_OBJECTS_ARGS_INIT; |
| 156 | struct pack_objects_args cruft_po_args = PACK_OBJECTS_ARGS_INIT; |
| 157 | enum repack_write_midx_mode write_midx = REPACK_WRITE_MIDX_NONE; |
| 158 | const char *cruft_expiration = NULL; |
| 159 | const char *expire_to = NULL; |
| 160 | const char *filter_to = NULL; |
| 161 | const char *opt_window = NULL; |
| 162 | const char *opt_window_memory = NULL; |
| 163 | const char *opt_depth = NULL; |
| 164 | const char *opt_threads = NULL; |
| 165 | unsigned long combine_cruft_below_size = 0ul; |
| 166 | |
| 167 | struct option builtin_repack_options[] = { |
| 168 | OPT_BIT('a', NULL, &pack_everything, |
| 169 | N_("pack everything in a single pack"), ALL_INTO_ONE), |
| 170 | OPT_BIT('A', NULL, &pack_everything, |
| 171 | N_("same as -a, and turn unreachable objects loose"), |
| 172 | LOOSEN_UNREACHABLE | ALL_INTO_ONE), |
| 173 | OPT_BIT(0, "cruft", &pack_everything, |
| 174 | N_("same as -a, pack unreachable cruft objects separately"), |
| 175 | PACK_CRUFT), |
| 176 | OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"), |
| 177 | N_("with --cruft, expire objects older than this")), |
| 178 | OPT_UNSIGNED(0, "combine-cruft-below-size", |
| 179 | &combine_cruft_below_size, |
| 180 | N_("with --cruft, only repack cruft packs smaller than this")), |
| 181 | OPT_UNSIGNED(0, "max-cruft-size", &cruft_po_args.max_pack_size, |
| 182 | N_("with --cruft, limit the size of new cruft packs")), |
| 183 | OPT_BOOL('d', NULL, &delete_redundant, |
| 184 | N_("remove redundant packs, and run git-prune-packed")), |
| 185 | OPT_BOOL('f', NULL, &po_args.no_reuse_delta, |
| 186 | N_("pass --no-reuse-delta to git-pack-objects")), |
| 187 | OPT_BOOL('F', NULL, &po_args.no_reuse_object, |
| 188 | N_("pass --no-reuse-object to git-pack-objects")), |
| 189 | OPT_INTEGER(0, "name-hash-version", &po_args.name_hash_version, |
| 190 | N_("specify the name hash version to use for grouping similar objects by path")), |
| 191 | OPT_BOOL(0, "path-walk", &po_args.path_walk, |
| 192 | N_("pass --path-walk to git-pack-objects")), |
| 193 | OPT_NEGBIT('n', NULL, &run_update_server_info, |
| 194 | N_("do not run git-update-server-info"), 1), |
| 195 | OPT__QUIET(&po_args.quiet, N_("be quiet")), |
| 196 | OPT_BOOL('l', "local", &po_args.local, |
| 197 | N_("pass --local to git-pack-objects")), |
| 198 | OPT_BOOL('b', "write-bitmap-index", &write_bitmaps, |
| 199 | N_("write bitmap index")), |
| 200 | OPT_BOOL('i', "delta-islands", &use_delta_islands, |
| 201 | N_("pass --delta-islands to git-pack-objects")), |
| 202 | OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), |
| 203 | N_("with -A, do not loosen objects older than this")), |
| 204 | OPT_BOOL('k', "keep-unreachable", &keep_unreachable, |
| 205 | N_("with -a, repack unreachable objects")), |
| 206 | OPT_STRING(0, "window", &opt_window, N_("n"), |
| 207 | N_("size of the window used for delta compression")), |
| 208 | OPT_STRING(0, "window-memory", &opt_window_memory, N_("bytes"), |
| 209 | N_("same as the above, but limit memory size instead of entries count")), |
| 210 | OPT_STRING(0, "depth", &opt_depth, N_("n"), |
| 211 | N_("limits the maximum delta depth")), |
| 212 | OPT_STRING(0, "threads", &opt_threads, N_("n"), |
| 213 | N_("limits the maximum number of threads")), |
| 214 | OPT_UNSIGNED(0, "max-pack-size", &po_args.max_pack_size, |
| 215 | N_("maximum size of each packfile")), |
| 216 | OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options), |
| 217 | OPT_BOOL(0, "pack-kept-objects", &po_args.pack_kept_objects, |
| 218 | N_("repack objects in packs marked with .keep")), |
| 219 | OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), |
| 220 | N_("do not repack this pack")), |
| 221 | OPT_INTEGER('g', "geometric", &geometry.split_factor, |
| 222 | N_("find a geometric progression with factor <N>")), |
| 223 | OPT_CALLBACK_F(0, "write-midx", &write_midx, |
| 224 | N_("mode"), |
| 225 | N_("write a multi-pack index of the resulting packs"), |
| 226 | PARSE_OPT_OPTARG, option_parse_write_midx), |
| 227 | OPT_SET_INT_F('m', NULL, &write_midx, |
| 228 | N_("write a multi-pack index of the resulting packs"), |
| 229 | REPACK_WRITE_MIDX_DEFAULT, |
| 230 | PARSE_OPT_HIDDEN), |
| 231 | OPT_STRING(0, "expire-to", &expire_to, N_("dir"), |
| 232 | N_("pack prefix to store a pack containing pruned objects")), |
| 233 | OPT_STRING(0, "filter-to", &filter_to, N_("dir"), |
| 234 | N_("pack prefix to store a pack containing filtered out objects")), |
| 235 | OPT_END() |
| 236 | }; |
| 237 | |
| 238 | list_objects_filter_init(&po_args.filter_options); |
| 239 | |
| 240 | memset(&config_ctx, 0, sizeof(config_ctx)); |
| 241 | config_ctx.po_args = &po_args; |
| 242 | config_ctx.cruft_po_args = &cruft_po_args; |
| 243 | config_ctx.midx_split_factor = DEFAULT_MIDX_SPLIT_FACTOR; |
| 244 | config_ctx.midx_new_layer_threshold = DEFAULT_MIDX_NEW_LAYER_THRESHOLD; |
| 245 | |
| 246 | repo_config(repo, repack_config, &config_ctx); |
| 247 | |
| 248 | argc = parse_options(argc, argv, prefix, builtin_repack_options, |
| 249 | git_repack_usage, 0); |
| 250 | |
| 251 | po_args.window = xstrdup_or_null(opt_window); |
| 252 | po_args.window_memory = xstrdup_or_null(opt_window_memory); |
| 253 | po_args.depth = xstrdup_or_null(opt_depth); |
| 254 | po_args.threads = xstrdup_or_null(opt_threads); |
| 255 | |
| 256 | if (delete_redundant && repo->repository_format_precious_objects) |
| 257 | die(_("cannot delete packs in a precious-objects repo")); |
| 258 | |
| 259 | die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A", |
| 260 | keep_unreachable, "-k/--keep-unreachable", |
| 261 | pack_everything & PACK_CRUFT, "--cruft"); |
| 262 | |
| 263 | if (pack_everything & PACK_CRUFT) |
| 264 | pack_everything |= ALL_INTO_ONE; |
| 265 | |
| 266 | if (write_bitmaps < 0) { |
| 267 | if (write_midx == REPACK_WRITE_MIDX_NONE && |
| 268 | (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository())) |
| 269 | write_bitmaps = 0; |
| 270 | } |
| 271 | if (po_args.pack_kept_objects < 0) |
| 272 | po_args.pack_kept_objects = write_bitmaps > 0 && |
| 273 | write_midx == REPACK_WRITE_MIDX_NONE; |
| 274 | |
| 275 | if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && |
| 276 | write_midx == REPACK_WRITE_MIDX_NONE) |
| 277 | die(_(incremental_bitmap_conflict_error)); |
| 278 | |
| 279 | if (write_bitmaps && po_args.local && |
| 280 | odb_has_alternates(repo->objects)) { |
| 281 | /* |
| 282 | * When asked to do a local repack, but we have |
| 283 | * packfiles that are inherited from an alternate, then |
| 284 | * we cannot guarantee that the multi-pack-index would |
| 285 | * have full coverage of all objects. We thus disable |
| 286 | * writing bitmaps in that case. |
| 287 | */ |
| 288 | warning(_("disabling bitmap writing, as some objects are not being packed")); |
| 289 | write_bitmaps = 0; |
| 290 | } |
| 291 | |
| 292 | if (config_ctx.midx_split_factor < 2) |
| 293 | die(_("invalid value for %s: %d"), "--midx-split-factor", |
| 294 | config_ctx.midx_split_factor); |
| 295 | if (config_ctx.midx_new_layer_threshold < 1) |
| 296 | die(_("invalid value for %s: %d"), "--midx-new-layer-threshold", |
| 297 | config_ctx.midx_new_layer_threshold); |
| 298 | |
| 299 | if (write_midx != REPACK_WRITE_MIDX_NONE && write_bitmaps) { |
| 300 | struct strbuf path = STRBUF_INIT; |
| 301 | |
| 302 | strbuf_addf(&path, "%s/%s_XXXXXX", |
| 303 | repo_get_object_directory(repo), |
| 304 | "bitmap-ref-tips"); |
| 305 | |
| 306 | refs_snapshot = xmks_tempfile(path.buf); |
| 307 | midx_snapshot_refs(repo, refs_snapshot); |
| 308 | |
| 309 | strbuf_release(&path); |
| 310 | } |
| 311 | |
| 312 | packdir = mkpathdup("%s/pack", repo_get_object_directory(repo)); |
| 313 | packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid()); |
| 314 | packtmp = mkpathdup("%s/%s", packdir, packtmp_name); |
| 315 | |
| 316 | existing.repo = repo; |
| 317 | existing_packs_collect(&existing, &keep_pack_list); |
| 318 | |
| 319 | if (geometry.split_factor) { |
| 320 | if (pack_everything) |
| 321 | die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a"); |
| 322 | if (write_midx == REPACK_WRITE_MIDX_INCREMENTAL) { |
| 323 | geometry.midx_layer_threshold = config_ctx.midx_new_layer_threshold; |
| 324 | geometry.midx_layer_threshold_set = true; |
| 325 | } |
| 326 | pack_geometry_init(&geometry, &existing, &po_args); |
| 327 | pack_geometry_split(&geometry); |
| 328 | } |
| 329 | |
| 330 | prepare_pack_objects(&cmd, &po_args, packtmp); |
| 331 | |
| 332 | show_progress = !po_args.quiet && isatty(2); |
| 333 | |
| 334 | strvec_push(&cmd.args, "--keep-true-parents"); |
| 335 | for (i = 0; i < keep_pack_list.nr; i++) |
| 336 | strvec_pushf(&cmd.args, "--keep-pack=%s", |
| 337 | keep_pack_list.items[i].string); |
| 338 | strvec_push(&cmd.args, "--non-empty"); |
| 339 | if (!geometry.split_factor) { |
| 340 | /* |
| 341 | * We need to grab all reachable objects, including those that |
| 342 | * are reachable from reflogs and the index. |
| 343 | * |
| 344 | * When repacking into a geometric progression of packs, |
| 345 | * however, we ask 'git pack-objects --stdin-packs', and it is |
| 346 | * not about packing objects based on reachability but about |
| 347 | * repacking all the objects in specified packs and loose ones |
| 348 | * (indeed, --stdin-packs is incompatible with these options). |
| 349 | */ |
| 350 | strvec_push(&cmd.args, "--all"); |
| 351 | strvec_push(&cmd.args, "--reflog"); |
| 352 | strvec_push(&cmd.args, "--indexed-objects"); |
| 353 | } |
| 354 | if (repo_has_promisor_remote(repo)) |
| 355 | strvec_push(&cmd.args, "--exclude-promisor-objects"); |
| 356 | if (write_midx == REPACK_WRITE_MIDX_NONE) { |
| 357 | if (write_bitmaps > 0) |
| 358 | strvec_push(&cmd.args, "--write-bitmap-index"); |
| 359 | else if (write_bitmaps < 0) |
| 360 | strvec_push(&cmd.args, "--write-bitmap-index-quiet"); |
| 361 | } |
| 362 | if (use_delta_islands) |
| 363 | strvec_push(&cmd.args, "--delta-islands"); |
| 364 | |
| 365 | if (pack_everything & ALL_INTO_ONE) { |
| 366 | repack_promisor_objects(repo, &po_args, &names, packtmp); |
| 367 | |
| 368 | if (existing_packs_has_non_kept(&existing) && |
| 369 | delete_redundant && |
| 370 | !(pack_everything & PACK_CRUFT)) { |
| 371 | for_each_string_list_item(item, &names) { |
| 372 | strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack", |
| 373 | packtmp_name, item->string); |
| 374 | } |
| 375 | if (unpack_unreachable) { |
| 376 | strvec_pushf(&cmd.args, |
| 377 | "--unpack-unreachable=%s", |
| 378 | unpack_unreachable); |
| 379 | } else if (pack_everything & LOOSEN_UNREACHABLE) { |
| 380 | strvec_push(&cmd.args, |
| 381 | "--unpack-unreachable"); |
| 382 | } else if (keep_unreachable) { |
| 383 | strvec_push(&cmd.args, "--keep-unreachable"); |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | if (keep_unreachable && delete_redundant && |
| 388 | !(pack_everything & PACK_CRUFT)) |
| 389 | strvec_push(&cmd.args, "--pack-loose-unreachable"); |
| 390 | } else if (geometry.split_factor) { |
| 391 | pack_geometry_repack_promisors(repo, &po_args, &geometry, |
| 392 | &names, packtmp); |
| 393 | |
| 394 | if (midx_must_contain_cruft) |
| 395 | strvec_push(&cmd.args, "--stdin-packs"); |
| 396 | else |
| 397 | strvec_push(&cmd.args, "--stdin-packs=follow"); |
| 398 | strvec_push(&cmd.args, "--unpacked"); |
| 399 | } else { |
| 400 | strvec_push(&cmd.args, "--unpacked"); |
| 401 | strvec_push(&cmd.args, "--incremental"); |
| 402 | } |
| 403 | |
| 404 | if (po_args.filter_options.choice) |
| 405 | strvec_pushf(&cmd.args, "--filter=%s", |
| 406 | expand_list_objects_filter_spec(&po_args.filter_options)); |
| 407 | else if (filter_to) |
| 408 | die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter"); |
| 409 | |
| 410 | if (geometry.split_factor) |
| 411 | cmd.in = -1; |
| 412 | else |
| 413 | cmd.no_stdin = 1; |
| 414 | |
| 415 | ret = start_command(&cmd); |
| 416 | if (ret) |
| 417 | goto cleanup; |
| 418 | |
| 419 | if (geometry.split_factor) { |
| 420 | FILE *in = xfdopen(cmd.in, "w"); |
| 421 | /* |
| 422 | * The resulting pack should contain all objects in packs that |
| 423 | * are going to be rolled up, but exclude objects in packs which |
| 424 | * are being left alone. |
| 425 | */ |
| 426 | for (i = 0; i < geometry.split; i++) |
| 427 | fprintf(in, "%s\n", pack_basename(geometry.pack[i])); |
| 428 | for (i = geometry.split; i < geometry.pack_nr; i++) { |
| 429 | const char *basename = pack_basename(geometry.pack[i]); |
| 430 | char marker = '^'; |
| 431 | |
| 432 | if (!midx_must_contain_cruft && |
| 433 | !string_list_has_string(&existing.midx_packs, |
| 434 | basename)) { |
| 435 | /* |
| 436 | * Assume non-MIDX'd packs are not |
| 437 | * necessarily closed under |
| 438 | * reachability. |
| 439 | */ |
| 440 | marker = '!'; |
| 441 | } |
| 442 | |
| 443 | fprintf(in, "%c%s\n", marker, basename); |
| 444 | } |
| 445 | fclose(in); |
| 446 | } |
| 447 | |
| 448 | { |
| 449 | struct write_pack_opts opts = { |
| 450 | .packdir = packdir, |
| 451 | .destination = packdir, |
| 452 | .packtmp = packtmp, |
| 453 | }; |
| 454 | ret = finish_pack_objects_cmd(repo->hash_algo, &opts, &cmd, |
| 455 | &names); |
| 456 | if (ret) |
| 457 | goto cleanup; |
| 458 | } |
| 459 | |
| 460 | if (!names.nr) { |
| 461 | if (!po_args.quiet) |
| 462 | printf_ln(_("Nothing new to pack.")); |
| 463 | /* |
| 464 | * If we didn't write any new packs, the non-cruft packs |
| 465 | * may refer to once-unreachable objects in the cruft |
| 466 | * pack(s). |
| 467 | * |
| 468 | * If there isn't already a MIDX, the one we write |
| 469 | * must include the cruft pack(s), in case the |
| 470 | * non-cruft pack(s) refer to once-cruft objects. |
| 471 | * |
| 472 | * If there is already a MIDX, we can punt here, since |
| 473 | * midx_has_unknown_packs() will make the decision for |
| 474 | * us. |
| 475 | */ |
| 476 | if (!get_multi_pack_index(existing.source)) |
| 477 | midx_must_contain_cruft = 1; |
| 478 | } |
| 479 | |
| 480 | if (pack_everything & PACK_CRUFT) { |
| 481 | struct write_pack_opts opts = { |
| 482 | .po_args = &cruft_po_args, |
| 483 | .destination = packtmp, |
| 484 | .packtmp = packtmp, |
| 485 | .packdir = packdir, |
| 486 | }; |
| 487 | |
| 488 | if (!cruft_po_args.window) |
| 489 | cruft_po_args.window = xstrdup_or_null(po_args.window); |
| 490 | if (!cruft_po_args.window_memory) |
| 491 | cruft_po_args.window_memory = xstrdup_or_null(po_args.window_memory); |
| 492 | if (!cruft_po_args.depth) |
| 493 | cruft_po_args.depth = xstrdup_or_null(po_args.depth); |
| 494 | if (!cruft_po_args.threads) |
| 495 | cruft_po_args.threads = xstrdup_or_null(po_args.threads); |
| 496 | if (!cruft_po_args.max_pack_size) |
| 497 | cruft_po_args.max_pack_size = po_args.max_pack_size; |
| 498 | |
| 499 | cruft_po_args.local = po_args.local; |
| 500 | cruft_po_args.quiet = po_args.quiet; |
| 501 | cruft_po_args.delta_base_offset = po_args.delta_base_offset; |
| 502 | cruft_po_args.pack_kept_objects = 0; |
| 503 | |
| 504 | ret = write_cruft_pack(&opts, cruft_expiration, |
| 505 | combine_cruft_below_size, &names, |
| 506 | &existing); |
| 507 | if (ret) |
| 508 | goto cleanup; |
| 509 | |
| 510 | if (delete_redundant && expire_to) { |
| 511 | /* |
| 512 | * If `--expire-to` is given with `-d`, it's possible |
| 513 | * that we're about to prune some objects. With cruft |
| 514 | * packs, pruning is implicit: any objects from existing |
| 515 | * packs that weren't picked up by new packs are removed |
| 516 | * when their packs are deleted. |
| 517 | * |
| 518 | * Generate an additional cruft pack, with one twist: |
| 519 | * `names` now includes the name of the cruft pack |
| 520 | * written in the previous step. So the contents of |
| 521 | * _this_ cruft pack exclude everything contained in the |
| 522 | * existing cruft pack (that is, all of the unreachable |
| 523 | * objects which are no older than |
| 524 | * `--cruft-expiration`). |
| 525 | * |
| 526 | * To make this work, cruft_expiration must become NULL |
| 527 | * so that this cruft pack doesn't actually prune any |
| 528 | * objects. If it were non-NULL, this call would always |
| 529 | * generate an empty pack (since every object not in the |
| 530 | * cruft pack generated above will have an mtime older |
| 531 | * than the expiration). |
| 532 | * |
| 533 | * Pretend we don't have a `--combine-cruft-below-size` |
| 534 | * argument, since we're not selectively combining |
| 535 | * anything based on size to generate the limbo cruft |
| 536 | * pack, but rather removing all cruft packs from the |
| 537 | * main repository regardless of size. |
| 538 | */ |
| 539 | opts.destination = expire_to; |
| 540 | ret = write_cruft_pack(&opts, NULL, 0ul, &names, |
| 541 | &existing); |
| 542 | if (ret) |
| 543 | goto cleanup; |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | if (po_args.filter_options.choice) { |
| 548 | struct write_pack_opts opts = { |
| 549 | .po_args = &po_args, |
| 550 | .destination = filter_to, |
| 551 | .packdir = packdir, |
| 552 | .packtmp = packtmp, |
| 553 | }; |
| 554 | |
| 555 | if (!opts.destination) |
| 556 | opts.destination = packtmp; |
| 557 | |
| 558 | ret = write_filtered_pack(&opts, &existing, &names); |
| 559 | if (ret) |
| 560 | goto cleanup; |
| 561 | } |
| 562 | |
| 563 | string_list_sort(&names); |
| 564 | |
| 565 | odb_close(repo->objects); |
| 566 | |
| 567 | /* |
| 568 | * Ok we have prepared all new packfiles. |
| 569 | */ |
| 570 | for_each_string_list_item(item, &names) |
| 571 | generated_pack_install(item->util, item->string, packdir, |
| 572 | packtmp); |
| 573 | /* End of pack replacement. */ |
| 574 | |
| 575 | if (delete_redundant && pack_everything & ALL_INTO_ONE) { |
| 576 | if (write_midx == REPACK_WRITE_MIDX_INCREMENTAL) |
| 577 | existing_packs_retain_midx_packs(&existing); |
| 578 | existing_packs_mark_for_deletion(&existing, &names); |
| 579 | } |
| 580 | |
| 581 | if (write_midx != REPACK_WRITE_MIDX_NONE) { |
| 582 | struct repack_write_midx_opts opts = { |
| 583 | .existing = &existing, |
| 584 | .geometry = &geometry, |
| 585 | .names = &names, |
| 586 | .refs_snapshot = refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, |
| 587 | .packdir = packdir, |
| 588 | .show_progress = show_progress, |
| 589 | .write_bitmaps = write_bitmaps > 0, |
| 590 | .midx_must_contain_cruft = midx_must_contain_cruft, |
| 591 | .midx_split_factor = config_ctx.midx_split_factor, |
| 592 | .midx_new_layer_threshold = config_ctx.midx_new_layer_threshold, |
| 593 | .mode = write_midx, |
| 594 | }; |
| 595 | |
| 596 | ret = repack_write_midx(&opts); |
| 597 | if (ret) |
| 598 | goto cleanup; |
| 599 | } |
| 600 | |
| 601 | odb_reprepare(repo->objects); |
| 602 | |
| 603 | if (delete_redundant) { |
| 604 | int opts = 0; |
| 605 | bool wrote_incremental_midx = write_midx == REPACK_WRITE_MIDX_INCREMENTAL; |
| 606 | |
| 607 | existing_packs_remove_redundant(&existing, packdir, |
| 608 | wrote_incremental_midx); |
| 609 | |
| 610 | if (geometry.split_factor) |
| 611 | pack_geometry_remove_redundant(&geometry, &names, |
| 612 | &existing, packdir, |
| 613 | wrote_incremental_midx); |
| 614 | if (show_progress) |
| 615 | opts |= PRUNE_PACKED_VERBOSE; |
| 616 | prune_packed_objects(opts); |
| 617 | |
| 618 | if (!keep_unreachable && |
| 619 | (!(pack_everything & LOOSEN_UNREACHABLE) || |
| 620 | unpack_unreachable) && |
| 621 | is_repository_shallow(repo)) |
| 622 | prune_shallow(PRUNE_QUICK); |
| 623 | } |
| 624 | |
| 625 | if (run_update_server_info) |
| 626 | update_server_info(repo, 0); |
| 627 | |
| 628 | if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) { |
| 629 | unsigned flags = 0; |
| 630 | if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL, 0)) |
| 631 | flags |= MIDX_WRITE_INCREMENTAL; |
| 632 | write_midx_file(existing.source, NULL, NULL, flags); |
| 633 | } |
| 634 | |
| 635 | cleanup: |
| 636 | string_list_clear(&keep_pack_list, 0); |
| 637 | string_list_clear(&names, 1); |
| 638 | existing_packs_release(&existing); |
| 639 | pack_geometry_release(&geometry); |
| 640 | pack_objects_args_release(&po_args); |
| 641 | pack_objects_args_release(&cruft_po_args); |
| 642 | |
| 643 | return ret; |
| 644 | } |