builtin / worktree.con commit grep: recurse in-process using 'struct repository' (f9ee2fc)
   1#include "cache.h"
   2#include "config.h"
   3#include "builtin.h"
   4#include "dir.h"
   5#include "parse-options.h"
   6#include "argv-array.h"
   7#include "branch.h"
   8#include "refs.h"
   9#include "run-command.h"
  10#include "sigchain.h"
  11#include "refs.h"
  12#include "utf8.h"
  13#include "worktree.h"
  14
  15static const char * const worktree_usage[] = {
  16        N_("git worktree add [<options>] <path> [<branch>]"),
  17        N_("git worktree list [<options>]"),
  18        N_("git worktree lock [<options>] <path>"),
  19        N_("git worktree prune [<options>]"),
  20        N_("git worktree unlock <path>"),
  21        NULL
  22};
  23
  24struct add_opts {
  25        int force;
  26        int detach;
  27        int checkout;
  28        int keep_locked;
  29        const char *new_branch;
  30        int force_new_branch;
  31};
  32
  33static int show_only;
  34static int verbose;
  35static timestamp_t expire;
  36
  37static int prune_worktree(const char *id, struct strbuf *reason)
  38{
  39        struct stat st;
  40        char *path;
  41        int fd, len;
  42
  43        if (!is_directory(git_path("worktrees/%s", id))) {
  44                strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
  45                return 1;
  46        }
  47        if (file_exists(git_path("worktrees/%s/locked", id)))
  48                return 0;
  49        if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
  50                strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
  51                return 1;
  52        }
  53        fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
  54        if (fd < 0) {
  55                strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
  56                            id, strerror(errno));
  57                return 1;
  58        }
  59        len = st.st_size;
  60        path = xmallocz(len);
  61        read_in_full(fd, path, len);
  62        close(fd);
  63        while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
  64                len--;
  65        if (!len) {
  66                strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
  67                free(path);
  68                return 1;
  69        }
  70        path[len] = '\0';
  71        if (!file_exists(path)) {
  72                struct stat st_link;
  73                free(path);
  74                /*
  75                 * the repo is moved manually and has not been
  76                 * accessed since?
  77                 */
  78                if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
  79                    st_link.st_nlink > 1)
  80                        return 0;
  81                if (st.st_mtime <= expire) {
  82                        strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
  83                        return 1;
  84                } else {
  85                        return 0;
  86                }
  87        }
  88        free(path);
  89        return 0;
  90}
  91
  92static void prune_worktrees(void)
  93{
  94        struct strbuf reason = STRBUF_INIT;
  95        struct strbuf path = STRBUF_INIT;
  96        DIR *dir = opendir(git_path("worktrees"));
  97        struct dirent *d;
  98        int ret;
  99        if (!dir)
 100                return;
 101        while ((d = readdir(dir)) != NULL) {
 102                if (is_dot_or_dotdot(d->d_name))
 103                        continue;
 104                strbuf_reset(&reason);
 105                if (!prune_worktree(d->d_name, &reason))
 106                        continue;
 107                if (show_only || verbose)
 108                        printf("%s\n", reason.buf);
 109                if (show_only)
 110                        continue;
 111                git_path_buf(&path, "worktrees/%s", d->d_name);
 112                ret = remove_dir_recursively(&path, 0);
 113                if (ret < 0 && errno == ENOTDIR)
 114                        ret = unlink(path.buf);
 115                if (ret)
 116                        error_errno(_("failed to remove '%s'"), path.buf);
 117        }
 118        closedir(dir);
 119        if (!show_only)
 120                rmdir(git_path("worktrees"));
 121        strbuf_release(&reason);
 122        strbuf_release(&path);
 123}
 124
 125static int prune(int ac, const char **av, const char *prefix)
 126{
 127        struct option options[] = {
 128                OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
 129                OPT__VERBOSE(&verbose, N_("report pruned working trees")),
 130                OPT_EXPIRY_DATE(0, "expire", &expire,
 131                                N_("expire working trees older than <time>")),
 132                OPT_END()
 133        };
 134
 135        expire = TIME_MAX;
 136        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 137        if (ac)
 138                usage_with_options(worktree_usage, options);
 139        prune_worktrees();
 140        return 0;
 141}
 142
 143static char *junk_work_tree;
 144static char *junk_git_dir;
 145static int is_junk;
 146static pid_t junk_pid;
 147
 148static void remove_junk(void)
 149{
 150        struct strbuf sb = STRBUF_INIT;
 151        if (!is_junk || getpid() != junk_pid)
 152                return;
 153        if (junk_git_dir) {
 154                strbuf_addstr(&sb, junk_git_dir);
 155                remove_dir_recursively(&sb, 0);
 156                strbuf_reset(&sb);
 157        }
 158        if (junk_work_tree) {
 159                strbuf_addstr(&sb, junk_work_tree);
 160                remove_dir_recursively(&sb, 0);
 161        }
 162        strbuf_release(&sb);
 163}
 164
 165static void remove_junk_on_signal(int signo)
 166{
 167        remove_junk();
 168        sigchain_pop(signo);
 169        raise(signo);
 170}
 171
 172static const char *worktree_basename(const char *path, int *olen)
 173{
 174        const char *name;
 175        int len;
 176
 177        len = strlen(path);
 178        while (len && is_dir_sep(path[len - 1]))
 179                len--;
 180
 181        for (name = path + len - 1; name > path; name--)
 182                if (is_dir_sep(*name)) {
 183                        name++;
 184                        break;
 185                }
 186
 187        *olen = len;
 188        return name;
 189}
 190
 191static int add_worktree(const char *path, const char *refname,
 192                        const struct add_opts *opts)
 193{
 194        struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
 195        struct strbuf sb = STRBUF_INIT;
 196        const char *name;
 197        struct stat st;
 198        struct child_process cp = CHILD_PROCESS_INIT;
 199        struct argv_array child_env = ARGV_ARRAY_INIT;
 200        int counter = 0, len, ret;
 201        struct strbuf symref = STRBUF_INIT;
 202        struct commit *commit = NULL;
 203
 204        if (file_exists(path) && !is_empty_dir(path))
 205                die(_("'%s' already exists"), path);
 206
 207        /* is 'refname' a branch or commit? */
 208        if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
 209                 ref_exists(symref.buf)) { /* it's a branch */
 210                if (!opts->force)
 211                        die_if_checked_out(symref.buf, 0);
 212        } else { /* must be a commit */
 213                commit = lookup_commit_reference_by_name(refname);
 214                if (!commit)
 215                        die(_("invalid reference: %s"), refname);
 216        }
 217
 218        name = worktree_basename(path, &len);
 219        git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
 220        len = sb_repo.len;
 221        if (safe_create_leading_directories_const(sb_repo.buf))
 222                die_errno(_("could not create leading directories of '%s'"),
 223                          sb_repo.buf);
 224        while (!stat(sb_repo.buf, &st)) {
 225                counter++;
 226                strbuf_setlen(&sb_repo, len);
 227                strbuf_addf(&sb_repo, "%d", counter);
 228        }
 229        name = strrchr(sb_repo.buf, '/') + 1;
 230
 231        junk_pid = getpid();
 232        atexit(remove_junk);
 233        sigchain_push_common(remove_junk_on_signal);
 234
 235        if (mkdir(sb_repo.buf, 0777))
 236                die_errno(_("could not create directory of '%s'"), sb_repo.buf);
 237        junk_git_dir = xstrdup(sb_repo.buf);
 238        is_junk = 1;
 239
 240        /*
 241         * lock the incomplete repo so prune won't delete it, unlock
 242         * after the preparation is over.
 243         */
 244        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 245        if (!opts->keep_locked)
 246                write_file(sb.buf, "initializing");
 247        else
 248                write_file(sb.buf, "added with --lock");
 249
 250        strbuf_addf(&sb_git, "%s/.git", path);
 251        if (safe_create_leading_directories_const(sb_git.buf))
 252                die_errno(_("could not create leading directories of '%s'"),
 253                          sb_git.buf);
 254        junk_work_tree = xstrdup(path);
 255
 256        strbuf_reset(&sb);
 257        strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
 258        write_file(sb.buf, "%s", real_path(sb_git.buf));
 259        write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
 260                   real_path(get_git_common_dir()), name);
 261        /*
 262         * This is to keep resolve_ref() happy. We need a valid HEAD
 263         * or is_git_directory() will reject the directory. Any value which
 264         * looks like an object ID will do since it will be immediately
 265         * replaced by the symbolic-ref or update-ref invocation in the new
 266         * worktree.
 267         */
 268        strbuf_reset(&sb);
 269        strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
 270        write_file(sb.buf, "%s", sha1_to_hex(null_sha1));
 271        strbuf_reset(&sb);
 272        strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
 273        write_file(sb.buf, "../..");
 274
 275        fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name);
 276
 277        argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
 278        argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
 279        cp.git_cmd = 1;
 280
 281        if (commit)
 282                argv_array_pushl(&cp.args, "update-ref", "HEAD",
 283                                 oid_to_hex(&commit->object.oid), NULL);
 284        else
 285                argv_array_pushl(&cp.args, "symbolic-ref", "HEAD",
 286                                 symref.buf, NULL);
 287        cp.env = child_env.argv;
 288        ret = run_command(&cp);
 289        if (ret)
 290                goto done;
 291
 292        if (opts->checkout) {
 293                cp.argv = NULL;
 294                argv_array_clear(&cp.args);
 295                argv_array_pushl(&cp.args, "reset", "--hard", NULL);
 296                cp.env = child_env.argv;
 297                ret = run_command(&cp);
 298                if (ret)
 299                        goto done;
 300        }
 301
 302        is_junk = 0;
 303        FREE_AND_NULL(junk_work_tree);
 304        FREE_AND_NULL(junk_git_dir);
 305
 306done:
 307        if (ret || !opts->keep_locked) {
 308                strbuf_reset(&sb);
 309                strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 310                unlink_or_warn(sb.buf);
 311        }
 312        argv_array_clear(&child_env);
 313        strbuf_release(&sb);
 314        strbuf_release(&symref);
 315        strbuf_release(&sb_repo);
 316        strbuf_release(&sb_git);
 317        return ret;
 318}
 319
 320static int add(int ac, const char **av, const char *prefix)
 321{
 322        struct add_opts opts;
 323        const char *new_branch_force = NULL;
 324        char *path;
 325        const char *branch;
 326        struct option options[] = {
 327                OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 328                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
 329                           N_("create a new branch")),
 330                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
 331                           N_("create or reset a branch")),
 332                OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
 333                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 334                OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
 335                OPT_END()
 336        };
 337
 338        memset(&opts, 0, sizeof(opts));
 339        opts.checkout = 1;
 340        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 341        if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1)
 342                die(_("-b, -B, and --detach are mutually exclusive"));
 343        if (ac < 1 || ac > 2)
 344                usage_with_options(worktree_usage, options);
 345
 346        path = prefix_filename(prefix, av[0]);
 347        branch = ac < 2 ? "HEAD" : av[1];
 348
 349        if (!strcmp(branch, "-"))
 350                branch = "@{-1}";
 351
 352        opts.force_new_branch = !!new_branch_force;
 353        if (opts.force_new_branch) {
 354                struct strbuf symref = STRBUF_INIT;
 355
 356                opts.new_branch = new_branch_force;
 357
 358                if (!opts.force &&
 359                    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
 360                    ref_exists(symref.buf))
 361                        die_if_checked_out(symref.buf, 0);
 362                strbuf_release(&symref);
 363        }
 364
 365        if (ac < 2 && !opts.new_branch && !opts.detach) {
 366                int n;
 367                const char *s = worktree_basename(path, &n);
 368                opts.new_branch = xstrndup(s, n);
 369        }
 370
 371        if (opts.new_branch) {
 372                struct child_process cp = CHILD_PROCESS_INIT;
 373                cp.git_cmd = 1;
 374                argv_array_push(&cp.args, "branch");
 375                if (opts.force_new_branch)
 376                        argv_array_push(&cp.args, "--force");
 377                argv_array_push(&cp.args, opts.new_branch);
 378                argv_array_push(&cp.args, branch);
 379                if (run_command(&cp))
 380                        return -1;
 381                branch = opts.new_branch;
 382        }
 383
 384        return add_worktree(path, branch, &opts);
 385}
 386
 387static void show_worktree_porcelain(struct worktree *wt)
 388{
 389        printf("worktree %s\n", wt->path);
 390        if (wt->is_bare)
 391                printf("bare\n");
 392        else {
 393                printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
 394                if (wt->is_detached)
 395                        printf("detached\n");
 396                else if (wt->head_ref)
 397                        printf("branch %s\n", wt->head_ref);
 398        }
 399        printf("\n");
 400}
 401
 402static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
 403{
 404        struct strbuf sb = STRBUF_INIT;
 405        int cur_path_len = strlen(wt->path);
 406        int path_adj = cur_path_len - utf8_strwidth(wt->path);
 407
 408        strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
 409        if (wt->is_bare)
 410                strbuf_addstr(&sb, "(bare)");
 411        else {
 412                strbuf_addf(&sb, "%-*s ", abbrev_len,
 413                                find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
 414                if (wt->is_detached)
 415                        strbuf_addstr(&sb, "(detached HEAD)");
 416                else if (wt->head_ref) {
 417                        char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
 418                        strbuf_addf(&sb, "[%s]", ref);
 419                        free(ref);
 420                } else
 421                        strbuf_addstr(&sb, "(error)");
 422        }
 423        printf("%s\n", sb.buf);
 424
 425        strbuf_release(&sb);
 426}
 427
 428static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
 429{
 430        int i;
 431
 432        for (i = 0; wt[i]; i++) {
 433                int sha1_len;
 434                int path_len = strlen(wt[i]->path);
 435
 436                if (path_len > *maxlen)
 437                        *maxlen = path_len;
 438                sha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev));
 439                if (sha1_len > *abbrev)
 440                        *abbrev = sha1_len;
 441        }
 442}
 443
 444static int list(int ac, const char **av, const char *prefix)
 445{
 446        int porcelain = 0;
 447
 448        struct option options[] = {
 449                OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
 450                OPT_END()
 451        };
 452
 453        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 454        if (ac)
 455                usage_with_options(worktree_usage, options);
 456        else {
 457                struct worktree **worktrees = get_worktrees(GWT_SORT_LINKED);
 458                int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
 459
 460                if (!porcelain)
 461                        measure_widths(worktrees, &abbrev, &path_maxlen);
 462
 463                for (i = 0; worktrees[i]; i++) {
 464                        if (porcelain)
 465                                show_worktree_porcelain(worktrees[i]);
 466                        else
 467                                show_worktree(worktrees[i], path_maxlen, abbrev);
 468                }
 469                free_worktrees(worktrees);
 470        }
 471        return 0;
 472}
 473
 474static int lock_worktree(int ac, const char **av, const char *prefix)
 475{
 476        const char *reason = "", *old_reason;
 477        struct option options[] = {
 478                OPT_STRING(0, "reason", &reason, N_("string"),
 479                           N_("reason for locking")),
 480                OPT_END()
 481        };
 482        struct worktree **worktrees, *wt;
 483
 484        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 485        if (ac != 1)
 486                usage_with_options(worktree_usage, options);
 487
 488        worktrees = get_worktrees(0);
 489        wt = find_worktree(worktrees, prefix, av[0]);
 490        if (!wt)
 491                die(_("'%s' is not a working tree"), av[0]);
 492        if (is_main_worktree(wt))
 493                die(_("The main working tree cannot be locked or unlocked"));
 494
 495        old_reason = is_worktree_locked(wt);
 496        if (old_reason) {
 497                if (*old_reason)
 498                        die(_("'%s' is already locked, reason: %s"),
 499                            av[0], old_reason);
 500                die(_("'%s' is already locked"), av[0]);
 501        }
 502
 503        write_file(git_common_path("worktrees/%s/locked", wt->id),
 504                   "%s", reason);
 505        free_worktrees(worktrees);
 506        return 0;
 507}
 508
 509static int unlock_worktree(int ac, const char **av, const char *prefix)
 510{
 511        struct option options[] = {
 512                OPT_END()
 513        };
 514        struct worktree **worktrees, *wt;
 515        int ret;
 516
 517        ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 518        if (ac != 1)
 519                usage_with_options(worktree_usage, options);
 520
 521        worktrees = get_worktrees(0);
 522        wt = find_worktree(worktrees, prefix, av[0]);
 523        if (!wt)
 524                die(_("'%s' is not a working tree"), av[0]);
 525        if (is_main_worktree(wt))
 526                die(_("The main working tree cannot be locked or unlocked"));
 527        if (!is_worktree_locked(wt))
 528                die(_("'%s' is not locked"), av[0]);
 529        ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
 530        free_worktrees(worktrees);
 531        return ret;
 532}
 533
 534int cmd_worktree(int ac, const char **av, const char *prefix)
 535{
 536        struct option options[] = {
 537                OPT_END()
 538        };
 539
 540        git_config(git_default_config, NULL);
 541
 542        if (ac < 2)
 543                usage_with_options(worktree_usage, options);
 544        if (!prefix)
 545                prefix = "";
 546        if (!strcmp(av[1], "add"))
 547                return add(ac - 1, av + 1, prefix);
 548        if (!strcmp(av[1], "prune"))
 549                return prune(ac - 1, av + 1, prefix);
 550        if (!strcmp(av[1], "list"))
 551                return list(ac - 1, av + 1, prefix);
 552        if (!strcmp(av[1], "lock"))
 553                return lock_worktree(ac - 1, av + 1, prefix);
 554        if (!strcmp(av[1], "unlock"))
 555                return unlock_worktree(ac - 1, av + 1, prefix);
 556        usage_with_options(worktree_usage, options);
 557}