static int lock_worktree(int ac, const char **av, const char *prefix) { const char *reason = "", *old_reason; struct option options[] = { OPT_STRING(0, "reason", &reason, N_("string"), N_("reason for locking")), OPT_END() }; struct worktree **worktrees, *wt; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac != 1) usage_with_options(worktree_usage, options); worktrees = get_worktrees(0); wt = find_worktree(worktrees, prefix, av[0]); if (!wt) die(_("'%s' is not a working tree"), av[0]); if (is_main_worktree(wt)) die(_("The main working tree cannot be locked or unlocked")); old_reason = is_worktree_locked(wt); if (old_reason) { if (*old_reason) die(_("'%s' is already locked, reason: %s"), av[0], old_reason); die(_("'%s' is already locked"), av[0]); } write_file(git_common_path("worktrees/%s/locked", wt->id), "%s", reason); free_worktrees(worktrees); return 0; }
static int move_worktree(int ac, const char **av, const char *prefix) { struct option options[] = { OPT_END() }; struct worktree **worktrees, *wt; struct strbuf dst = STRBUF_INIT; struct strbuf errmsg = STRBUF_INIT; const char *reason; char *path; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac != 2) usage_with_options(worktree_usage, options); path = prefix_filename(prefix, av[1]); strbuf_addstr(&dst, path); free(path); worktrees = get_worktrees(0); wt = find_worktree(worktrees, prefix, av[0]); if (!wt) die(_("'%s' is not a working tree"), av[0]); if (is_main_worktree(wt)) die(_("'%s' is a main working tree"), av[0]); if (is_directory(dst.buf)) { const char *sep = find_last_dir_sep(wt->path); if (!sep) die(_("could not figure out destination name from '%s'"), wt->path); strbuf_trim_trailing_dir_sep(&dst); strbuf_addstr(&dst, sep); } if (file_exists(dst.buf)) die(_("target '%s' already exists"), dst.buf); validate_no_submodules(wt); reason = is_worktree_locked(wt); if (reason) { if (*reason) die(_("cannot move a locked working tree, lock reason: %s"), reason); die(_("cannot move a locked working tree")); } if (validate_worktree(wt, &errmsg, 0)) die(_("validation failed, cannot move working tree: %s"), errmsg.buf); strbuf_release(&errmsg); if (rename(wt->path, dst.buf) == -1) die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf); update_worktree_location(wt, dst.buf); strbuf_release(&dst); free_worktrees(worktrees); return 0; }
void strbuf_worktree_ref(const struct worktree *wt, struct strbuf *sb, const char *refname) { switch (ref_type(refname)) { case REF_TYPE_PSEUDOREF: case REF_TYPE_PER_WORKTREE: if (wt && !wt->is_current) { if (is_main_worktree(wt)) strbuf_addstr(sb, "main-worktree/"); else strbuf_addf(sb, "worktrees/%s/", wt->id); } break; case REF_TYPE_MAIN_PSEUDOREF: case REF_TYPE_OTHER_PSEUDOREF: break; case REF_TYPE_NORMAL: /* * For shared refs, don't prefix worktrees/ or * main-worktree/. It's not necessary and * files-backend.c can't handle it anyway. */ break; } strbuf_addstr(sb, refname); }
static int remove_worktree(int ac, const char **av, const char *prefix) { int force = 0; struct option options[] = { OPT_BOOL(0, "force", &force, N_("force removing even if the worktree is dirty")), OPT_END() }; struct worktree **worktrees, *wt; struct strbuf errmsg = STRBUF_INIT; const char *reason; int ret = 0; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac != 1) usage_with_options(worktree_usage, options); worktrees = get_worktrees(0); wt = find_worktree(worktrees, prefix, av[0]); if (!wt) die(_("'%s' is not a working tree"), av[0]); if (is_main_worktree(wt)) die(_("'%s' is a main working tree"), av[0]); reason = is_worktree_locked(wt); if (reason) { if (*reason) die(_("cannot remove a locked working tree, lock reason: %s"), reason); die(_("cannot remove a locked working tree")); } if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK)) die(_("validation failed, cannot remove working tree: %s"), errmsg.buf); strbuf_release(&errmsg); if (file_exists(wt->path)) { if (!force) check_clean_worktree(wt, av[0]); ret |= delete_git_work_tree(wt); } /* * continue on even if ret is non-zero, there's no going back * from here. */ ret |= delete_git_dir(wt); free_worktrees(worktrees); return ret; }
void update_worktree_location(struct worktree *wt, const char *path_) { struct strbuf path = STRBUF_INIT; if (is_main_worktree(wt)) BUG("can't relocate main worktree"); strbuf_realpath(&path, path_, 1); if (fspathcmp(wt->path, path.buf)) { write_file(git_common_path("worktrees/%s/gitdir", wt->id), "%s/.git", path.buf); free(wt->path); wt->path = strbuf_detach(&path, NULL); } strbuf_release(&path); }
const char *worktree_lock_reason(struct worktree *wt) { assert(!is_main_worktree(wt)); if (!wt->lock_reason_valid) { struct strbuf path = STRBUF_INIT; strbuf_addstr(&path, worktree_git_path(wt, "locked")); if (file_exists(path.buf)) { struct strbuf lock_reason = STRBUF_INIT; if (strbuf_read_file(&lock_reason, path.buf, 0) < 0) die_errno(_("failed to read '%s'"), path.buf); strbuf_trim(&lock_reason); wt->lock_reason = strbuf_detach(&lock_reason, NULL); } else wt->lock_reason = NULL; wt->lock_reason_valid = 1; strbuf_release(&path); } return wt->lock_reason; }
static int unlock_worktree(int ac, const char **av, const char *prefix) { struct option options[] = { OPT_END() }; struct worktree **worktrees, *wt; int ret; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac != 1) usage_with_options(worktree_usage, options); worktrees = get_worktrees(0); wt = find_worktree(worktrees, prefix, av[0]); if (!wt) die(_("'%s' is not a working tree"), av[0]); if (is_main_worktree(wt)) die(_("The main working tree cannot be locked or unlocked")); if (!is_worktree_locked(wt)) die(_("'%s' is not locked"), av[0]); ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id)); free_worktrees(worktrees); return ret; }
int validate_worktree(const struct worktree *wt, struct strbuf *errmsg, unsigned flags) { struct strbuf wt_path = STRBUF_INIT; char *path = NULL; int err, ret = -1; strbuf_addf(&wt_path, "%s/.git", wt->path); if (is_main_worktree(wt)) { if (is_directory(wt_path.buf)) { ret = 0; goto done; } /* * Main worktree using .git file to point to the * repository would make it impossible to know where * the actual worktree is if this function is executed * from another worktree. No .git file support for now. */ strbuf_addf_gently(errmsg, _("'%s' at main working tree is not the repository directory"), wt_path.buf); goto done; } /* * Make sure "gitdir" file points to a real .git file and that * file points back here. */ if (!is_absolute_path(wt->path)) { strbuf_addf_gently(errmsg, _("'%s' file does not contain absolute path to the working tree location"), git_common_path("worktrees/%s/gitdir", wt->id)); goto done; } if (flags & WT_VALIDATE_WORKTREE_MISSING_OK && !file_exists(wt->path)) { ret = 0; goto done; } if (!file_exists(wt_path.buf)) { strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf); goto done; } path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err)); if (!path) { strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"), wt_path.buf, err); goto done; } ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id))); if (ret) strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"), wt->path, git_common_path("worktrees/%s", wt->id)); done: free(path); strbuf_release(&wt_path); return ret; }