builtin / repack.con commit index-pack: use strip_suffix to avoid magic numbers (592ce20)
   1#include "builtin.h"
   2#include "cache.h"
   3#include "dir.h"
   4#include "parse-options.h"
   5#include "run-command.h"
   6#include "sigchain.h"
   7#include "strbuf.h"
   8#include "string-list.h"
   9#include "argv-array.h"
  10
  11static int delta_base_offset = 1;
  12static int pack_kept_objects = -1;
  13static char *packdir, *packtmp;
  14
  15static const char *const git_repack_usage[] = {
  16        N_("git repack [options]"),
  17        NULL
  18};
  19
  20static int repack_config(const char *var, const char *value, void *cb)
  21{
  22        if (!strcmp(var, "repack.usedeltabaseoffset")) {
  23                delta_base_offset = git_config_bool(var, value);
  24                return 0;
  25        }
  26        if (!strcmp(var, "repack.packkeptobjects")) {
  27                pack_kept_objects = git_config_bool(var, value);
  28                return 0;
  29        }
  30        return git_default_config(var, value, cb);
  31}
  32
  33/*
  34 * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files.
  35 */
  36static void remove_temporary_files(void)
  37{
  38        struct strbuf buf = STRBUF_INIT;
  39        size_t dirlen, prefixlen;
  40        DIR *dir;
  41        struct dirent *e;
  42
  43        dir = opendir(packdir);
  44        if (!dir)
  45                return;
  46
  47        /* Point at the slash at the end of ".../objects/pack/" */
  48        dirlen = strlen(packdir) + 1;
  49        strbuf_addstr(&buf, packtmp);
  50        /* Hold the length of  ".tmp-%d-pack-" */
  51        prefixlen = buf.len - dirlen;
  52
  53        while ((e = readdir(dir))) {
  54                if (strncmp(e->d_name, buf.buf + dirlen, prefixlen))
  55                        continue;
  56                strbuf_setlen(&buf, dirlen);
  57                strbuf_addstr(&buf, e->d_name);
  58                unlink(buf.buf);
  59        }
  60        closedir(dir);
  61        strbuf_release(&buf);
  62}
  63
  64static void remove_pack_on_signal(int signo)
  65{
  66        remove_temporary_files();
  67        sigchain_pop(signo);
  68        raise(signo);
  69}
  70
  71/*
  72 * Adds all packs hex strings to the fname list, which do not
  73 * have a corresponding .keep file.
  74 */
  75static void get_non_kept_pack_filenames(struct string_list *fname_list)
  76{
  77        DIR *dir;
  78        struct dirent *e;
  79        char *fname;
  80
  81        if (!(dir = opendir(packdir)))
  82                return;
  83
  84        while ((e = readdir(dir)) != NULL) {
  85                size_t len;
  86                if (!strip_suffix(e->d_name, ".pack", &len))
  87                        continue;
  88
  89                fname = xmemdupz(e->d_name, len);
  90
  91                if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
  92                        string_list_append_nodup(fname_list, fname);
  93                else
  94                        free(fname);
  95        }
  96        closedir(dir);
  97}
  98
  99static void remove_redundant_pack(const char *dir_name, const char *base_name)
 100{
 101        const char *exts[] = {".pack", ".idx", ".keep", ".bitmap"};
 102        int i;
 103        struct strbuf buf = STRBUF_INIT;
 104        size_t plen;
 105
 106        strbuf_addf(&buf, "%s/%s", dir_name, base_name);
 107        plen = buf.len;
 108
 109        for (i = 0; i < ARRAY_SIZE(exts); i++) {
 110                strbuf_setlen(&buf, plen);
 111                strbuf_addstr(&buf, exts[i]);
 112                unlink(buf.buf);
 113        }
 114        strbuf_release(&buf);
 115}
 116
 117#define ALL_INTO_ONE 1
 118#define LOOSEN_UNREACHABLE 2
 119
 120int cmd_repack(int argc, const char **argv, const char *prefix)
 121{
 122        struct {
 123                const char *name;
 124                unsigned optional:1;
 125        } exts[] = {
 126                {".pack"},
 127                {".idx"},
 128                {".bitmap", 1},
 129        };
 130        struct child_process cmd;
 131        struct string_list_item *item;
 132        struct argv_array cmd_args = ARGV_ARRAY_INIT;
 133        struct string_list names = STRING_LIST_INIT_DUP;
 134        struct string_list rollback = STRING_LIST_INIT_NODUP;
 135        struct string_list existing_packs = STRING_LIST_INIT_DUP;
 136        struct strbuf line = STRBUF_INIT;
 137        int ext, ret, failed;
 138        FILE *out;
 139
 140        /* variables to be filled by option parsing */
 141        int pack_everything = 0;
 142        int delete_redundant = 0;
 143        const char *unpack_unreachable = NULL;
 144        const char *window = NULL, *window_memory = NULL;
 145        const char *depth = NULL;
 146        const char *max_pack_size = NULL;
 147        int no_reuse_delta = 0, no_reuse_object = 0;
 148        int no_update_server_info = 0;
 149        int quiet = 0;
 150        int local = 0;
 151        int write_bitmap = -1;
 152
 153        struct option builtin_repack_options[] = {
 154                OPT_BIT('a', NULL, &pack_everything,
 155                                N_("pack everything in a single pack"), ALL_INTO_ONE),
 156                OPT_BIT('A', NULL, &pack_everything,
 157                                N_("same as -a, and turn unreachable objects loose"),
 158                                   LOOSEN_UNREACHABLE | ALL_INTO_ONE),
 159                OPT_BOOL('d', NULL, &delete_redundant,
 160                                N_("remove redundant packs, and run git-prune-packed")),
 161                OPT_BOOL('f', NULL, &no_reuse_delta,
 162                                N_("pass --no-reuse-delta to git-pack-objects")),
 163                OPT_BOOL('F', NULL, &no_reuse_object,
 164                                N_("pass --no-reuse-object to git-pack-objects")),
 165                OPT_BOOL('n', NULL, &no_update_server_info,
 166                                N_("do not run git-update-server-info")),
 167                OPT__QUIET(&quiet, N_("be quiet")),
 168                OPT_BOOL('l', "local", &local,
 169                                N_("pass --local to git-pack-objects")),
 170                OPT_BOOL('b', "write-bitmap-index", &write_bitmap,
 171                                N_("write bitmap index")),
 172                OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"),
 173                                N_("with -A, do not loosen objects older than this")),
 174                OPT_STRING(0, "window", &window, N_("n"),
 175                                N_("size of the window used for delta compression")),
 176                OPT_STRING(0, "window-memory", &window_memory, N_("bytes"),
 177                                N_("same as the above, but limit memory size instead of entries count")),
 178                OPT_STRING(0, "depth", &depth, N_("n"),
 179                                N_("limits the maximum delta depth")),
 180                OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"),
 181                                N_("maximum size of each packfile")),
 182                OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
 183                                N_("repack objects in packs marked with .keep")),
 184                OPT_END()
 185        };
 186
 187        git_config(repack_config, NULL);
 188
 189        argc = parse_options(argc, argv, prefix, builtin_repack_options,
 190                                git_repack_usage, 0);
 191
 192        if (pack_kept_objects < 0)
 193                pack_kept_objects = write_bitmap;
 194
 195        packdir = mkpathdup("%s/pack", get_object_directory());
 196        packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid());
 197
 198        sigchain_push_common(remove_pack_on_signal);
 199
 200        argv_array_push(&cmd_args, "pack-objects");
 201        argv_array_push(&cmd_args, "--keep-true-parents");
 202        if (!pack_kept_objects)
 203                argv_array_push(&cmd_args, "--honor-pack-keep");
 204        argv_array_push(&cmd_args, "--non-empty");
 205        argv_array_push(&cmd_args, "--all");
 206        argv_array_push(&cmd_args, "--reflog");
 207        if (window)
 208                argv_array_pushf(&cmd_args, "--window=%s", window);
 209        if (window_memory)
 210                argv_array_pushf(&cmd_args, "--window-memory=%s", window_memory);
 211        if (depth)
 212                argv_array_pushf(&cmd_args, "--depth=%s", depth);
 213        if (max_pack_size)
 214                argv_array_pushf(&cmd_args, "--max-pack-size=%s", max_pack_size);
 215        if (no_reuse_delta)
 216                argv_array_pushf(&cmd_args, "--no-reuse-delta");
 217        if (no_reuse_object)
 218                argv_array_pushf(&cmd_args, "--no-reuse-object");
 219        if (write_bitmap >= 0)
 220                argv_array_pushf(&cmd_args, "--%swrite-bitmap-index",
 221                                 write_bitmap ? "" : "no-");
 222
 223        if (pack_everything & ALL_INTO_ONE) {
 224                get_non_kept_pack_filenames(&existing_packs);
 225
 226                if (existing_packs.nr && delete_redundant) {
 227                        if (unpack_unreachable)
 228                                argv_array_pushf(&cmd_args,
 229                                                "--unpack-unreachable=%s",
 230                                                unpack_unreachable);
 231                        else if (pack_everything & LOOSEN_UNREACHABLE)
 232                                argv_array_push(&cmd_args,
 233                                                "--unpack-unreachable");
 234                }
 235        } else {
 236                argv_array_push(&cmd_args, "--unpacked");
 237                argv_array_push(&cmd_args, "--incremental");
 238        }
 239
 240        if (local)
 241                argv_array_push(&cmd_args,  "--local");
 242        if (quiet)
 243                argv_array_push(&cmd_args,  "--quiet");
 244        if (delta_base_offset)
 245                argv_array_push(&cmd_args,  "--delta-base-offset");
 246
 247        argv_array_push(&cmd_args, packtmp);
 248
 249        memset(&cmd, 0, sizeof(cmd));
 250        cmd.argv = cmd_args.argv;
 251        cmd.git_cmd = 1;
 252        cmd.out = -1;
 253        cmd.no_stdin = 1;
 254
 255        ret = start_command(&cmd);
 256        if (ret)
 257                return ret;
 258
 259        out = xfdopen(cmd.out, "r");
 260        while (strbuf_getline(&line, out, '\n') != EOF) {
 261                if (line.len != 40)
 262                        die("repack: Expecting 40 character sha1 lines only from pack-objects.");
 263                string_list_append(&names, line.buf);
 264        }
 265        fclose(out);
 266        ret = finish_command(&cmd);
 267        if (ret)
 268                return ret;
 269        argv_array_clear(&cmd_args);
 270
 271        if (!names.nr && !quiet)
 272                printf("Nothing new to pack.\n");
 273
 274        /*
 275         * Ok we have prepared all new packfiles.
 276         * First see if there are packs of the same name and if so
 277         * if we can move them out of the way (this can happen if we
 278         * repacked immediately after packing fully.
 279         */
 280        failed = 0;
 281        for_each_string_list_item(item, &names) {
 282                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
 283                        char *fname, *fname_old;
 284                        fname = mkpathdup("%s/pack-%s%s", packdir,
 285                                                item->string, exts[ext].name);
 286                        if (!file_exists(fname)) {
 287                                free(fname);
 288                                continue;
 289                        }
 290
 291                        fname_old = mkpath("%s/old-%s%s", packdir,
 292                                                item->string, exts[ext].name);
 293                        if (file_exists(fname_old))
 294                                if (unlink(fname_old))
 295                                        failed = 1;
 296
 297                        if (!failed && rename(fname, fname_old)) {
 298                                free(fname);
 299                                failed = 1;
 300                                break;
 301                        } else {
 302                                string_list_append(&rollback, fname);
 303                        }
 304                }
 305                if (failed)
 306                        break;
 307        }
 308        if (failed) {
 309                struct string_list rollback_failure = STRING_LIST_INIT_DUP;
 310                for_each_string_list_item(item, &rollback) {
 311                        char *fname, *fname_old;
 312                        fname = mkpathdup("%s/%s", packdir, item->string);
 313                        fname_old = mkpath("%s/old-%s", packdir, item->string);
 314                        if (rename(fname_old, fname))
 315                                string_list_append(&rollback_failure, fname);
 316                        free(fname);
 317                }
 318
 319                if (rollback_failure.nr) {
 320                        int i;
 321                        fprintf(stderr,
 322                                "WARNING: Some packs in use have been renamed by\n"
 323                                "WARNING: prefixing old- to their name, in order to\n"
 324                                "WARNING: replace them with the new version of the\n"
 325                                "WARNING: file.  But the operation failed, and the\n"
 326                                "WARNING: attempt to rename them back to their\n"
 327                                "WARNING: original names also failed.\n"
 328                                "WARNING: Please rename them in %s manually:\n", packdir);
 329                        for (i = 0; i < rollback_failure.nr; i++)
 330                                fprintf(stderr, "WARNING:   old-%s -> %s\n",
 331                                        rollback_failure.items[i].string,
 332                                        rollback_failure.items[i].string);
 333                }
 334                exit(1);
 335        }
 336
 337        /* Now the ones with the same name are out of the way... */
 338        for_each_string_list_item(item, &names) {
 339                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
 340                        char *fname, *fname_old;
 341                        struct stat statbuffer;
 342                        int exists = 0;
 343                        fname = mkpathdup("%s/pack-%s%s",
 344                                        packdir, item->string, exts[ext].name);
 345                        fname_old = mkpathdup("%s-%s%s",
 346                                        packtmp, item->string, exts[ext].name);
 347                        if (!stat(fname_old, &statbuffer)) {
 348                                statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
 349                                chmod(fname_old, statbuffer.st_mode);
 350                                exists = 1;
 351                        }
 352                        if (exists || !exts[ext].optional) {
 353                                if (rename(fname_old, fname))
 354                                        die_errno(_("renaming '%s' failed"), fname_old);
 355                        }
 356                        free(fname);
 357                        free(fname_old);
 358                }
 359        }
 360
 361        /* Remove the "old-" files */
 362        for_each_string_list_item(item, &names) {
 363                for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
 364                        char *fname;
 365                        fname = mkpath("%s/old-%s%s",
 366                                        packdir,
 367                                        item->string,
 368                                        exts[ext].name);
 369                        if (remove_path(fname))
 370                                warning(_("removing '%s' failed"), fname);
 371                }
 372        }
 373
 374        /* End of pack replacement. */
 375
 376        if (delete_redundant) {
 377                sort_string_list(&names);
 378                for_each_string_list_item(item, &existing_packs) {
 379                        char *sha1;
 380                        size_t len = strlen(item->string);
 381                        if (len < 40)
 382                                continue;
 383                        sha1 = item->string + len - 40;
 384                        if (!string_list_has_string(&names, sha1))
 385                                remove_redundant_pack(packdir, item->string);
 386                }
 387                argv_array_push(&cmd_args, "prune-packed");
 388                if (quiet)
 389                        argv_array_push(&cmd_args, "--quiet");
 390
 391                memset(&cmd, 0, sizeof(cmd));
 392                cmd.argv = cmd_args.argv;
 393                cmd.git_cmd = 1;
 394                run_command(&cmd);
 395                argv_array_clear(&cmd_args);
 396        }
 397
 398        if (!no_update_server_info) {
 399                argv_array_push(&cmd_args, "update-server-info");
 400                memset(&cmd, 0, sizeof(cmd));
 401                cmd.argv = cmd_args.argv;
 402                cmd.git_cmd = 1;
 403                run_command(&cmd);
 404                argv_array_clear(&cmd_args);
 405        }
 406        remove_temporary_files();
 407        string_list_clear(&names, 0);
 408        string_list_clear(&rollback, 0);
 409        string_list_clear(&existing_packs, 0);
 410        strbuf_release(&line);
 411
 412        return 0;
 413}