worktree.con commit dir-iterator: release strbuf after use (9b7b029)
   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                if (!fspathcmp(path, real_path((*list)->path)))
 233                        break;
 234        free(path);
 235        free(to_free);
 236        return *list;
 237}
 238
 239int is_main_worktree(const struct worktree *wt)
 240{
 241        return !wt->id;
 242}
 243
 244const char *worktree_lock_reason(struct worktree *wt)
 245{
 246        assert(!is_main_worktree(wt));
 247
 248        if (!wt->lock_reason_valid) {
 249                struct strbuf path = STRBUF_INIT;
 250
 251                strbuf_addstr(&path, worktree_git_path(wt, "locked"));
 252                if (file_exists(path.buf)) {
 253                        struct strbuf lock_reason = STRBUF_INIT;
 254                        if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
 255                                die_errno(_("failed to read '%s'"), path.buf);
 256                        strbuf_trim(&lock_reason);
 257                        wt->lock_reason = strbuf_detach(&lock_reason, NULL);
 258                } else
 259                        wt->lock_reason = NULL;
 260                wt->lock_reason_valid = 1;
 261                strbuf_release(&path);
 262        }
 263
 264        return wt->lock_reason;
 265}
 266
 267/* convenient wrapper to deal with NULL strbuf */
 268static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
 269{
 270        va_list params;
 271
 272        if (!buf)
 273                return;
 274
 275        va_start(params, fmt);
 276        strbuf_vaddf(buf, fmt, params);
 277        va_end(params);
 278}
 279
 280int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
 281                      unsigned flags)
 282{
 283        struct strbuf wt_path = STRBUF_INIT;
 284        char *path = NULL;
 285        int err, ret = -1;
 286
 287        strbuf_addf(&wt_path, "%s/.git", wt->path);
 288
 289        if (is_main_worktree(wt)) {
 290                if (is_directory(wt_path.buf)) {
 291                        ret = 0;
 292                        goto done;
 293                }
 294                /*
 295                 * Main worktree using .git file to point to the
 296                 * repository would make it impossible to know where
 297                 * the actual worktree is if this function is executed
 298                 * from another worktree. No .git file support for now.
 299                 */
 300                strbuf_addf_gently(errmsg,
 301                                   _("'%s' at main working tree is not the repository directory"),
 302                                   wt_path.buf);
 303                goto done;
 304        }
 305
 306        /*
 307         * Make sure "gitdir" file points to a real .git file and that
 308         * file points back here.
 309         */
 310        if (!is_absolute_path(wt->path)) {
 311                strbuf_addf_gently(errmsg,
 312                                   _("'%s' file does not contain absolute path to the working tree location"),
 313                                   git_common_path("worktrees/%s/gitdir", wt->id));
 314                goto done;
 315        }
 316
 317        if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
 318            !file_exists(wt->path)) {
 319                ret = 0;
 320                goto done;
 321        }
 322
 323        if (!file_exists(wt_path.buf)) {
 324                strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf);
 325                goto done;
 326        }
 327
 328        path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err));
 329        if (!path) {
 330                strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"),
 331                                   wt_path.buf, err);
 332                goto done;
 333        }
 334
 335        ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id)));
 336
 337        if (ret)
 338                strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
 339                                   wt->path, git_common_path("worktrees/%s", wt->id));
 340done:
 341        free(path);
 342        strbuf_release(&wt_path);
 343        return ret;
 344}
 345
 346void update_worktree_location(struct worktree *wt, const char *path_)
 347{
 348        struct strbuf path = STRBUF_INIT;
 349
 350        if (is_main_worktree(wt))
 351                BUG("can't relocate main worktree");
 352
 353        strbuf_realpath(&path, path_, 1);
 354        if (fspathcmp(wt->path, path.buf)) {
 355                write_file(git_common_path("worktrees/%s/gitdir", wt->id),
 356                           "%s/.git", path.buf);
 357                free(wt->path);
 358                wt->path = strbuf_detach(&path, NULL);
 359        }
 360        strbuf_release(&path);
 361}
 362
 363int is_worktree_being_rebased(const struct worktree *wt,
 364                              const char *target)
 365{
 366        struct wt_status_state state;
 367        int found_rebase;
 368
 369        memset(&state, 0, sizeof(state));
 370        found_rebase = wt_status_check_rebase(wt, &state) &&
 371                ((state.rebase_in_progress ||
 372                  state.rebase_interactive_in_progress) &&
 373                 state.branch &&
 374                 starts_with(target, "refs/heads/") &&
 375                 !strcmp(state.branch, target + strlen("refs/heads/")));
 376        free(state.branch);
 377        free(state.onto);
 378        return found_rebase;
 379}
 380
 381int is_worktree_being_bisected(const struct worktree *wt,
 382                               const char *target)
 383{
 384        struct wt_status_state state;
 385        int found_rebase;
 386
 387        memset(&state, 0, sizeof(state));
 388        found_rebase = wt_status_check_bisect(wt, &state) &&
 389                state.branch &&
 390                starts_with(target, "refs/heads/") &&
 391                !strcmp(state.branch, target + strlen("refs/heads/"));
 392        free(state.branch);
 393        return found_rebase;
 394}
 395
 396/*
 397 * note: this function should be able to detect shared symref even if
 398 * HEAD is temporarily detached (e.g. in the middle of rebase or
 399 * bisect). New commands that do similar things should update this
 400 * function as well.
 401 */
 402const struct worktree *find_shared_symref(const char *symref,
 403                                          const char *target)
 404{
 405        const struct worktree *existing = NULL;
 406        static struct worktree **worktrees;
 407        int i = 0;
 408
 409        if (worktrees)
 410                free_worktrees(worktrees);
 411        worktrees = get_worktrees(0);
 412
 413        for (i = 0; worktrees[i]; i++) {
 414                struct worktree *wt = worktrees[i];
 415                const char *symref_target;
 416                struct ref_store *refs;
 417                int flags;
 418
 419                if (wt->is_bare)
 420                        continue;
 421
 422                if (wt->is_detached && !strcmp(symref, "HEAD")) {
 423                        if (is_worktree_being_rebased(wt, target)) {
 424                                existing = wt;
 425                                break;
 426                        }
 427                        if (is_worktree_being_bisected(wt, target)) {
 428                                existing = wt;
 429                                break;
 430                        }
 431                }
 432
 433                refs = get_worktree_ref_store(wt);
 434                symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
 435                                                        NULL, &flags);
 436                if ((flags & REF_ISSYMREF) &&
 437                    symref_target && !strcmp(symref_target, target)) {
 438                        existing = wt;
 439                        break;
 440                }
 441        }
 442
 443        return existing;
 444}
 445
 446int submodule_uses_worktrees(const char *path)
 447{
 448        char *submodule_gitdir;
 449        struct strbuf sb = STRBUF_INIT;
 450        DIR *dir;
 451        struct dirent *d;
 452        int ret = 0;
 453        struct repository_format format = REPOSITORY_FORMAT_INIT;
 454
 455        submodule_gitdir = git_pathdup_submodule(path, "%s", "");
 456        if (!submodule_gitdir)
 457                return 0;
 458
 459        /* The env would be set for the superproject. */
 460        get_common_dir_noenv(&sb, submodule_gitdir);
 461        free(submodule_gitdir);
 462
 463        /*
 464         * The check below is only known to be good for repository format
 465         * version 0 at the time of writing this code.
 466         */
 467        strbuf_addstr(&sb, "/config");
 468        read_repository_format(&format, sb.buf);
 469        if (format.version != 0) {
 470                strbuf_release(&sb);
 471                clear_repository_format(&format);
 472                return 1;
 473        }
 474        clear_repository_format(&format);
 475
 476        /* Replace config by worktrees. */
 477        strbuf_setlen(&sb, sb.len - strlen("config"));
 478        strbuf_addstr(&sb, "worktrees");
 479
 480        /* See if there is any file inside the worktrees directory. */
 481        dir = opendir(sb.buf);
 482        strbuf_release(&sb);
 483
 484        if (!dir)
 485                return 0;
 486
 487        while ((d = readdir(dir)) != NULL) {
 488                if (is_dot_or_dotdot(d->d_name))
 489                        continue;
 490
 491                ret = 1;
 492                break;
 493        }
 494        closedir(dir);
 495        return ret;
 496}
 497
 498int parse_worktree_ref(const char *worktree_ref, const char **name,
 499                       int *name_length, const char **ref)
 500{
 501        if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
 502                if (!*worktree_ref)
 503                        return -1;
 504                if (name)
 505                        *name = NULL;
 506                if (name_length)
 507                        *name_length = 0;
 508                if (ref)
 509                        *ref = worktree_ref;
 510                return 0;
 511        }
 512        if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
 513                const char *slash = strchr(worktree_ref, '/');
 514
 515                if (!slash || slash == worktree_ref || !slash[1])
 516                        return -1;
 517                if (name)
 518                        *name = worktree_ref;
 519                if (name_length)
 520                        *name_length = slash - worktree_ref;
 521                if (ref)
 522                        *ref = slash + 1;
 523                return 0;
 524        }
 525        return -1;
 526}
 527
 528void strbuf_worktree_ref(const struct worktree *wt,
 529                         struct strbuf *sb,
 530                         const char *refname)
 531{
 532        switch (ref_type(refname)) {
 533        case REF_TYPE_PSEUDOREF:
 534        case REF_TYPE_PER_WORKTREE:
 535                if (wt && !wt->is_current) {
 536                        if (is_main_worktree(wt))
 537                                strbuf_addstr(sb, "main-worktree/");
 538                        else
 539                                strbuf_addf(sb, "worktrees/%s/", wt->id);
 540                }
 541                break;
 542
 543        case REF_TYPE_MAIN_PSEUDOREF:
 544        case REF_TYPE_OTHER_PSEUDOREF:
 545                break;
 546
 547        case REF_TYPE_NORMAL:
 548                /*
 549                 * For shared refs, don't prefix worktrees/ or
 550                 * main-worktree/. It's not necessary and
 551                 * files-backend.c can't handle it anyway.
 552                 */
 553                break;
 554        }
 555        strbuf_addstr(sb, refname);
 556}
 557
 558const char *worktree_ref(const struct worktree *wt, const char *refname)
 559{
 560        static struct strbuf sb = STRBUF_INIT;
 561
 562        strbuf_reset(&sb);
 563        strbuf_worktree_ref(wt, &sb, refname);
 564        return sb.buf;
 565}
 566
 567int other_head_refs(each_ref_fn fn, void *cb_data)
 568{
 569        struct worktree **worktrees, **p;
 570        int ret = 0;
 571
 572        worktrees = get_worktrees(0);
 573        for (p = worktrees; *p; p++) {
 574                struct worktree *wt = *p;
 575                struct object_id oid;
 576                int flag;
 577
 578                if (wt->is_current)
 579                        continue;
 580
 581                if (!refs_read_ref_full(get_main_ref_store(the_repository),
 582                                        worktree_ref(wt, "HEAD"),
 583                                        RESOLVE_REF_READING,
 584                                        &oid, &flag))
 585                        ret = fn(worktree_ref(wt, "HEAD"), &oid, flag, cb_data);
 586                if (ret)
 587                        break;
 588        }
 589        free_worktrees(worktrees);
 590        return ret;
 591}