worktree.con commit Merge branch 'sg/do-not-skip-non-httpd-tests' (77067b6)
   1#include "cache.h"
   2#include "repository.h"
   3#include "refs.h"
   4#include "strbuf.h"
   5#include "worktree.h"
   6#include "dir.h"
   7#include "wt-status.h"
   8
   9void free_worktrees(struct worktree **worktrees)
  10{
  11        int i = 0;
  12
  13        for (i = 0; worktrees[i]; i++) {
  14                free(worktrees[i]->path);
  15                free(worktrees[i]->id);
  16                free(worktrees[i]->head_ref);
  17                free(worktrees[i]->lock_reason);
  18                free(worktrees[i]);
  19        }
  20        free (worktrees);
  21}
  22
  23/**
  24 * Update head_sha1, head_ref and is_detached of the given worktree
  25 */
  26static void add_head_info(struct worktree *wt)
  27{
  28        int flags;
  29        const char *target;
  30
  31        target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
  32                                         "HEAD",
  33                                         0,
  34                                         &wt->head_oid, &flags);
  35        if (!target)
  36                return;
  37
  38        if (flags & REF_ISSYMREF)
  39                wt->head_ref = xstrdup(target);
  40        else
  41                wt->is_detached = 1;
  42}
  43
  44/**
  45 * get the main worktree
  46 */
  47static struct worktree *get_main_worktree(void)
  48{
  49        struct worktree *worktree = NULL;
  50        struct strbuf path = STRBUF_INIT;
  51        struct strbuf worktree_path = STRBUF_INIT;
  52
  53        strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
  54        if (!strbuf_strip_suffix(&worktree_path, "/.git"))
  55                strbuf_strip_suffix(&worktree_path, "/.");
  56
  57        strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
  58
  59        worktree = xcalloc(1, sizeof(*worktree));
  60        worktree->path = strbuf_detach(&worktree_path, NULL);
  61        /*
  62         * NEEDSWORK: If this function is called from a secondary worktree and
  63         * config.worktree is present, is_bare_repository_cfg will reflect the
  64         * contents of config.worktree, not the contents of the main worktree.
  65         * This means that worktree->is_bare may be set to 0 even if the main
  66         * worktree is configured to be bare.
  67         */
  68        worktree->is_bare = (is_bare_repository_cfg == 1) ||
  69                is_bare_repository();
  70        add_head_info(worktree);
  71
  72        strbuf_release(&path);
  73        strbuf_release(&worktree_path);
  74        return worktree;
  75}
  76
  77static struct worktree *get_linked_worktree(const char *id)
  78{
  79        struct worktree *worktree = NULL;
  80        struct strbuf path = STRBUF_INIT;
  81        struct strbuf worktree_path = STRBUF_INIT;
  82
  83        if (!id)
  84                die("Missing linked worktree name");
  85
  86        strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
  87        if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
  88                /* invalid gitdir file */
  89                goto done;
  90
  91        strbuf_rtrim(&worktree_path);
  92        if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
  93                strbuf_reset(&worktree_path);
  94                strbuf_add_absolute_path(&worktree_path, ".");
  95                strbuf_strip_suffix(&worktree_path, "/.");
  96        }
  97
  98        strbuf_reset(&path);
  99        strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
 100
 101        worktree = xcalloc(1, sizeof(*worktree));
 102        worktree->path = strbuf_detach(&worktree_path, NULL);
 103        worktree->id = xstrdup(id);
 104        add_head_info(worktree);
 105
 106done:
 107        strbuf_release(&path);
 108        strbuf_release(&worktree_path);
 109        return worktree;
 110}
 111
 112static void mark_current_worktree(struct worktree **worktrees)
 113{
 114        char *git_dir = absolute_pathdup(get_git_dir());
 115        int i;
 116
 117        for (i = 0; worktrees[i]; i++) {
 118                struct worktree *wt = worktrees[i];
 119                const char *wt_git_dir = get_worktree_git_dir(wt);
 120
 121                if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
 122                        wt->is_current = 1;
 123                        break;
 124                }
 125        }
 126        free(git_dir);
 127}
 128
 129static int compare_worktree(const void *a_, const void *b_)
 130{
 131        const struct worktree *const *a = a_;
 132        const struct worktree *const *b = b_;
 133        return fspathcmp((*a)->path, (*b)->path);
 134}
 135
 136struct worktree **get_worktrees(unsigned flags)
 137{
 138        struct worktree **list = NULL;
 139        struct strbuf path = STRBUF_INIT;
 140        DIR *dir;
 141        struct dirent *d;
 142        int counter = 0, alloc = 2;
 143
 144        ALLOC_ARRAY(list, alloc);
 145
 146        list[counter++] = get_main_worktree();
 147
 148        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
 149        dir = opendir(path.buf);
 150        strbuf_release(&path);
 151        if (dir) {
 152                while ((d = readdir(dir)) != NULL) {
 153                        struct worktree *linked = NULL;
 154                        if (is_dot_or_dotdot(d->d_name))
 155                                continue;
 156
 157                        if ((linked = get_linked_worktree(d->d_name))) {
 158                                ALLOC_GROW(list, counter + 1, alloc);
 159                                list[counter++] = linked;
 160                        }
 161                }
 162                closedir(dir);
 163        }
 164        ALLOC_GROW(list, counter + 1, alloc);
 165        list[counter] = NULL;
 166
 167        if (flags & GWT_SORT_LINKED)
 168                /*
 169                 * don't sort the first item (main worktree), which will
 170                 * always be the first
 171                 */
 172                QSORT(list + 1, counter - 1, compare_worktree);
 173
 174        mark_current_worktree(list);
 175        return list;
 176}
 177
 178const char *get_worktree_git_dir(const struct worktree *wt)
 179{
 180        if (!wt)
 181                return get_git_dir();
 182        else if (!wt->id)
 183                return get_git_common_dir();
 184        else
 185                return git_common_path("worktrees/%s", wt->id);
 186}
 187
 188static struct worktree *find_worktree_by_suffix(struct worktree **list,
 189                                                const char *suffix)
 190{
 191        struct worktree *found = NULL;
 192        int nr_found = 0, suffixlen;
 193
 194        suffixlen = strlen(suffix);
 195        if (!suffixlen)
 196                return NULL;
 197
 198        for (; *list && nr_found < 2; list++) {
 199                const char      *path    = (*list)->path;
 200                int              pathlen = strlen(path);
 201                int              start   = pathlen - suffixlen;
 202
 203                /* suffix must start at directory boundary */
 204                if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
 205                    !fspathcmp(suffix, path + start)) {
 206                        found = *list;
 207                        nr_found++;
 208                }
 209        }
 210        return nr_found == 1 ? found : NULL;
 211}
 212
 213struct worktree *find_worktree(struct worktree **list,
 214                               const char *prefix,
 215                               const char *arg)
 216{
 217        struct worktree *wt;
 218        char *path;
 219        char *to_free = NULL;
 220
 221        if ((wt = find_worktree_by_suffix(list, arg)))
 222                return wt;
 223
 224        if (prefix)
 225                arg = to_free = prefix_filename(prefix, arg);
 226        path = real_pathdup(arg, 0);
 227        if (!path) {
 228                free(to_free);
 229                return NULL;
 230        }
 231        for (; *list; list++) {
 232                const char *wt_path = real_path_if_valid((*list)->path);
 233
 234                if (wt_path && !fspathcmp(path, wt_path))
 235                        break;
 236        }
 237        free(path);
 238        free(to_free);
 239        return *list;
 240}
 241
 242int is_main_worktree(const struct worktree *wt)
 243{
 244        return !wt->id;
 245}
 246
 247const char *worktree_lock_reason(struct worktree *wt)
 248{
 249        assert(!is_main_worktree(wt));
 250
 251        if (!wt->lock_reason_valid) {
 252                struct strbuf path = STRBUF_INIT;
 253
 254                strbuf_addstr(&path, worktree_git_path(wt, "locked"));
 255                if (file_exists(path.buf)) {
 256                        struct strbuf lock_reason = STRBUF_INIT;
 257                        if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
 258                                die_errno(_("failed to read '%s'"), path.buf);
 259                        strbuf_trim(&lock_reason);
 260                        wt->lock_reason = strbuf_detach(&lock_reason, NULL);
 261                } else
 262                        wt->lock_reason = NULL;
 263                wt->lock_reason_valid = 1;
 264                strbuf_release(&path);
 265        }
 266
 267        return wt->lock_reason;
 268}
 269
 270/* convenient wrapper to deal with NULL strbuf */
 271static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
 272{
 273        va_list params;
 274
 275        if (!buf)
 276                return;
 277
 278        va_start(params, fmt);
 279        strbuf_vaddf(buf, fmt, params);
 280        va_end(params);
 281}
 282
 283int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 284                      unsigned flags)
 285{
 286        struct strbuf wt_path = STRBUF_INIT;
 287        char *path = NULL;
 288        int err, ret = -1;
 289
 290        strbuf_addf(&wt_path, "%s/.git", wt->path);
 291
 292        if (is_main_worktree(wt)) {
 293                if (is_directory(wt_path.buf)) {
 294                        ret = 0;
 295                        goto done;
 296                }
 297                /*
 298                 * Main worktree using .git file to point to the
 299                 * repository would make it impossible to know where
 300                 * the actual worktree is if this function is executed
 301                 * from another worktree. No .git file support for now.
 302                 */
 303                strbuf_addf_gently(errmsg,
 304                                   _("'%s' at main working tree is not the repository directory"),
 305                                   wt_path.buf);
 306                goto done;
 307        }
 308
 309        /*
 310         * Make sure "gitdir" file points to a real .git file and that
 311         * file points back here.
 312         */
 313        if (!is_absolute_path(wt->path)) {
 314                strbuf_addf_gently(errmsg,
 315                                   _("'%s' file does not contain absolute path to the working tree location"),
 316                                   git_common_path("worktrees/%s/gitdir", wt->id));
 317                goto done;
 318        }
 319
 320        if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
 321            !file_exists(wt->path)) {
 322                ret = 0;
 323                goto done;
 324        }
 325
 326        if (!file_exists(wt_path.buf)) {
 327                strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf);
 328                goto done;
 329        }
 330
 331        path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err));
 332        if (!path) {
 333                strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"),
 334                                   wt_path.buf, err);
 335                goto done;
 336        }
 337
 338        ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id)));
 339
 340        if (ret)
 341                strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
 342                                   wt->path, git_common_path("worktrees/%s", wt->id));
 343done:
 344        free(path);
 345        strbuf_release(&wt_path);
 346        return ret;
 347}
 348
 349void update_worktree_location(struct worktree *wt, const char *path_)
 350{
 351        struct strbuf path = STRBUF_INIT;
 352
 353        if (is_main_worktree(wt))
 354                BUG("can't relocate main worktree");
 355
 356        strbuf_realpath(&path, path_, 1);
 357        if (fspathcmp(wt->path, path.buf)) {
 358                write_file(git_common_path("worktrees/%s/gitdir", wt->id),
 359                           "%s/.git", path.buf);
 360                free(wt->path);
 361                wt->path = strbuf_detach(&path, NULL);
 362        }
 363        strbuf_release(&path);
 364}
 365
 366int is_worktree_being_rebased(const struct worktree *wt,
 367                              const char *target)
 368{
 369        struct wt_status_state state;
 370        int found_rebase;
 371
 372        memset(&state, 0, sizeof(state));
 373        found_rebase = wt_status_check_rebase(wt, &state) &&
 374                ((state.rebase_in_progress ||
 375                  state.rebase_interactive_in_progress) &&
 376                 state.branch &&
 377                 starts_with(target, "refs/heads/") &&
 378                 !strcmp(state.branch, target + strlen("refs/heads/")));
 379        free(state.branch);
 380        free(state.onto);
 381        return found_rebase;
 382}
 383
 384int is_worktree_being_bisected(const struct worktree *wt,
 385                               const char *target)
 386{
 387        struct wt_status_state state;
 388        int found_rebase;
 389
 390        memset(&state, 0, sizeof(state));
 391        found_rebase = wt_status_check_bisect(wt, &state) &&
 392                state.branch &&
 393                starts_with(target, "refs/heads/") &&
 394                !strcmp(state.branch, target + strlen("refs/heads/"));
 395        free(state.branch);
 396        return found_rebase;
 397}
 398
 399/*
 400 * note: this function should be able to detect shared symref even if
 401 * HEAD is temporarily detached (e.g. in the middle of rebase or
 402 * bisect). New commands that do similar things should update this
 403 * function as well.
 404 */
 405const struct worktree *find_shared_symref(const char *symref,
 406                                          const char *target)
 407{
 408        const struct worktree *existing = NULL;
 409        static struct worktree **worktrees;
 410        int i = 0;
 411
 412        if (worktrees)
 413                free_worktrees(worktrees);
 414        worktrees = get_worktrees(0);
 415
 416        for (i = 0; worktrees[i]; i++) {
 417                struct worktree *wt = worktrees[i];
 418                const char *symref_target;
 419                struct ref_store *refs;
 420                int flags;
 421
 422                if (wt->is_bare)
 423                        continue;
 424
 425                if (wt->is_detached && !strcmp(symref, "HEAD")) {
 426                        if (is_worktree_being_rebased(wt, target)) {
 427                                existing = wt;
 428                                break;
 429                        }
 430                        if (is_worktree_being_bisected(wt, target)) {
 431                                existing = wt;
 432                                break;
 433                        }
 434                }
 435
 436                refs = get_worktree_ref_store(wt);
 437                symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
 438                                                        NULL, &flags);
 439                if ((flags & REF_ISSYMREF) &&
 440                    symref_target && !strcmp(symref_target, target)) {
 441                        existing = wt;
 442                        break;
 443                }
 444        }
 445
 446        return existing;
 447}
 448
 449int submodule_uses_worktrees(const char *path)
 450{
 451        char *submodule_gitdir;
 452        struct strbuf sb = STRBUF_INIT;
 453        DIR *dir;
 454        struct dirent *d;
 455        int ret = 0;
 456        struct repository_format format = REPOSITORY_FORMAT_INIT;
 457
 458        submodule_gitdir = git_pathdup_submodule(path, "%s", "");
 459        if (!submodule_gitdir)
 460                return 0;
 461
 462        /* The env would be set for the superproject. */
 463        get_common_dir_noenv(&sb, submodule_gitdir);
 464        free(submodule_gitdir);
 465
 466        /*
 467         * The check below is only known to be good for repository format
 468         * version 0 at the time of writing this code.
 469         */
 470        strbuf_addstr(&sb, "/config");
 471        read_repository_format(&format, sb.buf);
 472        if (format.version != 0) {
 473                strbuf_release(&sb);
 474                clear_repository_format(&format);
 475                return 1;
 476        }
 477        clear_repository_format(&format);
 478
 479        /* Replace config by worktrees. */
 480        strbuf_setlen(&sb, sb.len - strlen("config"));
 481        strbuf_addstr(&sb, "worktrees");
 482
 483        /* See if there is any file inside the worktrees directory. */
 484        dir = opendir(sb.buf);
 485        strbuf_release(&sb);
 486
 487        if (!dir)
 488                return 0;
 489
 490        while ((d = readdir(dir)) != NULL) {
 491                if (is_dot_or_dotdot(d->d_name))
 492                        continue;
 493
 494                ret = 1;
 495                break;
 496        }
 497        closedir(dir);
 498        return ret;
 499}
 500
 501int parse_worktree_ref(const char *worktree_ref, const char **name,
 502                       int *name_length, const char **ref)
 503{
 504        if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
 505                if (!*worktree_ref)
 506                        return -1;
 507                if (name)
 508                        *name = NULL;
 509                if (name_length)
 510                        *name_length = 0;
 511                if (ref)
 512                        *ref = worktree_ref;
 513                return 0;
 514        }
 515        if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
 516                const char *slash = strchr(worktree_ref, '/');
 517
 518                if (!slash || slash == worktree_ref || !slash[1])
 519                        return -1;
 520                if (name)
 521                        *name = worktree_ref;
 522                if (name_length)
 523                        *name_length = slash - worktree_ref;
 524                if (ref)
 525                        *ref = slash + 1;
 526                return 0;
 527        }
 528        return -1;
 529}
 530
 531void strbuf_worktree_ref(const struct worktree *wt,
 532                         struct strbuf *sb,
 533                         const char *refname)
 534{
 535        switch (ref_type(refname)) {
 536        case REF_TYPE_PSEUDOREF:
 537        case REF_TYPE_PER_WORKTREE:
 538                if (wt && !wt->is_current) {
 539                        if (is_main_worktree(wt))
 540                                strbuf_addstr(sb, "main-worktree/");
 541                        else
 542                                strbuf_addf(sb, "worktrees/%s/", wt->id);
 543                }
 544                break;
 545
 546        case REF_TYPE_MAIN_PSEUDOREF:
 547        case REF_TYPE_OTHER_PSEUDOREF:
 548                break;
 549
 550        case REF_TYPE_NORMAL:
 551                /*
 552                 * For shared refs, don't prefix worktrees/ or
 553                 * main-worktree/. It's not necessary and
 554                 * files-backend.c can't handle it anyway.
 555                 */
 556                break;
 557        }
 558        strbuf_addstr(sb, refname);
 559}
 560
 561const char *worktree_ref(const struct worktree *wt, const char *refname)
 562{
 563        static struct strbuf sb = STRBUF_INIT;
 564
 565        strbuf_reset(&sb);
 566        strbuf_worktree_ref(wt, &sb, refname);
 567        return sb.buf;
 568}
 569
 570int other_head_refs(each_ref_fn fn, void *cb_data)
 571{
 572        struct worktree **worktrees, **p;
 573        int ret = 0;
 574
 575        worktrees = get_worktrees(0);
 576        for (p = worktrees; *p; p++) {
 577                struct worktree *wt = *p;
 578                struct object_id oid;
 579                int flag;
 580
 581                if (wt->is_current)
 582                        continue;
 583
 584                if (!refs_read_ref_full(get_main_ref_store(the_repository),
 585                                        worktree_ref(wt, "HEAD"),
 586                                        RESOLVE_REF_READING,
 587                                        &oid, &flag))
 588                        ret = fn(worktree_ref(wt, "HEAD"), &oid, flag, cb_data);
 589                if (ret)
 590                        break;
 591        }
 592        free_worktrees(worktrees);
 593        return ret;
 594}