builtin / repack.con commit Merge branch 'sb/strbuf-h-update' into maint (c2d6378)
   1#include "builtin.h"
   2#include "cache.h"
   3#include "config.h"
   4#include "dir.h"
   5#include "parse-options.h"
   6#include "run-command.h"
   7#include "sigchain.h"
   8#include "strbuf.h"
   9#include "string-list.h"
  10#include "argv-array.h"
  11#include "packfile.h"
  12#include "object-store.h"
  13
  14static int delta_base_offset = 1;
  15static int pack_kept_objects = -1;
  16static int write_bitmaps;
  17static char *packdir, *packtmp;
  18
  19static const char *const git_repack_usage[] = {
  20        N_("git repack [<options>]"),
  21        NULL
  22};
  23
  24static const char incremental_bitmap_conflict_error[] = N_(
  25"Incremental repacks are incompatible with bitmap indexes.  Use\n"
  26"--no-write-bitmap-index or disable the pack.writebitmaps configuration."
  27);
  28
  29
  30static int repack_config(const char *var, const char *value, void *cb)
  31{
  32        if (!strcmp(var, "repack.usedeltabaseoffset")) {
  33                delta_base_offset = git_config_bool(var, value);
  34                return 0;
  35        }
  36        if (!strcmp(var, "repack.packkeptobjects")) {
  37                pack_kept_objects = git_config_bool(var, value);
  38                return 0;
  39        }
  40        if (!strcmp(var, "repack.writebitmaps") ||
  41            !strcmp(var, "pack.writebitmaps")) {
  42                write_bitmaps = git_config_bool(var, value);
  43                return 0;
  44        }
  45        return git_default_config(var, value, cb);
  46}
  47
  48/*
  49 * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files.
  50 */
  51static void remove_temporary_files(void)
  52{
  53        struct strbuf buf = STRBUF_INIT;
  54        size_t dirlen, prefixlen;
  55        DIR *dir;
  56        struct dirent *e;
  57
  58        dir = opendir(packdir);
  59        if (!dir)
  60                return;
  61
  62        /* Point at the slash at the end of ".../objects/pack/" */
  63        dirlen = strlen(packdir) + 1;
  64        strbuf_addstr(&buf, packtmp);
  65        /* Hold the length of  ".tmp-%d-pack-" */
  66        prefixlen = buf.len - dirlen;
  67
  68        while ((e = readdir(dir))) {
  69                if (strncmp(e->d_name, buf.buf + dirlen, prefixlen))
  70                        continue;
  71                strbuf_setlen(&buf, dirlen);
  72                strbuf_addstr(&buf, e->d_name);
  73                unlink(buf.buf);
  74        }
  75        closedir(dir);
  76        strbuf_release(&buf);
  77}
  78
  79static void remove_pack_on_signal(int signo)
  80{
  81        remove_temporary_files();
  82        sigchain_pop(signo);
  83        raise(signo);
  84}
  85
  86/*
  87 * Adds all packs hex strings to the fname list, which do not
  88 * have a corresponding .keep file. These packs are not to
  89 * be kept if we are going to pack everything into one file.
  90 */
  91static void get_non_kept_pack_filenames(struct string_list *fname_list,
  92                                        const struct string_list *extra_keep)
  93{
  94        DIR *dir;
  95        struct dirent *e;
  96        char *fname;
  97
  98        if (!(dir = opendir(packdir)))
  99                return;
 100
 101        while ((e = readdir(dir)) != NULL) {
 102                size_t len;
 103                int i;
 104
 105                for (i = 0; i < extra_keep->nr; i++)
 106                        if (!fspathcmp(e->d_name, extra_keep->items[i].string))
 107                                break;
 108                if (extra_keep->nr > 0 && i < extra_keep->nr)
 109                        continue;
 110
 111                if (!strip_suffix(e->d_name, ".pack", &len))
 112                        continue;
 113
 114                fname = xmemdupz(e->d_name, len);
 115
 116                if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
 117                        string_list_append_nodup(fname_list, fname);
 118                else
 119                        free(fname);
 120        }
 121        closedir(dir);
 122}
 123
 124static void remove_redundant_pack(const char *dir_name, const char *base_name)
 125{
 126        const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"};
 127        int i;
 128        struct strbuf buf = STRBUF_INIT;
 129        size_t plen;
 130
 131        strbuf_addf(&buf, "%s/%s", dir_name, base_name);
 132        plen = buf.len;
 133
 134        for (i = 0; i < ARRAY_SIZE(exts); i++) {
 135                strbuf_setlen(&buf, plen);
 136                strbuf_addstr(&buf, exts[i]);
 137                unlink(buf.buf);
 138        }
 139        strbuf_release(&buf);
 140}
 141
 142struct pack_objects_args {
 143        const char *window;
 144        const char *window_memory;
 145        const char *depth;
 146        const char *threads;
 147        const char *max_pack_size;
 148        int no_reuse_delta;
 149        int no_reuse_object;
 150        int quiet;
 151        int local;
 152};
 153
 154static void prepare_pack_objects(struct child_process *cmd,
 155                                 const struct pack_objects_args *args)
 156{
 157        argv_array_push(&cmd->args, "pack-objects");
 158        if (args->window)
 159                argv_array_pushf(&cmd->args, "--window=%s", args->window);
 160        if (args->window_memory)
 161                argv_array_pushf(&cmd->args, "--window-memory=%s", args->window_memory);
 162        if (args->depth)
 163                argv_array_pushf(&cmd->args, "--depth=%s", args->depth);
 164        if (args->threads)
 165                argv_array_pushf(&cmd->args, "--threads=%s", args->threads);
 166        if (args->max_pack_size)
 167                argv_array_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
 168        if (args->no_reuse_delta)
 169                argv_array_pushf(&cmd->args, "--no-reuse-delta");
 170        if (args->no_reuse_object)
 171                argv_array_pushf(&cmd->args, "--no-reuse-object");
 172        if (args->local)
 173                argv_array_push(&cmd->args,  "--local");
 174        if (args->quiet)
 175                argv_array_push(&cmd->args,  "--quiet");
 176        if (delta_base_offset)
 177                argv_array_push(&cmd->args,  "--delta-base-offset");
 178        argv_array_push(&cmd->args, packtmp);
 179        cmd->git_cmd = 1;
 180        cmd->out = -1;
 181}
 182
 183/*
 184 * Write oid to the given struct child_process's stdin, starting it first if
 185 * necessary.
 186 */
 187static int write_oid(const struct object_id *oid, struct packed_git *pack,
 188                     uint32_t pos, void *data)
 189{
 190        struct child_process *cmd = data;
 191
 192        if (cmd->in == -1) {
 193                if (start_command(cmd))
 194                        die("Could not start pack-objects to repack promisor objects");
 195        }
 196
 197        xwrite(cmd->in, oid_to_hex(oid), GIT_SHA1_HEXSZ);
 198        xwrite(cmd->in, "\n", 1);
 199        return 0;
 200}
 201
 202static void repack_promisor_objects(const struct pack_objects_args *args,
 203                                    struct string_list *names)
 204{
 205        struct child_process cmd = CHILD_PROCESS_INIT;
 206        FILE *out;
 207        struct strbuf line = STRBUF_INIT;
 208
 209        prepare_pack_objects(&cmd, args);
 210        cmd.in = -1;
 211
 212        /*
 213         * NEEDSWORK: Giving pack-objects only the OIDs without any ordering
 214         * hints may result in suboptimal deltas in the resulting pack. See if
 215         * the OIDs can be sent with fake paths such that pack-objects can use a
 216         * {type -> existing pack order} ordering when computing deltas instead
 217         * of a {type -> size} ordering, which may produce better deltas.
 218         */
 219        for_each_packed_object(write_oid, &cmd,
 220                               FOR_EACH_OBJECT_PROMISOR_ONLY);
 221
 222        if (cmd.in == -1)
 223                /* No packed objects; cmd was never started */
 224                return;
 225
 226        close(cmd.in);
 227
 228        out = xfdopen(cmd.out, "r");
 229        while (strbuf_getline_lf(&line, out) != EOF) {
 230                char *promisor_name;
 231                int fd;
 232                if (line.len != 40)
 233                        die("repack: Expecting 40 character sha1 lines only from pack-objects.");
 234                string_list_append(names, line.buf);
 235
 236                /*
 237                 * pack-objects creates the .pack and .idx files, but not the
 238                 * .promisor file. Create the .promisor file, which is empty.
 239                 */
 240                promisor_name = mkpathdup("%s-%s.promisor", packtmp,
 241                                          line.buf);
 242                fd = open(promisor_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
 243                if (fd < 0)
 244                        die_errno("unable to create '%s'", promisor_name);
 245                close(fd);
 246                free(promisor_name);
 247        }
 248        fclose(out);
 249        if (finish_command(&cmd))
 250                die("Could not finish pack-objects to repack promisor objects");
 251}
 252
 253#define ALL_INTO_ONE 1
 254#define LOOSEN_UNREACHABLE 2
 255
 256int cmd_repack(int argc, const char **argv, const char *prefix)
 257{
 258        struct {
 259                const char *name;
 260                unsigned optional:1;
 261        } exts[] = {
 262                {".pack"},
 263                {".idx"},
 264                {".bitmap", 1},
 265                {".promisor", 1},
 266        };
 267        struct child_process cmd = CHILD_PROCESS_INIT;
 268        struct string_list_item *item;
 269        struct string_list names = STRING_LIST_INIT_DUP;
 270        struct string_list rollback = STRING_LIST_INIT_NODUP;
 271        struct string_list existing_packs = STRING_LIST_INIT_DUP;
 272        struct strbuf line = STRBUF_INIT;
 273        int i, ext, ret, failed;
 274        FILE *out;
 275
 276        /* variables to be filled by option parsing */
 277        int pack_everything = 0;
 278        int delete_redundant = 0;
 279        const char *unpack_unreachable = NULL;
 280        int keep_unreachable = 0;
 281        struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
 282        int no_update_server_info = 0;
 283        struct pack_objects_args po_args = {NULL};
 284
 285        struct option builtin_repack_options[] = {
 286                OPT_BIT('a', NULL, &pack_everything,
 287                                N_("pack everything in a single pack"), ALL_INTO_ONE),
 288                OPT_BIT('A', NULL, &pack_everything,
 289                                N_("same as -a, and turn unreachable objects loose"),
 290                                   LOOSEN_UNREACHABLE | ALL_INTO_ONE),
 291                OPT_BOOL('d', NULL, &delete_redundant,
 292                                N_("remove redundant packs, and run git-prune-packed")),
 293                OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
 294                                N_("pass --no-reuse-delta to git-pack-objects")),
 295                OPT_BOOL('F', NULL, &po_args.no_reuse_object,
 296                                N_("pass --no-reuse-object to git-pack-objects")),
 297                OPT_BOOL('n', NULL, &no_update_server_info,
 298                                N_("do not run git-update-server-info")),
 299                OPT__QUIET(&po_args.quiet, N_("be quiet")),
 300                OPT_BOOL('l', "local", &po_args.local,
 301                                N_("pass --local to git-pack-objects")),
 302                OPT_BOOL('b', "write-bitmap-index", &write_bitmaps,
 303                                N_("write bitmap index")),
 304                OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
 305                                N_("with -A, do not loosen objects older than this")),
 306                OPT_BOOL('k', "keep-unreachable", &keep_unreachable,
 307                                N_("with -a, repack unreachable objects")),
 308                OPT_STRING(0, "window", &po_args.window, N_("n"),
 309                                N_("size of the window used for delta compression")),
 310                OPT_STRING(0, "window-memory", &po_args.window_memory, N_("bytes"),
 311                                N_("same as the above, but limit memory size instead of entries count")),
 312                OPT_STRING(0, "depth", &po_args.depth, N_("n"),
 313                                N_("limits the maximum delta depth")),
 314                OPT_STRING(0, "threads", &po_args.threads, N_("n"),
 315                                N_("limits the maximum number of threads")),
 316                OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"),
 317                                N_("maximum size of each packfile")),
 318                OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
 319                                N_("repack objects in packs marked with .keep")),
 320                OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
 321                                N_("do not repack this pack")),
 322                OPT_END()
 323        };
 324
 325        git_config(repack_config, NULL);
 326
 327        argc = parse_options(argc, argv, prefix, builtin_repack_options,
 328                                git_repack_usage, 0);
 329
 330        if (delete_redundant && repository_format_precious_objects)
 331                die(_("cannot delete packs in a precious-objects repo"));
 332
 333        if (keep_unreachable &&
 334            (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE)))
 335                die(_("--keep-unreachable and -A are incompatible"));
 336
 337        if (pack_kept_objects < 0)
 338                pack_kept_objects = write_bitmaps;
 339
 340        if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
 341                die(_(incremental_bitmap_conflict_error));
 342
 343        packdir = mkpathdup("%s/pack", get_object_directory());
 344        packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
 345
 346        sigchain_push_common(remove_pack_on_signal);
 347
 348        prepare_pack_objects(&cmd, &po_args);
 349
 350        argv_array_push(&cmd.args, "--keep-true-parents");
 351        if (!pack_kept_objects)
 352                argv_array_push(&cmd.args, "--honor-pack-keep");
 353        for (i = 0; i < keep_pack_list.nr; i++)
 354                argv_array_pushf(&cmd.args, "--keep-pack=%s",
 355                                 keep_pack_list.items[i].string);
 356        argv_array_push(&cmd.args, "--non-empty");
 357        argv_array_push(&cmd.args, "--all");
 358        argv_array_push(&cmd.args, "--reflog");
 359        argv_array_push(&cmd.args, "--indexed-objects");
 360        if (repository_format_partial_clone)
 361                argv_array_push(&cmd.args, "--exclude-promisor-objects");
 362        if (write_bitmaps)
 363                argv_array_push(&cmd.args, "--write-bitmap-index");
 364
 365        if (pack_everything & ALL_INTO_ONE) {
 366                get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
 367
 368                repack_promisor_objects(&po_args, &names);
 369
 370                if (existing_packs.nr && delete_redundant) {
 371                        if (unpack_unreachable) {
 372                                argv_array_pushf(&cmd.args,
 373                                                "--unpack-unreachable=%s",
 374                                                unpack_unreachable);
 375                                argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
 376                        } else if (pack_everything & LOOSEN_UNREACHABLE) {
 377                                argv_array_push(&cmd.args,
 378                                                "--unpack-unreachable");
 379                        } else if (keep_unreachable) {
 380                                argv_array_push(&cmd.args, "--keep-unreachable");
 381                                argv_array_push(&cmd.args, "--pack-loose-unreachable");
 382                        } else {
 383                                argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
 384                        }
 385                }
 386        } else {
 387                argv_array_push(&cmd.args, "--unpacked");
 388                argv_array_push(&cmd.args, "--incremental");
 389        }
 390
 391        cmd.no_stdin = 1;
 392
 393        ret = start_command(&cmd);
 394        if (ret)
 395                return ret;
 396
 397        out = xfdopen(cmd.out, "r");
 398        while (strbuf_getline_lf(&line, out) != EOF) {
 399                if (line.len != 40)
 400                        die("repack: Expecting 40 character sha1 lines only from pack-objects.");
 401                string_list_append(&names, line.buf);
 402        }
 403        fclose(out);
 404        ret = finish_command(&cmd);
 405        if (ret)
 406                return ret;
 407
 408        if (!names.nr && !po_args.quiet)
 409                printf("Nothing new to pack.\n");
 410
 411        /*
 412         * Ok we have prepared all new packfiles.
 413         * First see if there are packs of the same name and if so
 414         * if we can move them out of the way (this can happen if we
 415         * repacked immediately after packing fully.
 416         */
 417        failed = 0;
 418        for_each_string_list_item(item, &names) {
 419                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
 420                        char *fname, *fname_old;
 421                        fname = mkpathdup("%s/pack-%s%s", packdir,
 422                                                item->string, exts[ext].name);
 423                        if (!file_exists(fname)) {
 424                                free(fname);
 425                                continue;
 426                        }
 427
 428                        fname_old = mkpathdup("%s/old-%s%s", packdir,
 429                                                item->string, exts[ext].name);
 430                        if (file_exists(fname_old))
 431                                if (unlink(fname_old))
 432                                        failed = 1;
 433
 434                        if (!failed && rename(fname, fname_old)) {
 435                                free(fname);
 436                                free(fname_old);
 437                                failed = 1;
 438                                break;
 439                        } else {
 440                                string_list_append(&rollback, fname);
 441                                free(fname_old);
 442                        }
 443                }
 444                if (failed)
 445                        break;
 446        }
 447        if (failed) {
 448                struct string_list rollback_failure = STRING_LIST_INIT_DUP;
 449                for_each_string_list_item(item, &rollback) {
 450                        char *fname, *fname_old;
 451                        fname = mkpathdup("%s/%s", packdir, item->string);
 452                        fname_old = mkpathdup("%s/old-%s", packdir, item->string);
 453                        if (rename(fname_old, fname))
 454                                string_list_append(&rollback_failure, fname);
 455                        free(fname);
 456                        free(fname_old);
 457                }
 458
 459                if (rollback_failure.nr) {
 460                        int i;
 461                        fprintf(stderr,
 462                                "WARNING: Some packs in use have been renamed by\n"
 463                                "WARNING: prefixing old- to their name, in order to\n"
 464                                "WARNING: replace them with the new version of the\n"
 465                                "WARNING: file.  But the operation failed, and the\n"
 466                                "WARNING: attempt to rename them back to their\n"
 467                                "WARNING: original names also failed.\n"
 468                                "WARNING: Please rename them in %s manually:\n", packdir);
 469                        for (i = 0; i < rollback_failure.nr; i++)
 470                                fprintf(stderr, "WARNING:   old-%s -> %s\n",
 471                                        rollback_failure.items[i].string,
 472                                        rollback_failure.items[i].string);
 473                }
 474                exit(1);
 475        }
 476
 477        /* Now the ones with the same name are out of the way... */
 478        for_each_string_list_item(item, &names) {
 479                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
 480                        char *fname, *fname_old;
 481                        struct stat statbuffer;
 482                        int exists = 0;
 483                        fname = mkpathdup("%s/pack-%s%s",
 484                                        packdir, item->string, exts[ext].name);
 485                        fname_old = mkpathdup("%s-%s%s",
 486                                        packtmp, item->string, exts[ext].name);
 487                        if (!stat(fname_old, &statbuffer)) {
 488                                statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
 489                                chmod(fname_old, statbuffer.st_mode);
 490                                exists = 1;
 491                        }
 492                        if (exists || !exts[ext].optional) {
 493                                if (rename(fname_old, fname))
 494                                        die_errno(_("renaming '%s' failed"), fname_old);
 495                        }
 496                        free(fname);
 497                        free(fname_old);
 498                }
 499        }
 500
 501        /* Remove the "old-" files */
 502        for_each_string_list_item(item, &names) {
 503                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
 504                        char *fname;
 505                        fname = mkpathdup("%s/old-%s%s",
 506                                          packdir,
 507                                          item->string,
 508                                          exts[ext].name);
 509                        if (remove_path(fname))
 510                                warning(_("failed to remove '%s'"), fname);
 511                        free(fname);
 512                }
 513        }
 514
 515        /* End of pack replacement. */
 516
 517        reprepare_packed_git(the_repository);
 518
 519        if (delete_redundant) {
 520                int opts = 0;
 521                string_list_sort(&names);
 522                for_each_string_list_item(item, &existing_packs) {
 523                        char *sha1;
 524                        size_t len = strlen(item->string);
 525                        if (len < 40)
 526                                continue;
 527                        sha1 = item->string + len - 40;
 528                        if (!string_list_has_string(&names, sha1))
 529                                remove_redundant_pack(packdir, item->string);
 530                }
 531                if (!po_args.quiet && isatty(2))
 532                        opts |= PRUNE_PACKED_VERBOSE;
 533                prune_packed_objects(opts);
 534
 535                if (!keep_unreachable &&
 536                    (!(pack_everything & LOOSEN_UNREACHABLE) ||
 537                     unpack_unreachable) &&
 538                    is_repository_shallow(the_repository))
 539                        prune_shallow(PRUNE_QUICK);
 540        }
 541
 542        if (!no_update_server_info)
 543                update_server_info(0);
 544        remove_temporary_files();
 545        string_list_clear(&names, 0);
 546        string_list_clear(&rollback, 0);
 547        string_list_clear(&existing_packs, 0);
 548        strbuf_release(&line);
 549
 550        return 0;
 551}