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