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; }
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; }
int other_head_refs(each_ref_fn fn, void *cb_data) { struct worktree **worktrees, **p; int ret = 0; worktrees = get_worktrees(0); for (p = worktrees; *p; p++) { struct worktree *wt = *p; struct object_id oid; int flag; if (wt->is_current) continue; if (!refs_read_ref_full(get_main_ref_store(the_repository), worktree_ref(wt, "HEAD"), RESOLVE_REF_READING, &oid, &flag)) ret = fn(worktree_ref(wt, "HEAD"), &oid, flag, cb_data); if (ret) break; } free_worktrees(worktrees); return ret; }
static int list(int ac, const char **av, const char *prefix) { int porcelain = 0; struct option options[] = { OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")), OPT_END() }; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac) usage_with_options(worktree_usage, options); else { struct worktree **worktrees = get_worktrees(GWT_SORT_LINKED); int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i; if (!porcelain) measure_widths(worktrees, &abbrev, &path_maxlen); for (i = 0; worktrees[i]; i++) { if (porcelain) show_worktree_porcelain(worktrees[i]); else show_worktree(worktrees[i], path_maxlen, abbrev); } free_worktrees(worktrees); } return 0; }
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; }
/* * note: this function should be able to detect shared symref even if * HEAD is temporarily detached (e.g. in the middle of rebase or * bisect). New commands that do similar things should update this * function as well. */ const struct worktree *find_shared_symref(const char *symref, const char *target) { const struct worktree *existing = NULL; struct strbuf path = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; static struct worktree **worktrees; int i = 0; if (worktrees) free_worktrees(worktrees); worktrees = get_worktrees(); for (i = 0; worktrees[i]; i++) { struct worktree *wt = worktrees[i]; if (wt->is_bare) continue; if (wt->is_detached && !strcmp(symref, "HEAD")) { if (is_worktree_being_rebased(wt, target)) { existing = wt; break; } if (is_worktree_being_bisected(wt, target)) { existing = wt; break; } } strbuf_reset(&path); strbuf_reset(&sb); strbuf_addf(&path, "%s/%s", get_worktree_git_dir(wt), symref); if (parse_ref(path.buf, &sb, NULL)) { continue; } if (!strcmp(sb.buf, target)) { existing = wt; break; } } strbuf_release(&path); strbuf_release(&sb); return existing; }
/* * note: this function should be able to detect shared symref even if * HEAD is temporarily detached (e.g. in the middle of rebase or * bisect). New commands that do similar things should update this * function as well. */ const struct worktree *find_shared_symref(const char *symref, const char *target) { const struct worktree *existing = NULL; static struct worktree **worktrees; int i = 0; if (worktrees) free_worktrees(worktrees); worktrees = get_worktrees(0); for (i = 0; worktrees[i]; i++) { struct worktree *wt = worktrees[i]; const char *symref_target; struct ref_store *refs; int flags; if (wt->is_bare) continue; if (wt->is_detached && !strcmp(symref, "HEAD")) { if (is_worktree_being_rebased(wt, target)) { existing = wt; break; } if (is_worktree_being_bisected(wt, target)) { existing = wt; break; } } refs = get_worktree_ref_store(wt); symref_target = refs_resolve_ref_unsafe(refs, symref, 0, NULL, &flags); if ((flags & REF_ISSYMREF) && symref_target && !strcmp(symref_target, target)) { existing = wt; break; } } return existing; }
static void reject_rebase_or_bisect_branch(const char *target) { struct worktree **worktrees = get_worktrees(); int i; for (i = 0; worktrees[i]; i++) { struct worktree *wt = worktrees[i]; if (!wt->is_detached) continue; if (is_worktree_being_rebased(wt, target)) die(_("Branch %s is being rebased at %s"), target, wt->path); if (is_worktree_being_bisected(wt, target)) die(_("Branch %s is being bisected at %s"), target, wt->path); } free_worktrees(worktrees); }
int replace_each_worktree_head_symref(const char *oldref, const char *newref) { int ret = 0; struct worktree **worktrees = get_worktrees(0); int i; for (i = 0; worktrees[i]; i++) { if (worktrees[i]->is_detached) continue; if (strcmp(oldref, worktrees[i]->head_ref)) continue; if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]), newref)) { ret = -1; error(_("HEAD of working tree %s is not updated"), worktrees[i]->path); } } free_worktrees(worktrees); return ret; }
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; }
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) { struct expire_reflog_policy_cb cb; timestamp_t now = time(NULL); int i, status, do_all, all_worktrees = 1; int explicit_expiry = 0; unsigned int flags = 0; default_reflog_expire_unreachable = now - 30 * 24 * 3600; default_reflog_expire = now - 90 * 24 * 3600; git_config(reflog_expire_config, NULL); save_commit_buffer = 0; do_all = status = 0; memset(&cb, 0, sizeof(cb)); cb.cmd.expire_total = default_reflog_expire; cb.cmd.expire_unreachable = default_reflog_expire_unreachable; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) flags |= EXPIRE_REFLOGS_DRY_RUN; else if (starts_with(arg, "--expire=")) { if (parse_expiry_date(arg + 9, &cb.cmd.expire_total)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_TOTAL; } else if (starts_with(arg, "--expire-unreachable=")) { if (parse_expiry_date(arg + 21, &cb.cmd.expire_unreachable)) die(_("'%s' is not a valid timestamp"), arg); explicit_expiry |= EXPIRE_UNREACH; } else if (!strcmp(arg, "--stale-fix")) cb.cmd.stalefix = 1; else if (!strcmp(arg, "--rewrite")) flags |= EXPIRE_REFLOGS_REWRITE; else if (!strcmp(arg, "--updateref")) flags |= EXPIRE_REFLOGS_UPDATE_REF; else if (!strcmp(arg, "--all")) do_all = 1; else if (!strcmp(arg, "--single-worktree")) all_worktrees = 0; else if (!strcmp(arg, "--verbose")) flags |= EXPIRE_REFLOGS_VERBOSE; else if (!strcmp(arg, "--")) { i++; break; } else if (arg[0] == '-') usage(_(reflog_expire_usage)); else break; } /* * We can trust the commits and objects reachable from refs * even in older repository. We cannot trust what's reachable * from reflog if the repository was pruned with older git. */ if (cb.cmd.stalefix) { repo_init_revisions(the_repository, &cb.cmd.revs, prefix); if (flags & EXPIRE_REFLOGS_VERBOSE) printf(_("Marking reachable objects...")); mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL); if (flags & EXPIRE_REFLOGS_VERBOSE) putchar('\n'); } if (do_all) { struct collect_reflog_cb collected; struct worktree **worktrees, **p; int i; memset(&collected, 0, sizeof(collected)); worktrees = get_worktrees(0); for (p = worktrees; *p; p++) { if (!all_worktrees && !(*p)->is_current) continue; collected.wt = *p; refs_for_each_reflog(get_worktree_ref_store(*p), collect_reflog, &collected); } free_worktrees(worktrees); for (i = 0; i < collected.nr; i++) { struct collected_reflog *e = collected.e[i]; set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); status |= reflog_expire(e->reflog, &e->oid, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, &cb); free(e); } free(collected.e); } for (; i < argc; i++) { char *ref; struct object_id oid; if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) { status |= error(_("%s points nowhere!"), argv[i]); continue; } set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref); status |= reflog_expire(ref, &oid, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, &cb); } return status; }