commit - 8505ab6630ab9715baa433a84979e90862e4c420
commit + 1589164d8ec910efcc3083905b0c2680c1957846
blob - 7a71eef0f01f4ba0d7203040a9f6ce7891dc108a
blob + 495bc5a1d44efc6d760792f0976a4cbf808e0b8a
--- Makefile.inc
+++ Makefile.inc
.else
CFLAGS += -Werror -Wall -Wstrict-prototypes -Wmissing-prototypes
CFLAGS += -Wwrite-strings -Wunused-variable
-CFLAGS += -Wno-unused-function
PREFIX ?= ${HOME}
BINDIR ?= ${PREFIX}/bin
LIBEXECDIR ?= ${BINDIR}
blob - 12ab5c9682db9094d25525bc55e414088b2c75a8
blob + 1f51749ddf6911fb3d86837fa46e6565d991b44c
--- cvg/got.c
+++ cvg/got.c
__dead static void usage(int, int);
__dead static void usage_import(void);
__dead static void usage_clone(void);
-__dead static void usage_fetch(void);
__dead static void usage_checkout(void);
__dead static void usage_update(void);
__dead static void usage_log(void);
__dead static void usage_tree(void);
__dead static void usage_status(void);
__dead static void usage_ref(void);
-__dead static void usage_branch(void);
__dead static void usage_tag(void);
__dead static void usage_add(void);
__dead static void usage_remove(void);
__dead static void usage_patch(void);
__dead static void usage_revert(void);
__dead static void usage_commit(void);
-__dead static void usage_send(void);
__dead static void usage_cherrypick(void);
__dead static void usage_backout(void);
-__dead static void usage_rebase(void);
-__dead static void usage_histedit(void);
-__dead static void usage_integrate(void);
-__dead static void usage_merge(void);
-__dead static void usage_stage(void);
-__dead static void usage_unstage(void);
__dead static void usage_cat(void);
__dead static void usage_info(void);
static const struct got_error* cmd_import(int, char *[]);
static const struct got_error* cmd_clone(int, char *[]);
-static const struct got_error* cmd_fetch(int, char *[]);
static const struct got_error* cmd_checkout(int, char *[]);
static const struct got_error* cmd_update(int, char *[]);
static const struct got_error* cmd_log(int, char *[]);
static const struct got_error* cmd_tree(int, char *[]);
static const struct got_error* cmd_status(int, char *[]);
static const struct got_error* cmd_ref(int, char *[]);
-static const struct got_error* cmd_branch(int, char *[]);
static const struct got_error* cmd_tag(int, char *[]);
static const struct got_error* cmd_add(int, char *[]);
static const struct got_error* cmd_remove(int, char *[]);
static const struct got_error* cmd_patch(int, char *[]);
static const struct got_error* cmd_revert(int, char *[]);
static const struct got_error* cmd_commit(int, char *[]);
-static const struct got_error* cmd_send(int, char *[]);
static const struct got_error* cmd_cherrypick(int, char *[]);
static const struct got_error* cmd_backout(int, char *[]);
-static const struct got_error* cmd_rebase(int, char *[]);
-static const struct got_error* cmd_histedit(int, char *[]);
-static const struct got_error* cmd_integrate(int, char *[]);
-static const struct got_error* cmd_merge(int, char *[]);
-static const struct got_error* cmd_stage(int, char *[]);
-static const struct got_error* cmd_unstage(int, char *[]);
static const struct got_error* cmd_cat(int, char *[]);
static const struct got_error* cmd_info(int, char *[]);
static const struct got_cmd got_commands[] = {
{ "import", cmd_import, usage_import, "im" },
{ "clone", cmd_clone, usage_clone, "cl" },
- /*{ "fetch", cmd_fetch, usage_fetch, "fe" },*/ /* rolled into update */
{ "checkout", cmd_checkout, usage_checkout, "co" },
{ "update", cmd_update, usage_update, "up" },
{ "log", cmd_log, usage_log, "" },
{ "tree", cmd_tree, usage_tree, "tr" },
{ "status", cmd_status, usage_status, "st" },
{ "ref", cmd_ref, usage_ref, "" },
- /*{ "branch", cmd_branch, usage_branch, "br" },*/
{ "tag", cmd_tag, usage_tag, "" },
{ "add", cmd_add, usage_add, "" },
{ "remove", cmd_remove, usage_remove, "rm" },
{ "patch", cmd_patch, usage_patch, "pa" },
{ "revert", cmd_revert, usage_revert, "rv" },
{ "commit", cmd_commit, usage_commit, "ci" },
- /*{ "send", cmd_send, usage_send, "se" },*/ /* part of commit */
{ "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
{ "backout", cmd_backout, usage_backout, "bo" },
- /*{ "rebase", cmd_rebase, usage_rebase, "rb" },*/
- /*{ "histedit", cmd_histedit, usage_histedit, "he" },*/
- /*{ "integrate", cmd_integrate, usage_integrate,"ig" },*/
- /*{ "merge", cmd_merge, usage_merge, "mg" },*/
- /*{ "stage", cmd_stage, usage_stage, "sg" },*/
- /*{ "unstage", cmd_unstage, usage_unstage, "ug" },*/
{ "cat", cmd_cat, usage_cat, "" },
{ "info", cmd_info, usage_info, "" },
};
}
static const struct got_error *
-update_symref(const char *refname, struct got_reference *target_ref,
- int verbosity, struct got_repository *repo)
-{
- const struct got_error *err = NULL, *unlock_err;
- struct got_reference *symref;
- int symref_is_locked = 0;
-
- err = got_ref_open(&symref, repo, refname, 1);
- if (err) {
- if (err->code != GOT_ERR_NOT_REF)
- return err;
- err = got_ref_alloc_symref(&symref, refname, target_ref);
- if (err)
- goto done;
-
- err = got_ref_write(symref, repo);
- if (err)
- goto done;
-
- if (verbosity >= 0)
- printf("Created reference %s: %s\n",
- got_ref_get_name(symref),
- got_ref_get_symref_target(symref));
- } else {
- symref_is_locked = 1;
-
- if (strcmp(got_ref_get_symref_target(symref),
- got_ref_get_name(target_ref)) == 0)
- goto done;
-
- err = got_ref_change_symref(symref,
- got_ref_get_name(target_ref));
- if (err)
- goto done;
-
- err = got_ref_write(symref, repo);
- if (err)
- goto done;
-
- if (verbosity >= 0)
- printf("Updated %s: %s\n", got_ref_get_name(symref),
- got_ref_get_symref_target(symref));
-
- }
-done:
- if (symref_is_locked) {
- unlock_err = got_ref_unlock(symref);
- if (unlock_err && err == NULL)
- err = unlock_err;
- }
- got_ref_close(symref);
- return err;
-}
-
-__dead static void
-usage_fetch(void)
-{
- fprintf(stderr, "usage: %s fetch [-adlqtvX] [-b branch] "
- "[-R reference] [-r repository-path] [remote-repository]\n",
- getprogname());
- exit(1);
-}
-
-static const struct got_error *
-delete_missing_ref(struct got_reference *ref,
- int verbosity, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_object_id *id = NULL;
- char *id_str = NULL;
-
- if (got_ref_is_symbolic(ref)) {
- err = got_ref_delete(ref, repo);
- if (err)
- return err;
- if (verbosity >= 0) {
- printf("Deleted %s: %s\n",
- got_ref_get_name(ref),
- got_ref_get_symref_target(ref));
- }
- } else {
- err = got_ref_resolve(&id, repo, ref);
- if (err)
- return err;
- err = got_object_id_str(&id_str, id);
- if (err)
- goto done;
-
- err = got_ref_delete(ref, repo);
- if (err)
- goto done;
- if (verbosity >= 0) {
- printf("Deleted %s: %s\n",
- got_ref_get_name(ref), id_str);
- }
- }
-done:
- free(id);
- free(id_str);
- return err;
-}
-
-static const struct got_error *
-delete_missing_refs(struct got_pathlist_head *their_refs,
- struct got_pathlist_head *their_symrefs,
- const struct got_remote_repo *remote,
- int verbosity, struct got_repository *repo)
-{
- const struct got_error *err = NULL, *unlock_err;
- struct got_reflist_head my_refs;
- struct got_reflist_entry *re;
- struct got_pathlist_entry *pe;
- char *remote_namespace = NULL;
- char *local_refname = NULL;
-
- TAILQ_INIT(&my_refs);
-
- if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
- == -1)
- return got_error_from_errno("asprintf");
-
- err = got_ref_list(&my_refs, repo, NULL, got_ref_cmp_by_name, NULL);
- if (err)
- goto done;
-
- TAILQ_FOREACH(re, &my_refs, entry) {
- const char *refname = got_ref_get_name(re->ref);
- const char *their_refname;
-
- if (remote->mirror_references) {
- their_refname = refname;
- } else {
- if (strncmp(refname, remote_namespace,
- strlen(remote_namespace)) == 0) {
- if (strcmp(refname + strlen(remote_namespace),
- GOT_REF_HEAD) == 0)
- continue;
- if (asprintf(&local_refname, "refs/heads/%s",
- refname + strlen(remote_namespace)) == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
- } else if (strncmp(refname, "refs/tags/", 10) != 0)
- continue;
-
- their_refname = local_refname;
- }
-
- TAILQ_FOREACH(pe, their_refs, entry) {
- if (strcmp(their_refname, pe->path) == 0)
- break;
- }
- if (pe != NULL)
- continue;
-
- TAILQ_FOREACH(pe, their_symrefs, entry) {
- if (strcmp(their_refname, pe->path) == 0)
- break;
- }
- if (pe != NULL)
- continue;
-
- err = delete_missing_ref(re->ref, verbosity, repo);
- if (err)
- break;
-
- if (local_refname) {
- struct got_reference *ref;
- err = got_ref_open(&ref, repo, local_refname, 1);
- if (err) {
- if (err->code != GOT_ERR_NOT_REF)
- break;
- free(local_refname);
- local_refname = NULL;
- continue;
- }
- err = delete_missing_ref(ref, verbosity, repo);
- if (err)
- break;
- unlock_err = got_ref_unlock(ref);
- got_ref_close(ref);
- if (unlock_err && err == NULL) {
- err = unlock_err;
- break;
- }
-
- free(local_refname);
- local_refname = NULL;
- }
- }
-done:
- got_ref_list_free(&my_refs);
- free(remote_namespace);
- free(local_refname);
- return err;
-}
-
-static const struct got_error *
update_wanted_ref(const char *refname, struct got_object_id *id,
const char *remote_repo_name, int verbosity, struct got_repository *repo)
{
return err;
}
-static const struct got_error *
-delete_refs_for_remote(struct got_repository *repo, const char *remote_name)
-{
- const struct got_error *err = NULL;
- struct got_reflist_head refs;
- struct got_reflist_entry *re;
- char *prefix;
-
- TAILQ_INIT(&refs);
-
- if (asprintf(&prefix, "refs/remotes/%s", remote_name) == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
- err = got_ref_list(&refs, repo, prefix, got_ref_cmp_by_name, NULL);
- if (err)
- goto done;
-
- TAILQ_FOREACH(re, &refs, entry)
- delete_ref(repo, re->ref);
-done:
- got_ref_list_free(&refs);
- return err;
-}
-
-
-
__dead static void
usage_checkout(void)
{
}
static const struct got_error *
-switch_head_ref(struct got_reference *head_ref,
- struct got_object_id *commit_id, struct got_worktree *worktree,
- struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- char *base_id_str;
- int ref_has_moved = 0;
-
- /* Trivial case: switching between two different references. */
- if (strcmp(got_ref_get_name(head_ref),
- got_worktree_get_head_ref_name(worktree)) != 0) {
- printf("Switching work tree from %s to %s\n",
- got_worktree_get_head_ref_name(worktree),
- got_ref_get_name(head_ref));
- return got_worktree_set_head_ref(worktree, head_ref);
- }
-
- err = check_linear_ancestry(commit_id,
- got_worktree_get_base_commit_id(worktree), 0, repo);
- if (err) {
- if (err->code != GOT_ERR_ANCESTRY)
- return err;
- ref_has_moved = 1;
- }
- if (!ref_has_moved)
- return NULL;
-
- /* Switching to a rebased branch with the same reference name. */
- err = got_object_id_str(&base_id_str,
- got_worktree_get_base_commit_id(worktree));
- if (err)
- return err;
- printf("Reference %s now points at a different branch\n",
- got_worktree_get_head_ref_name(worktree));
- printf("Switching work tree from %s to %s\n", base_id_str,
- got_worktree_get_head_ref_name(worktree));
- return NULL;
-}
-
-static const struct got_error *
check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
{
const struct got_error *err;
}
__dead static void
-usage_branch(void)
-{
- fprintf(stderr, "usage: %s branch [-lnt] [-c commit] [-d name] "
- "[-r repository-path] [name]\n", getprogname());
- exit(1);
-}
-
-static const struct got_error *
-list_branch(struct got_repository *repo, struct got_worktree *worktree,
- struct got_reference *ref)
-{
- const struct got_error *err = NULL;
- const char *refname, *marker = " ";
- char *refstr;
-
- refname = got_ref_get_name(ref);
- if (worktree && strcmp(refname,
- got_worktree_get_head_ref_name(worktree)) == 0) {
- struct got_object_id *id = NULL;
-
- err = got_ref_resolve(&id, repo, ref);
- if (err)
- return err;
- if (got_object_id_cmp(id,
- got_worktree_get_base_commit_id(worktree)) == 0)
- marker = "* ";
- else
- marker = "~ ";
- free(id);
- }
-
- if (strncmp(refname, "refs/heads/", 11) == 0)
- refname += 11;
- if (strncmp(refname, "refs/got/worktree/", 18) == 0)
- refname += 18;
- if (strncmp(refname, "refs/remotes/", 13) == 0)
- refname += 13;
-
- refstr = got_ref_to_str(ref);
- if (refstr == NULL)
- return got_error_from_errno("got_ref_to_str");
-
- printf("%s%s: %s\n", marker, refname, refstr);
- free(refstr);
- return NULL;
-}
-
-static const struct got_error *
-show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
-{
- const char *refname;
-
- if (worktree == NULL)
- return got_error(GOT_ERR_NOT_WORKTREE);
-
- refname = got_worktree_get_head_ref_name(worktree);
-
- if (strncmp(refname, "refs/heads/", 11) == 0)
- refname += 11;
- if (strncmp(refname, "refs/got/worktree/", 18) == 0)
- refname += 18;
-
- printf("%s\n", refname);
-
- return NULL;
-}
-
-static const struct got_error *
-list_branches(struct got_repository *repo, struct got_worktree *worktree,
- int sort_by_time)
-{
- static const struct got_error *err = NULL;
- struct got_reflist_head refs;
- struct got_reflist_entry *re;
- struct got_reference *temp_ref = NULL;
- int rebase_in_progress, histedit_in_progress;
-
- TAILQ_INIT(&refs);
-
- if (worktree) {
- err = got_worktree_rebase_in_progress(&rebase_in_progress,
- worktree);
- if (err)
- return err;
-
- err = got_worktree_histedit_in_progress(&histedit_in_progress,
- worktree);
- if (err)
- return err;
-
- if (rebase_in_progress || histedit_in_progress) {
- err = got_ref_open(&temp_ref, repo,
- got_worktree_get_head_ref_name(worktree), 0);
- if (err)
- return err;
- list_branch(repo, worktree, temp_ref);
- got_ref_close(temp_ref);
- }
- }
-
- err = got_ref_list(&refs, repo, "refs/heads", sort_by_time ?
- got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
- repo);
- if (err)
- return err;
-
- TAILQ_FOREACH(re, &refs, entry)
- list_branch(repo, worktree, re->ref);
-
- got_ref_list_free(&refs);
-
- err = got_ref_list(&refs, repo, "refs/remotes", sort_by_time ?
- got_ref_cmp_by_commit_timestamp_descending : got_ref_cmp_by_name,
- repo);
- if (err)
- return err;
-
- TAILQ_FOREACH(re, &refs, entry)
- list_branch(repo, worktree, re->ref);
-
- got_ref_list_free(&refs);
-
- return NULL;
-}
-
-static const struct got_error *
-delete_branch(struct got_repository *repo, struct got_worktree *worktree,
- const char *branch_name)
-{
- const struct got_error *err = NULL;
- struct got_reference *ref = NULL;
- char *refname, *remote_refname = NULL;
-
- if (strncmp(branch_name, "refs/", 5) == 0)
- branch_name += 5;
- if (strncmp(branch_name, "heads/", 6) == 0)
- branch_name += 6;
- else if (strncmp(branch_name, "remotes/", 8) == 0)
- branch_name += 8;
-
- if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
- return got_error_from_errno("asprintf");
-
- if (asprintf(&remote_refname, "refs/remotes/%s",
- branch_name) == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
-
- err = got_ref_open(&ref, repo, refname, 0);
- if (err) {
- const struct got_error *err2;
- if (err->code != GOT_ERR_NOT_REF)
- goto done;
- /*
- * Keep 'err' intact such that if neither branch exists
- * we report "refs/heads" rather than "refs/remotes" in
- * our error message.
- */
- err2 = got_ref_open(&ref, repo, remote_refname, 0);
- if (err2)
- goto done;
- err = NULL;
- }
-
- if (worktree &&
- strcmp(got_worktree_get_head_ref_name(worktree),
- got_ref_get_name(ref)) == 0) {
- err = got_error_msg(GOT_ERR_SAME_BRANCH,
- "will not delete this work tree's current branch");
- goto done;
- }
-
- err = delete_ref(repo, ref);
-done:
- if (ref)
- got_ref_close(ref);
- free(refname);
- free(remote_refname);
- return err;
-}
-
-static const struct got_error *
-add_branch(struct got_repository *repo, const char *branch_name,
- struct got_object_id *base_commit_id)
-{
- const struct got_error *err = NULL;
- struct got_reference *ref = NULL;
- char *refname = NULL;
-
- /*
- * Don't let the user create a branch name with a leading '-'.
- * While technically a valid reference name, this case is usually
- * an unintended typo.
- */
- if (branch_name[0] == '-')
- return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
-
- if (strncmp(branch_name, "refs/heads/", 11) == 0)
- branch_name += 11;
-
- if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
-
- err = got_ref_open(&ref, repo, refname, 0);
- if (err == NULL) {
- err = got_error(GOT_ERR_BRANCH_EXISTS);
- goto done;
- } else if (err->code != GOT_ERR_NOT_REF)
- goto done;
-
- err = got_ref_alloc(&ref, refname, base_commit_id);
- if (err)
- goto done;
-
- err = got_ref_write(ref, repo);
-done:
- if (ref)
- got_ref_close(ref);
- free(refname);
- return err;
-}
-
-static const struct got_error *
-cmd_branch(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_repository *repo = NULL;
- struct got_worktree *worktree = NULL;
- char *cwd = NULL, *repo_path = NULL;
- int ch, do_list = 0, do_show = 0, do_update = 1, sort_by_time = 0;
- const char *delref = NULL, *commit_id_arg = NULL;
- struct got_reference *ref = NULL;
- struct got_pathlist_head paths;
- struct got_object_id *commit_id = NULL;
- char *commit_id_str = NULL;
- int *pack_fds = NULL;
-
- TAILQ_INIT(&paths);
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec "
- "sendfd unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "c:d:lnr:t")) != -1) {
- switch (ch) {
- case 'c':
- commit_id_arg = optarg;
- break;
- case 'd':
- delref = optarg;
- break;
- case 'l':
- do_list = 1;
- break;
- case 'n':
- do_update = 0;
- break;
- case 'r':
- repo_path = realpath(optarg, NULL);
- if (repo_path == NULL)
- return got_error_from_errno2("realpath",
- optarg);
- got_path_strip_trailing_slashes(repo_path);
- break;
- case 't':
- sort_by_time = 1;
- break;
- default:
- usage_branch();
- /* NOTREACHED */
- }
- }
-
- if (do_list && delref)
- option_conflict('l', 'd');
- if (sort_by_time && !do_list)
- errx(1, "-t option requires -l option");
-
- argc -= optind;
- argv += optind;
-
- if (!do_list && !delref && argc == 0)
- do_show = 1;
-
- if ((do_list || delref || do_show) && commit_id_arg != NULL)
- errx(1, "-c option can only be used when creating a branch");
-
- if (do_list || delref) {
- if (argc > 0)
- usage_branch();
- } else if (!do_show && argc != 1)
- usage_branch();
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- if (repo_path == NULL) {
- error = got_worktree_open(&worktree, cwd);
- if (error && error->code != GOT_ERR_NOT_WORKTREE)
- goto done;
- else
- error = NULL;
- if (worktree) {
- repo_path =
- strdup(got_worktree_get_repo_path(worktree));
- if (repo_path == NULL)
- error = got_error_from_errno("strdup");
- if (error)
- goto done;
- } else {
- repo_path = strdup(cwd);
- if (repo_path == NULL) {
- error = got_error_from_errno("strdup");
- goto done;
- }
- }
- }
-
- error = got_repo_open(&repo, repo_path, NULL, pack_fds);
- if (error != NULL)
- goto done;
-
-#ifndef PROFILE
- if (do_list || do_show) {
- /* Remove "cpath" promise. */
- if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
- NULL) == -1)
- err(1, "pledge");
- }
-#endif
-
- error = apply_unveil(got_repo_get_path(repo), do_list,
- worktree ? got_worktree_get_root_path(worktree) : NULL);
- if (error)
- goto done;
-
- if (do_show)
- error = show_current_branch(repo, worktree);
- else if (do_list)
- error = list_branches(repo, worktree, sort_by_time);
- else if (delref)
- error = delete_branch(repo, worktree, delref);
- else {
- struct got_reflist_head refs;
- TAILQ_INIT(&refs);
- error = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name,
- NULL);
- if (error)
- goto done;
- if (commit_id_arg == NULL)
- commit_id_arg = worktree ?
- got_worktree_get_head_ref_name(worktree) :
- GOT_REF_HEAD;
- error = got_repo_match_object_id(&commit_id, NULL,
- commit_id_arg, GOT_OBJ_TYPE_COMMIT, &refs, repo);
- got_ref_list_free(&refs);
- if (error)
- goto done;
- error = add_branch(repo, argv[0], commit_id);
- if (error)
- goto done;
- if (worktree && do_update) {
- struct got_update_progress_arg upa;
- char *branch_refname = NULL;
-
- error = got_object_id_str(&commit_id_str, commit_id);
- if (error)
- goto done;
- error = get_worktree_paths_from_argv(&paths, 0, NULL,
- worktree);
- if (error)
- goto done;
- if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
- == -1) {
- error = got_error_from_errno("asprintf");
- goto done;
- }
- error = got_ref_open(&ref, repo, branch_refname, 0);
- free(branch_refname);
- if (error)
- goto done;
- error = switch_head_ref(ref, commit_id, worktree,
- repo);
- if (error)
- goto done;
- error = got_worktree_set_base_commit_id(worktree, repo,
- commit_id);
- if (error)
- goto done;
- memset(&upa, 0, sizeof(upa));
- error = got_worktree_checkout_files(worktree, &paths,
- repo, update_progress, &upa, check_cancelled,
- NULL);
- if (error)
- goto done;
- if (upa.did_something) {
- printf("Updated to %s: %s\n",
- got_worktree_get_head_ref_name(worktree),
- commit_id_str);
- }
- print_update_progress_stats(&upa);
- }
- }
-done:
- if (ref)
- got_ref_close(ref);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (worktree)
- got_worktree_close(worktree);
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- free(cwd);
- free(repo_path);
- free(commit_id);
- free(commit_id_str);
- got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
- return error;
-}
-
-
-__dead static void
usage_tag(void)
{
fprintf(stderr, "usage: %s tag [-lVv] [-c commit] [-m message] "
return error;
}
-__dead static void
-usage_send(void)
+/*
+ * Print and if delete is set delete all ref_prefix references.
+ * If wanted_ref is not NULL, only print or delete this reference.
+ */
+static const struct got_error *
+process_logmsg_refs(const char *ref_prefix, size_t prefix_len,
+ const char *wanted_ref, int delete, struct got_worktree *worktree,
+ struct got_repository *repo)
{
- fprintf(stderr, "usage: %s send [-afqTv] [-b branch] [-d branch] "
- "[-r repository-path] [-t tag] [remote-repository]\n",
- getprogname());
- exit(1);
-}
+ const struct got_error *err;
+ struct got_pathlist_head paths;
+ struct got_reflist_head refs;
+ struct got_reflist_entry *re;
+ struct got_reflist_object_id_map *refs_idmap = NULL;
+ struct got_commit_object *commit = NULL;
+ struct got_object_id *id = NULL;
+ const char *header_prefix;
+ char *uuidstr = NULL;
+ int found = 0;
-static void
-print_load_info(int print_colored, int print_found, int print_trees,
- int ncolored, int nfound, int ntrees)
-{
- if (print_colored) {
- printf("%d commit%s colored", ncolored,
- ncolored == 1 ? "" : "s");
- }
- if (print_found) {
- printf("%s%d object%s found",
- ncolored > 0 ? "; " : "",
- nfound, nfound == 1 ? "" : "s");
- }
- if (print_trees) {
- printf("; %d tree%s scanned", ntrees,
- ntrees == 1 ? "" : "s");
- }
-}
+ TAILQ_INIT(&refs);
+ TAILQ_INIT(&paths);
-struct got_send_progress_arg {
- char last_scaled_packsize[FMT_SCALED_STRSIZE];
- int verbosity;
- int last_ncolored;
- int last_nfound;
- int last_ntrees;
- int loading_done;
- int last_ncommits;
- int last_nobj_total;
- int last_p_deltify;
- int last_p_written;
- int last_p_sent;
- int printed_something;
- int sent_something;
- struct got_pathlist_head *delete_branches;
-};
+ err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, repo);
+ if (err)
+ goto done;
-static const struct got_error *
-send_progress(void *arg, int ncolored, int nfound, int ntrees,
- off_t packfile_size, int ncommits, int nobj_total, int nobj_deltify,
- int nobj_written, off_t bytes_sent, const char *refname,
- const char *errmsg, int success)
-{
- struct got_send_progress_arg *a = arg;
- char scaled_packsize[FMT_SCALED_STRSIZE];
- char scaled_sent[FMT_SCALED_STRSIZE];
- int p_deltify = 0, p_written = 0, p_sent = 0;
- int print_colored = 0, print_found = 0, print_trees = 0;
- int print_searching = 0, print_total = 0;
- int print_deltify = 0, print_written = 0, print_sent = 0;
+ err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
+ if (err)
+ goto done;
- if (a->verbosity < 0)
- return NULL;
+ if (worktree != NULL) {
+ err = got_worktree_get_uuid(&uuidstr, worktree);
+ if (err)
+ goto done;
+ }
- if (refname) {
- const char *status = success ? "accepted" : "rejected";
+ if (wanted_ref) {
+ if (strncmp(wanted_ref, "refs/heads/", 11) == 0)
+ wanted_ref += 11;
+ }
- if (success) {
- struct got_pathlist_entry *pe;
- TAILQ_FOREACH(pe, a->delete_branches, entry) {
- const char *branchname = pe->path;
- if (got_path_cmp(branchname, refname,
- strlen(branchname), strlen(refname)) == 0) {
- status = "deleted";
- a->sent_something = 1;
- break;
- }
- }
- }
+ if (strcmp(ref_prefix, GOT_WORKTREE_BACKOUT_REF_PREFIX) == 0)
+ header_prefix = "backout";
+ else
+ header_prefix = "cherrypick";
- if (a->printed_something)
- putchar('\n');
- printf("Server has %s %s", status, refname);
- if (errmsg)
- printf(": %s", errmsg);
- a->printed_something = 1;
- return NULL;
- }
+ TAILQ_FOREACH(re, &refs, entry) {
+ const char *refname, *wt;
- if (a->last_ncolored != ncolored) {
- print_colored = 1;
- a->last_ncolored = ncolored;
- }
+ refname = got_ref_get_name(re->ref);
- if (a->last_nfound != nfound) {
- print_colored = 1;
- print_found = 1;
- a->last_nfound = nfound;
- }
+ err = check_cancelled(NULL);
+ if (err)
+ goto done;
- if (a->last_ntrees != ntrees) {
- print_colored = 1;
- print_found = 1;
- print_trees = 1;
- a->last_ntrees = ntrees;
- }
+ if (strncmp(refname, ref_prefix, prefix_len) == 0)
+ refname += prefix_len + 1; /* skip '-' delimiter */
+ else
+ continue;
- if ((print_colored || print_found || print_trees) &&
- !a->loading_done) {
- printf("\r");
- print_load_info(print_colored, print_found, print_trees,
- ncolored, nfound, ntrees);
- a->printed_something = 1;
- fflush(stdout);
- return NULL;
- } else if (!a->loading_done) {
- printf("\r");
- print_load_info(1, 1, 1, ncolored, nfound, ntrees);
- printf("\n");
- a->loading_done = 1;
- }
+ wt = refname;
- if (fmt_scaled(packfile_size, scaled_packsize) == -1)
- return got_error_from_errno("fmt_scaled");
- if (fmt_scaled(bytes_sent, scaled_sent) == -1)
- return got_error_from_errno("fmt_scaled");
+ if (worktree == NULL || strncmp(refname, uuidstr,
+ GOT_WORKTREE_UUID_STRLEN) == 0)
+ refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
+ else
+ continue;
- if (a->last_ncommits != ncommits) {
- print_searching = 1;
- a->last_ncommits = ncommits;
- }
+ err = got_repo_match_object_id(&id, NULL, refname,
+ GOT_OBJ_TYPE_COMMIT, NULL, repo);
+ if (err)
+ goto done;
- if (a->last_nobj_total != nobj_total) {
- print_searching = 1;
- print_total = 1;
- a->last_nobj_total = nobj_total;
- }
+ err = got_object_open_as_commit(&commit, repo, id);
+ if (err)
+ goto done;
- if (packfile_size > 0 && (a->last_scaled_packsize[0] == '\0' ||
- strcmp(scaled_packsize, a->last_scaled_packsize)) != 0) {
- if (strlcpy(a->last_scaled_packsize, scaled_packsize,
- FMT_SCALED_STRSIZE) >= FMT_SCALED_STRSIZE)
- return got_error(GOT_ERR_NO_SPACE);
- }
+ if (wanted_ref)
+ found = strncmp(wanted_ref, refname,
+ strlen(wanted_ref)) == 0;
+ if (wanted_ref && !found) {
+ struct got_reflist_head *ci_refs;
- if (nobj_deltify > 0 || nobj_written > 0) {
- if (nobj_deltify > 0) {
- p_deltify = (nobj_deltify * 100) / nobj_total;
- if (p_deltify != a->last_p_deltify) {
- a->last_p_deltify = p_deltify;
- print_searching = 1;
- print_total = 1;
- print_deltify = 1;
+ ci_refs = got_reflist_object_id_map_lookup(refs_idmap,
+ id);
+
+ if (ci_refs) {
+ char *refs_str = NULL;
+ char const *r = NULL;
+
+ err = build_refs_str(&refs_str, ci_refs, id,
+ repo, 1);
+ if (err)
+ goto done;
+
+ r = refs_str;
+ while (r) {
+ if (strncmp(r, wanted_ref,
+ strlen(wanted_ref)) == 0) {
+ found = 1;
+ break;
+ }
+ r = strchr(r, ' ');
+ if (r)
+ ++r;
+ }
+ free(refs_str);
}
}
- if (nobj_written > 0) {
- p_written = (nobj_written * 100) / nobj_total;
- if (p_written != a->last_p_written) {
- a->last_p_written = p_written;
- print_searching = 1;
- print_total = 1;
- print_deltify = 1;
- print_written = 1;
+
+ if (wanted_ref == NULL || found) {
+ if (delete) {
+ err = got_ref_delete(re->ref, repo);
+ if (err)
+ goto done;
+ printf("Deleted: ");
+ err = print_commit_oneline(commit, id, repo,
+ refs_idmap);
+ } else {
+ /*
+ * Print paths modified by commit to help
+ * associate commits with worktree changes.
+ */
+ err = get_changed_paths(&paths, commit,
+ repo, NULL);
+ if (err)
+ goto done;
+
+ err = print_commit(commit, id, repo, NULL,
+ &paths, NULL, 0, 0, refs_idmap, NULL,
+ header_prefix);
+ got_pathlist_free(&paths,
+ GOT_PATHLIST_FREE_ALL);
+
+ if (worktree == NULL)
+ printf("work tree: %.*s\n\n",
+ GOT_WORKTREE_UUID_STRLEN, wt);
}
+ if (err || found)
+ goto done;
}
- }
- if (bytes_sent > 0) {
- p_sent = (bytes_sent * 100) / packfile_size;
- if (p_sent != a->last_p_sent) {
- a->last_p_sent = p_sent;
- print_searching = 1;
- print_total = 1;
- print_deltify = 1;
- print_written = 1;
- print_sent = 1;
- }
- a->sent_something = 1;
+ got_object_commit_close(commit);
+ commit = NULL;
+ free(id);
+ id = NULL;
}
- if (print_searching || print_total || print_deltify || print_written ||
- print_sent)
- printf("\r");
- if (print_searching)
- printf("packing %d reference%s", ncommits,
- ncommits == 1 ? "" : "s");
- if (print_total)
- printf("; %d object%s", nobj_total,
- nobj_total == 1 ? "" : "s");
- if (print_deltify)
- printf("; deltify: %d%%", p_deltify);
- if (print_sent)
- printf("; uploading pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
- scaled_packsize, p_sent);
- else if (print_written)
- printf("; writing pack: %*s %d%%", FMT_SCALED_STRSIZE - 2,
- scaled_packsize, p_written);
- if (print_searching || print_total || print_deltify ||
- print_written || print_sent) {
- a->printed_something = 1;
- fflush(stdout);
- }
- return NULL;
-}
-
-static const struct got_error *
-cmd_send(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- char *cwd = NULL, *repo_path = NULL;
- const char *remote_name;
- char *proto = NULL, *host = NULL, *port = NULL;
- char *repo_name = NULL, *server_path = NULL;
- const struct got_remote_repo *remotes, *remote = NULL;
- int nremotes, nbranches = 0, ndelete_branches = 0;
- struct got_repository *repo = NULL;
- struct got_worktree *worktree = NULL;
- const struct got_gotconfig *repo_conf = NULL, *worktree_conf = NULL;
- struct got_pathlist_head branches;
- struct got_pathlist_head tags;
- struct got_reflist_head all_branches;
- struct got_reflist_head all_tags;
- struct got_pathlist_head delete_args;
- struct got_pathlist_head delete_branches;
- struct got_reflist_entry *re;
- struct got_pathlist_entry *pe;
- int i, ch, sendfd = -1, sendstatus;
- pid_t sendpid = -1;
- struct got_send_progress_arg spa;
- int verbosity = 0, overwrite_refs = 0;
- int send_all_branches = 0, send_all_tags = 0;
- struct got_reference *ref = NULL;
- int *pack_fds = NULL;
-
- TAILQ_INIT(&branches);
- TAILQ_INIT(&tags);
- TAILQ_INIT(&all_branches);
- TAILQ_INIT(&all_tags);
- TAILQ_INIT(&delete_args);
- TAILQ_INIT(&delete_branches);
-
- while ((ch = getopt(argc, argv, "ab:d:fqr:Tt:v")) != -1) {
- switch (ch) {
- case 'a':
- send_all_branches = 1;
- break;
- case 'b':
- error = got_pathlist_append(&branches, optarg, NULL);
- if (error)
- return error;
- nbranches++;
- break;
- case 'd':
- error = got_pathlist_append(&delete_args, optarg, NULL);
- if (error)
- return error;
- break;
- case 'f':
- overwrite_refs = 1;
- break;
- case 'q':
- verbosity = -1;
- break;
- case 'r':
- repo_path = realpath(optarg, NULL);
- if (repo_path == NULL)
- return got_error_from_errno2("realpath",
- optarg);
- got_path_strip_trailing_slashes(repo_path);
- break;
- case 'T':
- send_all_tags = 1;
- break;
- case 't':
- error = got_pathlist_append(&tags, optarg, NULL);
- if (error)
- return error;
- break;
- case 'v':
- if (verbosity < 0)
- verbosity = 0;
- else if (verbosity < 3)
- verbosity++;
- break;
- default:
- usage_send();
- /* NOTREACHED */
- }
- }
- argc -= optind;
- argv += optind;
-
- if (send_all_branches && !TAILQ_EMPTY(&branches))
- option_conflict('a', 'b');
- if (send_all_tags && !TAILQ_EMPTY(&tags))
- option_conflict('T', 't');
-
-
- if (argc == 0)
- remote_name = GOT_SEND_DEFAULT_REMOTE_NAME;
- else if (argc == 1)
- remote_name = argv[0];
- else
- usage_send();
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- if (repo_path == NULL) {
- error = got_worktree_open(&worktree, cwd);
- if (error && error->code != GOT_ERR_NOT_WORKTREE)
- goto done;
- else
- error = NULL;
- if (worktree) {
- repo_path =
- strdup(got_worktree_get_repo_path(worktree));
- if (repo_path == NULL)
- error = got_error_from_errno("strdup");
- if (error)
- goto done;
- } else {
- repo_path = strdup(cwd);
- if (repo_path == NULL) {
- error = got_error_from_errno("strdup");
- goto done;
- }
- }
- }
-
- error = got_repo_open(&repo, repo_path, NULL, pack_fds);
- if (error)
- goto done;
-
- if (worktree) {
- worktree_conf = got_worktree_get_gotconfig(worktree);
- if (worktree_conf) {
- got_gotconfig_get_remotes(&nremotes, &remotes,
- worktree_conf);
- for (i = 0; i < nremotes; i++) {
- if (strcmp(remotes[i].name, remote_name) == 0) {
- remote = &remotes[i];
- break;
- }
- }
- }
- }
- if (remote == NULL) {
- repo_conf = got_repo_get_gotconfig(repo);
- if (repo_conf) {
- got_gotconfig_get_remotes(&nremotes, &remotes,
- repo_conf);
- for (i = 0; i < nremotes; i++) {
- if (strcmp(remotes[i].name, remote_name) == 0) {
- remote = &remotes[i];
- break;
- }
- }
- }
- }
- if (remote == NULL) {
- got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
- for (i = 0; i < nremotes; i++) {
- if (strcmp(remotes[i].name, remote_name) == 0) {
- remote = &remotes[i];
- break;
- }
- }
- }
- if (remote == NULL) {
- error = got_error_path(remote_name, GOT_ERR_NO_REMOTE);
- goto done;
- }
-
- error = got_dial_parse_uri(&proto, &host, &port, &server_path,
- &repo_name, remote->send_url);
- if (error)
- goto done;
-
- if (strcmp(proto, "git") == 0) {
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec "
- "sendfd dns inet unveil", NULL) == -1)
- err(1, "pledge");
-#endif
- } else if (strcmp(proto, "git+ssh") == 0 ||
- strcmp(proto, "ssh") == 0) {
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec "
- "sendfd unveil", NULL) == -1)
- err(1, "pledge");
-#endif
- } else if (strcmp(proto, "http") == 0 ||
- strcmp(proto, "git+http") == 0) {
- error = got_error_path(proto, GOT_ERR_NOT_IMPL);
- goto done;
- } else {
- error = got_error_path(proto, GOT_ERR_BAD_PROTO);
- goto done;
- }
-
- error = got_dial_apply_unveil(proto);
- if (error)
- goto done;
-
- error = apply_unveil(got_repo_get_path(repo), 0, NULL);
- if (error)
- goto done;
-
- if (send_all_branches) {
- error = got_ref_list(&all_branches, repo, "refs/heads",
- got_ref_cmp_by_name, NULL);
- if (error)
- goto done;
- TAILQ_FOREACH(re, &all_branches, entry) {
- const char *branchname = got_ref_get_name(re->ref);
- error = got_pathlist_append(&branches,
- branchname, NULL);
- if (error)
- goto done;
- nbranches++;
- }
- } else if (nbranches == 0) {
- for (i = 0; i < remote->nsend_branches; i++) {
- error = got_pathlist_append(&branches,
- remote->send_branches[i], NULL);
- if (error)
- goto done;
- }
- }
-
- if (send_all_tags) {
- error = got_ref_list(&all_tags, repo, "refs/tags",
- got_ref_cmp_by_name, NULL);
- if (error)
- goto done;
- TAILQ_FOREACH(re, &all_tags, entry) {
- const char *tagname = got_ref_get_name(re->ref);
- error = got_pathlist_append(&tags,
- tagname, NULL);
- if (error)
- goto done;
- }
- }
-
- /*
- * To prevent accidents only branches in refs/heads/ can be deleted
- * with 'got send -d'.
- * Deleting anything else requires local repository access or Git.
- */
- TAILQ_FOREACH(pe, &delete_args, entry) {
- const char *branchname = pe->path;
- char *s;
- struct got_pathlist_entry *new;
- if (strncmp(branchname, "refs/heads/", 11) == 0) {
- s = strdup(branchname);
- if (s == NULL) {
- error = got_error_from_errno("strdup");
- goto done;
- }
- } else {
- if (asprintf(&s, "refs/heads/%s", branchname) == -1) {
- error = got_error_from_errno("asprintf");
- goto done;
- }
- }
- error = got_pathlist_insert(&new, &delete_branches, s, NULL);
- if (error || new == NULL /* duplicate */)
- free(s);
- if (error)
- goto done;
- ndelete_branches++;
- }
-
- if (nbranches == 0 && ndelete_branches == 0) {
- struct got_reference *head_ref;
- if (worktree)
- error = got_ref_open(&head_ref, repo,
- got_worktree_get_head_ref_name(worktree), 0);
- else
- error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
- if (error)
- goto done;
- if (got_ref_is_symbolic(head_ref)) {
- error = got_ref_resolve_symbolic(&ref, repo, head_ref);
- got_ref_close(head_ref);
- if (error)
- goto done;
- } else
- ref = head_ref;
- error = got_pathlist_append(&branches, got_ref_get_name(ref),
- NULL);
- if (error)
- goto done;
- nbranches++;
- }
-
- if (verbosity >= 0) {
- printf("Connecting to \"%s\" %s://%s%s%s%s%s\n",
- remote->name, proto, host,
- port ? ":" : "", port ? port : "",
- *server_path == '/' ? "" : "/", server_path);
- }
-
- error = got_send_connect(&sendpid, &sendfd, proto, host, port,
- server_path, verbosity);
- if (error)
- goto done;
-
- memset(&spa, 0, sizeof(spa));
- spa.last_scaled_packsize[0] = '\0';
- spa.last_p_deltify = -1;
- spa.last_p_written = -1;
- spa.verbosity = verbosity;
- spa.delete_branches = &delete_branches;
- error = got_send_pack(remote_name, &branches, &tags, &delete_branches,
- verbosity, overwrite_refs, sendfd, repo, send_progress, &spa,
- check_cancelled, NULL);
- if (spa.printed_something)
- putchar('\n');
- if (error)
- goto done;
- if (!spa.sent_something && verbosity >= 0)
- printf("Already up-to-date\n");
-done:
- if (sendpid > 0) {
- if (kill(sendpid, SIGTERM) == -1)
- error = got_error_from_errno("kill");
- if (waitpid(sendpid, &sendstatus, 0) == -1 && error == NULL)
- error = got_error_from_errno("waitpid");
- }
- if (sendfd != -1 && close(sendfd) == -1 && error == NULL)
- error = got_error_from_errno("close");
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (worktree)
- got_worktree_close(worktree);
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- if (ref)
- got_ref_close(ref);
- got_pathlist_free(&branches, GOT_PATHLIST_FREE_NONE);
- got_pathlist_free(&tags, GOT_PATHLIST_FREE_NONE);
- got_ref_list_free(&all_branches);
- got_ref_list_free(&all_tags);
- got_pathlist_free(&delete_args, GOT_PATHLIST_FREE_NONE);
- got_pathlist_free(&delete_branches, GOT_PATHLIST_FREE_PATH);
- free(cwd);
- free(repo_path);
- free(proto);
- free(host);
- free(port);
- free(server_path);
- free(repo_name);
- return error;
-}
-
-/*
- * Print and if delete is set delete all ref_prefix references.
- * If wanted_ref is not NULL, only print or delete this reference.
- */
-static const struct got_error *
-process_logmsg_refs(const char *ref_prefix, size_t prefix_len,
- const char *wanted_ref, int delete, struct got_worktree *worktree,
- struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_pathlist_head paths;
- struct got_reflist_head refs;
- struct got_reflist_entry *re;
- struct got_reflist_object_id_map *refs_idmap = NULL;
- struct got_commit_object *commit = NULL;
- struct got_object_id *id = NULL;
- const char *header_prefix;
- char *uuidstr = NULL;
- int found = 0;
-
- TAILQ_INIT(&refs);
- TAILQ_INIT(&paths);
-
- err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, repo);
- if (err)
- goto done;
-
- err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
- if (err)
- goto done;
-
- if (worktree != NULL) {
- err = got_worktree_get_uuid(&uuidstr, worktree);
- if (err)
- goto done;
- }
-
- if (wanted_ref) {
- if (strncmp(wanted_ref, "refs/heads/", 11) == 0)
- wanted_ref += 11;
- }
-
- if (strcmp(ref_prefix, GOT_WORKTREE_BACKOUT_REF_PREFIX) == 0)
- header_prefix = "backout";
- else
- header_prefix = "cherrypick";
-
- TAILQ_FOREACH(re, &refs, entry) {
- const char *refname, *wt;
-
- refname = got_ref_get_name(re->ref);
-
- err = check_cancelled(NULL);
- if (err)
- goto done;
-
- if (strncmp(refname, ref_prefix, prefix_len) == 0)
- refname += prefix_len + 1; /* skip '-' delimiter */
- else
- continue;
-
- wt = refname;
-
- if (worktree == NULL || strncmp(refname, uuidstr,
- GOT_WORKTREE_UUID_STRLEN) == 0)
- refname += GOT_WORKTREE_UUID_STRLEN + 1; /* skip '-' */
- else
- continue;
-
- err = got_repo_match_object_id(&id, NULL, refname,
- GOT_OBJ_TYPE_COMMIT, NULL, repo);
- if (err)
- goto done;
-
- err = got_object_open_as_commit(&commit, repo, id);
- if (err)
- goto done;
-
- if (wanted_ref)
- found = strncmp(wanted_ref, refname,
- strlen(wanted_ref)) == 0;
- if (wanted_ref && !found) {
- struct got_reflist_head *ci_refs;
-
- ci_refs = got_reflist_object_id_map_lookup(refs_idmap,
- id);
-
- if (ci_refs) {
- char *refs_str = NULL;
- char const *r = NULL;
-
- err = build_refs_str(&refs_str, ci_refs, id,
- repo, 1);
- if (err)
- goto done;
-
- r = refs_str;
- while (r) {
- if (strncmp(r, wanted_ref,
- strlen(wanted_ref)) == 0) {
- found = 1;
- break;
- }
- r = strchr(r, ' ');
- if (r)
- ++r;
- }
- free(refs_str);
- }
- }
-
- if (wanted_ref == NULL || found) {
- if (delete) {
- err = got_ref_delete(re->ref, repo);
- if (err)
- goto done;
- printf("Deleted: ");
- err = print_commit_oneline(commit, id, repo,
- refs_idmap);
- } else {
- /*
- * Print paths modified by commit to help
- * associate commits with worktree changes.
- */
- err = get_changed_paths(&paths, commit,
- repo, NULL);
- if (err)
- goto done;
-
- err = print_commit(commit, id, repo, NULL,
- &paths, NULL, 0, 0, refs_idmap, NULL,
- header_prefix);
- got_pathlist_free(&paths,
- GOT_PATHLIST_FREE_ALL);
-
- if (worktree == NULL)
- printf("work tree: %.*s\n\n",
- GOT_WORKTREE_UUID_STRLEN, wt);
- }
- if (err || found)
- goto done;
- }
-
- got_object_commit_close(commit);
- commit = NULL;
- free(id);
- id = NULL;
- }
-
- if (wanted_ref != NULL && !found)
- err = got_error_fmt(GOT_ERR_NOT_REF, "%s", wanted_ref);
-
-done:
- free(id);
- free(uuidstr);
- got_ref_list_free(&refs);
- got_pathlist_free(&paths, GOT_PATHLIST_FREE_ALL);
- if (refs_idmap)
- got_reflist_object_id_map_free(refs_idmap);
- if (commit)
- got_object_commit_close(commit);
- return err;
-}
-
-/*
- * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
- * identified by id for log messages to prepopulate the editor on commit.
- */
-static const struct got_error *
-logmsg_ref(struct got_object_id *id, const char *prefix,
- struct got_worktree *worktree, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- char *idstr, *ref = NULL, *refname = NULL;
- int histedit_in_progress;
- int rebase_in_progress, merge_in_progress;
-
- /*
- * Silenty refuse to create merge reference if any histedit, merge,
- * or rebase operation is in progress.
- */
- err = got_worktree_histedit_in_progress(&histedit_in_progress,
- worktree);
- if (err)
- return err;
- if (histedit_in_progress)
- return NULL;
-
- err = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
- if (err)
- return err;
- if (rebase_in_progress)
- return NULL;
-
- err = got_worktree_merge_in_progress(&merge_in_progress, worktree,
- repo);
- if (err)
- return err;
- if (merge_in_progress)
- return NULL;
-
- err = got_object_id_str(&idstr, id);
- if (err)
- return err;
-
- err = got_worktree_get_logmsg_ref_name(&refname, worktree, prefix);
- if (err)
- goto done;
-
- if (asprintf(&ref, "%s-%s", refname, idstr) == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
-
- err = create_ref(ref, got_worktree_get_base_commit_id(worktree),
- -1, repo);
-done:
- free(ref);
- free(idstr);
- free(refname);
- return err;
-}
-
-__dead static void
-usage_cherrypick(void)
-{
- fprintf(stderr, "usage: %s cherrypick [-lX] [commit-id]\n",
- getprogname());
- exit(1);
-}
-
-static const struct got_error *
-cmd_cherrypick(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_worktree *worktree = NULL;
- struct got_repository *repo = NULL;
- char *cwd = NULL, *commit_id_str = NULL;
- struct got_object_id *commit_id = NULL;
- struct got_commit_object *commit = NULL;
- struct got_object_qid *pid;
- int ch, list_refs = 0, remove_refs = 0;
- struct got_update_progress_arg upa;
- int *pack_fds = NULL;
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "lX")) != -1) {
- switch (ch) {
- case 'l':
- list_refs = 1;
- break;
- case 'X':
- remove_refs = 1;
- break;
- default:
- usage_cherrypick();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (list_refs || remove_refs) {
- if (argc != 0 && argc != 1)
- usage_cherrypick();
- } else if (argc != 1)
- usage_cherrypick();
- if (list_refs && remove_refs)
- option_conflict('l', 'X');
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (list_refs || remove_refs) {
- if (error->code != GOT_ERR_NOT_WORKTREE)
- goto done;
- } else {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error,
- "cherrypick", cwd);
- goto done;
- }
- }
-
- error = got_repo_open(&repo,
- worktree ? got_worktree_get_repo_path(worktree) : cwd,
- NULL, pack_fds);
- if (error != NULL)
- goto done;
-
- error = apply_unveil(got_repo_get_path(repo), 0,
- worktree ? got_worktree_get_root_path(worktree) : NULL);
- if (error)
- goto done;
-
- if (list_refs || remove_refs) {
- error = process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
- GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN,
- argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
- goto done;
- }
-
- error = got_repo_match_object_id(&commit_id, NULL, argv[0],
- GOT_OBJ_TYPE_COMMIT, NULL, repo);
- if (error)
- goto done;
- error = got_object_id_str(&commit_id_str, commit_id);
- if (error)
- goto done;
-
- error = got_object_open_as_commit(&commit, repo, commit_id);
- if (error)
- goto done;
- pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
- memset(&upa, 0, sizeof(upa));
- error = got_worktree_merge_files(worktree, pid ? &pid->id : NULL,
- commit_id, repo, update_progress, &upa, check_cancelled,
- NULL);
- if (error != NULL)
- goto done;
-
- if (upa.did_something) {
- error = logmsg_ref(commit_id,
- GOT_WORKTREE_CHERRYPICK_REF_PREFIX, worktree, repo);
- if (error)
- goto done;
- printf("Merged commit %s\n", commit_id_str);
- }
- print_merge_progress_stats(&upa);
-done:
- free(cwd);
- if (commit)
- got_object_commit_close(commit);
- free(commit_id_str);
- if (worktree)
- got_worktree_close(worktree);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
-
- return error;
-}
-
-__dead static void
-usage_backout(void)
-{
- fprintf(stderr, "usage: %s backout [-lX] [commit-id]\n", getprogname());
- exit(1);
-}
-
-static const struct got_error *
-cmd_backout(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_worktree *worktree = NULL;
- struct got_repository *repo = NULL;
- char *cwd = NULL, *commit_id_str = NULL;
- struct got_object_id *commit_id = NULL;
- struct got_commit_object *commit = NULL;
- struct got_object_qid *pid;
- int ch, list_refs = 0, remove_refs = 0;
- struct got_update_progress_arg upa;
- int *pack_fds = NULL;
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "lX")) != -1) {
- switch (ch) {
- case 'l':
- list_refs = 1;
- break;
- case 'X':
- remove_refs = 1;
- break;
- default:
- usage_backout();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (list_refs || remove_refs) {
- if (argc != 0 && argc != 1)
- usage_backout();
- } else if (argc != 1)
- usage_backout();
- if (list_refs && remove_refs)
- option_conflict('l', 'X');
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (list_refs || remove_refs) {
- if (error->code != GOT_ERR_NOT_WORKTREE)
- goto done;
- } else {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error,
- "backout", cwd);
- goto done;
- }
- }
-
- error = got_repo_open(&repo,
- worktree ? got_worktree_get_repo_path(worktree) : cwd,
- NULL, pack_fds);
- if (error != NULL)
- goto done;
-
- error = apply_unveil(got_repo_get_path(repo), 0,
- worktree ? got_worktree_get_root_path(worktree) : NULL);
- if (error)
- goto done;
-
- if (list_refs || remove_refs) {
- error = process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX,
- GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN,
- argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
- goto done;
- }
-
- error = got_repo_match_object_id(&commit_id, NULL, argv[0],
- GOT_OBJ_TYPE_COMMIT, NULL, repo);
- if (error)
- goto done;
- error = got_object_id_str(&commit_id_str, commit_id);
- if (error)
- goto done;
-
- error = got_object_open_as_commit(&commit, repo, commit_id);
- if (error)
- goto done;
- pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
- if (pid == NULL) {
- error = got_error(GOT_ERR_ROOT_COMMIT);
- goto done;
- }
-
- memset(&upa, 0, sizeof(upa));
- error = got_worktree_merge_files(worktree, commit_id, &pid->id,
- repo, update_progress, &upa, check_cancelled, NULL);
- if (error != NULL)
- goto done;
-
- if (upa.did_something) {
- error = logmsg_ref(commit_id, GOT_WORKTREE_BACKOUT_REF_PREFIX,
- worktree, repo);
- if (error)
- goto done;
- printf("Backed out commit %s\n", commit_id_str);
- }
- print_merge_progress_stats(&upa);
-done:
- free(cwd);
- if (commit)
- got_object_commit_close(commit);
- free(commit_id_str);
- if (worktree)
- got_worktree_close(worktree);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- return error;
-}
-
-__dead static void
-usage_rebase(void)
-{
- fprintf(stderr, "usage: %s rebase [-aCclX] [branch]\n", getprogname());
- exit(1);
-}
-
-static void
-trim_logmsg(char *logmsg, int limit)
-{
- char *nl;
- size_t len;
-
- len = strlen(logmsg);
- if (len > limit)
- len = limit;
- logmsg[len] = '\0';
- nl = strchr(logmsg, '\n');
- if (nl)
- *nl = '\0';
-}
-
-static const struct got_error *
-get_short_logmsg(char **logmsg, int limit, struct got_commit_object *commit)
-{
- const struct got_error *err;
- char *logmsg0 = NULL;
- const char *s;
-
- err = got_object_commit_get_logmsg(&logmsg0, commit);
- if (err)
- return err;
-
- s = logmsg0;
- while (isspace((unsigned char)s[0]))
- s++;
-
- *logmsg = strdup(s);
- if (*logmsg == NULL) {
- err = got_error_from_errno("strdup");
- goto done;
- }
-
- trim_logmsg(*logmsg, limit);
-done:
- free(logmsg0);
- return err;
-}
-
-static const struct got_error *
-show_rebase_merge_conflict(struct got_object_id *id,
- struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_commit_object *commit = NULL;
- char *id_str = NULL, *logmsg = NULL;
-
- err = got_object_open_as_commit(&commit, repo, id);
- if (err)
- return err;
-
- err = got_object_id_str(&id_str, id);
- if (err)
- goto done;
-
- id_str[12] = '\0';
-
- err = get_short_logmsg(&logmsg, 42, commit);
- if (err)
- goto done;
-
- printf("%s -> merge conflict: %s\n", id_str, logmsg);
-done:
- free(id_str);
- got_object_commit_close(commit);
- free(logmsg);
- return err;
-}
-
-static const struct got_error *
-show_rebase_progress(struct got_commit_object *commit,
- struct got_object_id *old_id, struct got_object_id *new_id)
-{
- const struct got_error *err;
- char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
-
- err = got_object_id_str(&old_id_str, old_id);
- if (err)
- goto done;
-
- if (new_id) {
- err = got_object_id_str(&new_id_str, new_id);
- if (err)
- goto done;
- }
-
- old_id_str[12] = '\0';
- if (new_id_str)
- new_id_str[12] = '\0';
-
- err = get_short_logmsg(&logmsg, 42, commit);
- if (err)
- goto done;
-
- printf("%s -> %s: %s\n", old_id_str,
- new_id_str ? new_id_str : "no-op change", logmsg);
-done:
- free(old_id_str);
- free(new_id_str);
- free(logmsg);
- return err;
-}
-
-static const struct got_error *
-rebase_complete(struct got_worktree *worktree, struct got_fileindex *fileindex,
- struct got_reference *branch, struct got_reference *tmp_branch,
- struct got_repository *repo, int create_backup)
-{
- printf("Switching work tree to %s\n", got_ref_get_name(branch));
- return got_worktree_rebase_complete(worktree, fileindex,
- tmp_branch, branch, repo, create_backup);
-}
-
-static const struct got_error *
-rebase_commit(struct got_pathlist_head *merged_paths,
- struct got_worktree *worktree, struct got_fileindex *fileindex,
- struct got_reference *tmp_branch, const char *committer,
- struct got_object_id *commit_id, int allow_conflict,
- struct got_repository *repo)
-{
- const struct got_error *error;
- struct got_commit_object *commit;
- struct got_object_id *new_commit_id;
-
- error = got_object_open_as_commit(&commit, repo, commit_id);
- if (error)
- return error;
-
- error = got_worktree_rebase_commit(&new_commit_id, merged_paths,
- worktree, fileindex, tmp_branch, committer, commit, commit_id,
- allow_conflict, repo);
- if (error) {
- if (error->code != GOT_ERR_COMMIT_NO_CHANGES)
- goto done;
- error = show_rebase_progress(commit, commit_id, NULL);
- } else {
- error = show_rebase_progress(commit, commit_id, new_commit_id);
- free(new_commit_id);
- }
-done:
- got_object_commit_close(commit);
- return error;
-}
-
-struct check_path_prefix_arg {
- const char *path_prefix;
- size_t len;
- int errcode;
-};
-
-static const struct got_error *
-check_path_prefix_in_diff(void *arg, struct got_blob_object *blob1,
- struct got_blob_object *blob2, FILE *f1, FILE *f2,
- struct got_object_id *id1, struct got_object_id *id2,
- const char *path1, const char *path2,
- mode_t mode1, mode_t mode2, struct got_repository *repo)
-{
- struct check_path_prefix_arg *a = arg;
-
- if ((path1 && !got_path_is_child(path1, a->path_prefix, a->len)) ||
- (path2 && !got_path_is_child(path2, a->path_prefix, a->len)))
- return got_error(a->errcode);
-
- return NULL;
-}
-
-static const struct got_error *
-check_path_prefix(struct got_object_id *parent_id,
- struct got_object_id *commit_id, const char *path_prefix,
- int errcode, struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_tree_object *tree1 = NULL, *tree2 = NULL;
- struct got_commit_object *commit = NULL, *parent_commit = NULL;
- struct check_path_prefix_arg cpp_arg;
-
- if (got_path_is_root_dir(path_prefix))
- return NULL;
-
- err = got_object_open_as_commit(&commit, repo, commit_id);
- if (err)
- goto done;
-
- err = got_object_open_as_commit(&parent_commit, repo, parent_id);
- if (err)
- goto done;
-
- err = got_object_open_as_tree(&tree1, repo,
- got_object_commit_get_tree_id(parent_commit));
- if (err)
- goto done;
-
- err = got_object_open_as_tree(&tree2, repo,
- got_object_commit_get_tree_id(commit));
- if (err)
- goto done;
-
- cpp_arg.path_prefix = path_prefix;
- while (cpp_arg.path_prefix[0] == '/')
- cpp_arg.path_prefix++;
- cpp_arg.len = strlen(cpp_arg.path_prefix);
- cpp_arg.errcode = errcode;
- err = got_diff_tree(tree1, tree2, NULL, NULL, -1, -1, "", "", repo,
- check_path_prefix_in_diff, &cpp_arg, 0);
-done:
- if (tree1)
- got_object_tree_close(tree1);
- if (tree2)
- got_object_tree_close(tree2);
- if (commit)
- got_object_commit_close(commit);
- if (parent_commit)
- got_object_commit_close(parent_commit);
- return err;
-}
-
-static const struct got_error *
-collect_commits(struct got_object_id_queue *commits,
- struct got_object_id *initial_commit_id,
- struct got_object_id *iter_start_id, struct got_object_id *iter_stop_id,
- const char *path_prefix, int path_prefix_errcode,
- struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_commit_graph *graph = NULL;
- struct got_object_id parent_id, commit_id;
- struct got_object_qid *qid;
-
- err = got_commit_graph_open(&graph, "/", 1);
- if (err)
- return err;
-
- err = got_commit_graph_iter_start(graph, iter_start_id, repo,
- check_cancelled, NULL);
- if (err)
- goto done;
-
- memcpy(&commit_id, initial_commit_id, sizeof(commit_id));
- while (got_object_id_cmp(&commit_id, iter_stop_id) != 0) {
- err = got_commit_graph_iter_next(&parent_id, graph, repo,
- check_cancelled, NULL);
- if (err) {
- if (err->code == GOT_ERR_ITER_COMPLETED) {
- err = got_error_msg(GOT_ERR_ANCESTRY,
- "ran out of commits to rebase before "
- "youngest common ancestor commit has "
- "been reached?!?");
- }
- goto done;
- } else {
- err = check_path_prefix(&parent_id, &commit_id,
- path_prefix, path_prefix_errcode, repo);
- if (err)
- goto done;
-
- err = got_object_qid_alloc(&qid, &commit_id);
- if (err)
- goto done;
- STAILQ_INSERT_HEAD(commits, qid, entry);
-
- memcpy(&commit_id, &parent_id, sizeof(commit_id));
- }
- }
-done:
- got_commit_graph_close(graph);
- return err;
-}
-
-static const struct got_error *
-get_commit_brief_str(char **brief_str, struct got_commit_object *commit)
-{
- const struct got_error *err = NULL;
- time_t committer_time;
- struct tm tm;
- char datebuf[11]; /* YYYY-MM-DD + NUL */
- char *author0 = NULL, *author, *smallerthan;
- char *logmsg0 = NULL, *logmsg, *newline;
-
- committer_time = got_object_commit_get_committer_time(commit);
- if (gmtime_r(&committer_time, &tm) == NULL)
- return got_error_from_errno("gmtime_r");
- if (strftime(datebuf, sizeof(datebuf), "%G-%m-%d", &tm) == 0)
- return got_error(GOT_ERR_NO_SPACE);
-
- author0 = strdup(got_object_commit_get_author(commit));
- if (author0 == NULL)
- return got_error_from_errno("strdup");
- author = author0;
- smallerthan = strchr(author, '<');
- if (smallerthan && smallerthan[1] != '\0')
- author = smallerthan + 1;
- author[strcspn(author, "@>")] = '\0';
-
- err = got_object_commit_get_logmsg(&logmsg0, commit);
- if (err)
- goto done;
- logmsg = logmsg0;
- while (*logmsg == '\n')
- logmsg++;
- newline = strchr(logmsg, '\n');
- if (newline)
- *newline = '\0';
-
- if (asprintf(brief_str, "%s %s %s",
- datebuf, author, logmsg) == -1)
- err = got_error_from_errno("asprintf");
-done:
- free(author0);
- free(logmsg0);
- return err;
-}
-
-static const struct got_error *
-delete_backup_ref(struct got_reference *ref, struct got_object_id *id,
- struct got_repository *repo)
-{
- const struct got_error *err;
- char *id_str;
-
- err = got_object_id_str(&id_str, id);
- if (err)
- return err;
-
- err = got_ref_delete(ref, repo);
- if (err)
- goto done;
-
- printf("Deleted %s: %s\n", got_ref_get_name(ref), id_str);
-done:
- free(id_str);
- return err;
-}
-
-static const struct got_error *
-print_backup_ref(const char *branch_name, const char *new_id_str,
- struct got_object_id *old_commit_id, struct got_commit_object *old_commit,
- struct got_reflist_object_id_map *refs_idmap,
- struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_reflist_head *refs;
- char *refs_str = NULL;
- struct got_object_id *new_commit_id = NULL;
- struct got_commit_object *new_commit = NULL;
- char *new_commit_brief_str = NULL;
- struct got_object_id *yca_id = NULL;
- struct got_commit_object *yca_commit = NULL;
- char *yca_id_str = NULL, *yca_brief_str = NULL;
- char *custom_refs_str;
-
- if (asprintf(&custom_refs_str, "formerly %s", branch_name) == -1)
- return got_error_from_errno("asprintf");
-
- err = print_commit(old_commit, old_commit_id, repo, NULL, NULL, NULL,
- 0, 0, refs_idmap, custom_refs_str, NULL);
- if (err)
- goto done;
-
- err = got_object_resolve_id_str(&new_commit_id, repo, new_id_str);
- if (err)
- goto done;
-
- refs = got_reflist_object_id_map_lookup(refs_idmap, new_commit_id);
- if (refs) {
- err = build_refs_str(&refs_str, refs, new_commit_id, repo, 0);
- if (err)
- goto done;
- }
-
- err = got_object_open_as_commit(&new_commit, repo, new_commit_id);
- if (err)
- goto done;
-
- err = get_commit_brief_str(&new_commit_brief_str, new_commit);
- if (err)
- goto done;
-
- err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
- old_commit_id, new_commit_id, 1, repo, check_cancelled, NULL);
- if (err)
- goto done;
-
- printf("has become commit %s%s%s%s\n %s\n", new_id_str,
- refs_str ? " (" : "", refs_str ? refs_str : "",
- refs_str ? ")" : "", new_commit_brief_str);
- if (yca_id && got_object_id_cmp(yca_id, new_commit_id) != 0 &&
- got_object_id_cmp(yca_id, old_commit_id) != 0) {
- free(refs_str);
- refs_str = NULL;
-
- err = got_object_open_as_commit(&yca_commit, repo, yca_id);
- if (err)
- goto done;
-
- err = get_commit_brief_str(&yca_brief_str, yca_commit);
- if (err)
- goto done;
-
- err = got_object_id_str(&yca_id_str, yca_id);
- if (err)
- goto done;
-
- refs = got_reflist_object_id_map_lookup(refs_idmap, yca_id);
- if (refs) {
- err = build_refs_str(&refs_str, refs, yca_id, repo, 0);
- if (err)
- goto done;
- }
- printf("history forked at %s%s%s%s\n %s\n",
- yca_id_str,
- refs_str ? " (" : "", refs_str ? refs_str : "",
- refs_str ? ")" : "", yca_brief_str);
- }
-done:
- free(custom_refs_str);
- free(new_commit_id);
- free(refs_str);
- free(yca_id);
- free(yca_id_str);
- free(yca_brief_str);
- if (new_commit)
- got_object_commit_close(new_commit);
- if (yca_commit)
- got_object_commit_close(yca_commit);
-
- return err;
-}
-
-static const struct got_error *
-worktree_has_logmsg_ref(const char *caller, struct got_worktree *worktree,
- struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_reflist_head refs;
- struct got_reflist_entry *re;
- char *uuidstr = NULL;
- static char msg[160];
-
- TAILQ_INIT(&refs);
-
- err = got_worktree_get_uuid(&uuidstr, worktree);
- if (err)
- goto done;
-
- err = got_ref_list(&refs, repo, "refs/got/worktree",
- got_ref_cmp_by_name, repo);
- if (err)
- goto done;
-
- TAILQ_FOREACH(re, &refs, entry) {
- const char *cmd, *refname, *type;
-
- refname = got_ref_get_name(re->ref);
-
- if (strncmp(refname, GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
- GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN) == 0) {
- refname += GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN + 1;
- cmd = "cherrypick";
- type = "cherrypicked";
- } else if (strncmp(refname, GOT_WORKTREE_BACKOUT_REF_PREFIX,
- GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN) == 0) {
- refname += GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN + 1;
- cmd = "backout";
- type = "backed-out";
- } else
- continue;
-
- if (strncmp(refname, uuidstr, GOT_WORKTREE_UUID_STRLEN) != 0)
- continue;
-
- snprintf(msg, sizeof(msg),
- "work tree has references created by %s commits which "
- "must be removed with 'got %s -X' before running the %s "
- "command", type, cmd, caller);
- err = got_error_msg(GOT_ERR_WORKTREE_META, msg);
- goto done;
- }
-
-done:
- free(uuidstr);
- got_ref_list_free(&refs);
- return err;
-}
-
-static const struct got_error *
-process_backup_refs(const char *backup_ref_prefix,
- const char *wanted_branch_name,
- int delete, struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_reflist_head refs, backup_refs;
- struct got_reflist_entry *re;
- const size_t backup_ref_prefix_len = strlen(backup_ref_prefix);
- struct got_object_id *old_commit_id = NULL;
- char *branch_name = NULL;
- struct got_commit_object *old_commit = NULL;
- struct got_reflist_object_id_map *refs_idmap = NULL;
- int wanted_branch_found = 0;
-
- TAILQ_INIT(&refs);
- TAILQ_INIT(&backup_refs);
-
- err = got_ref_list(&refs, repo, NULL, got_ref_cmp_by_name, NULL);
- if (err)
- return err;
-
- err = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
- if (err)
- goto done;
-
- if (wanted_branch_name) {
- if (strncmp(wanted_branch_name, "refs/heads/", 11) == 0)
- wanted_branch_name += 11;
- }
-
- err = got_ref_list(&backup_refs, repo, backup_ref_prefix,
- got_ref_cmp_by_commit_timestamp_descending, repo);
- if (err)
- goto done;
-
- TAILQ_FOREACH(re, &backup_refs, entry) {
- const char *refname = got_ref_get_name(re->ref);
- char *slash;
-
- err = check_cancelled(NULL);
- if (err)
- break;
-
- err = got_ref_resolve(&old_commit_id, repo, re->ref);
- if (err)
- break;
-
- err = got_object_open_as_commit(&old_commit, repo,
- old_commit_id);
- if (err)
- break;
-
- if (strncmp(backup_ref_prefix, refname,
- backup_ref_prefix_len) == 0)
- refname += backup_ref_prefix_len;
-
- while (refname[0] == '/')
- refname++;
-
- branch_name = strdup(refname);
- if (branch_name == NULL) {
- err = got_error_from_errno("strdup");
- break;
- }
- slash = strrchr(branch_name, '/');
- if (slash) {
- *slash = '\0';
- refname += strlen(branch_name) + 1;
- }
-
- if (wanted_branch_name == NULL ||
- strcmp(wanted_branch_name, branch_name) == 0) {
- wanted_branch_found = 1;
- if (delete) {
- err = delete_backup_ref(re->ref,
- old_commit_id, repo);
- } else {
- err = print_backup_ref(branch_name, refname,
- old_commit_id, old_commit, refs_idmap,
- repo);
- }
- if (err)
- break;
- }
-
- free(old_commit_id);
- old_commit_id = NULL;
- free(branch_name);
- branch_name = NULL;
- got_object_commit_close(old_commit);
- old_commit = NULL;
- }
-
- if (wanted_branch_name && !wanted_branch_found) {
- err = got_error_fmt(GOT_ERR_NOT_REF,
- "%s/%s/", backup_ref_prefix, wanted_branch_name);
- }
-done:
- if (refs_idmap)
- got_reflist_object_id_map_free(refs_idmap);
- got_ref_list_free(&refs);
- got_ref_list_free(&backup_refs);
- free(old_commit_id);
- free(branch_name);
- if (old_commit)
- got_object_commit_close(old_commit);
- return err;
-}
-
-static const struct got_error *
-abort_progress(void *arg, unsigned char status, const char *path)
-{
- /*
- * Unversioned files should not clutter progress output when
- * an operation is aborted.
- */
- if (status == GOT_STATUS_UNVERSIONED)
- return NULL;
-
- return update_progress(arg, status, path);
-}
-
-static const struct got_error *
-cmd_rebase(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_worktree *worktree = NULL;
- struct got_repository *repo = NULL;
- struct got_fileindex *fileindex = NULL;
- char *cwd = NULL, *committer = NULL, *gitconfig_path = NULL;
- struct got_reference *branch = NULL;
- struct got_reference *new_base_branch = NULL, *tmp_branch = NULL;
- struct got_object_id *commit_id = NULL, *parent_id = NULL;
- struct got_object_id *resume_commit_id = NULL;
- struct got_object_id *branch_head_commit_id = NULL, *yca_id = NULL;
- struct got_object_id *head_commit_id = NULL;
- struct got_reference *head_ref = NULL;
- struct got_commit_object *commit = NULL;
- int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0;
- int histedit_in_progress = 0, merge_in_progress = 0;
- int create_backup = 1, list_backups = 0, delete_backups = 0;
- int allow_conflict = 0;
- struct got_object_id_queue commits;
- struct got_pathlist_head merged_paths;
- const struct got_object_id_queue *parent_ids;
- struct got_object_qid *qid, *pid;
- struct got_update_progress_arg upa;
- int *pack_fds = NULL;
-
- STAILQ_INIT(&commits);
- TAILQ_INIT(&merged_paths);
- memset(&upa, 0, sizeof(upa));
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "aCclX")) != -1) {
- switch (ch) {
- case 'a':
- abort_rebase = 1;
- break;
- case 'C':
- allow_conflict = 1;
- break;
- case 'c':
- continue_rebase = 1;
- break;
- case 'l':
- list_backups = 1;
- break;
- case 'X':
- delete_backups = 1;
- break;
- default:
- usage_rebase();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (list_backups) {
- if (abort_rebase)
- option_conflict('l', 'a');
- if (allow_conflict)
- option_conflict('l', 'C');
- if (continue_rebase)
- option_conflict('l', 'c');
- if (delete_backups)
- option_conflict('l', 'X');
- if (argc != 0 && argc != 1)
- usage_rebase();
- } else if (delete_backups) {
- if (abort_rebase)
- option_conflict('X', 'a');
- if (allow_conflict)
- option_conflict('X', 'C');
- if (continue_rebase)
- option_conflict('X', 'c');
- if (list_backups)
- option_conflict('l', 'X');
- if (argc != 0 && argc != 1)
- usage_rebase();
- } else if (allow_conflict) {
- if (abort_rebase)
- option_conflict('C', 'a');
- if (!continue_rebase)
- errx(1, "-C option requires -c");
- } else {
- if (abort_rebase && continue_rebase)
- usage_rebase();
- else if (abort_rebase || continue_rebase) {
- if (argc != 0)
- usage_rebase();
- } else if (argc != 1)
- usage_rebase();
- }
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (list_backups || delete_backups) {
- if (error->code != GOT_ERR_NOT_WORKTREE)
- goto done;
- } else {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error,
- "rebase", cwd);
- goto done;
- }
- }
-
- error = get_gitconfig_path(&gitconfig_path);
- if (error)
- goto done;
- error = got_repo_open(&repo,
- worktree ? got_worktree_get_repo_path(worktree) : cwd,
- gitconfig_path, pack_fds);
- if (error != NULL)
- goto done;
-
- if (worktree != NULL && !list_backups && !delete_backups) {
- error = worktree_has_logmsg_ref("rebase", worktree, repo);
- if (error)
- goto done;
- }
-
- error = get_author(&committer, repo, worktree);
- if (error && error->code != GOT_ERR_COMMIT_NO_AUTHOR)
- goto done;
-
- error = apply_unveil(got_repo_get_path(repo), 0,
- worktree ? got_worktree_get_root_path(worktree) : NULL);
- if (error)
- goto done;
-
- if (list_backups || delete_backups) {
- error = process_backup_refs(
- GOT_WORKTREE_REBASE_BACKUP_REF_PREFIX,
- argc == 1 ? argv[0] : NULL, delete_backups, repo);
- goto done; /* nothing else to do */
- }
-
- error = got_worktree_histedit_in_progress(&histedit_in_progress,
- worktree);
- if (error)
- goto done;
- if (histedit_in_progress) {
- error = got_error(GOT_ERR_HISTEDIT_BUSY);
- goto done;
- }
-
- error = got_worktree_merge_in_progress(&merge_in_progress,
- worktree, repo);
- if (error)
- goto done;
- if (merge_in_progress) {
- error = got_error(GOT_ERR_MERGE_BUSY);
- goto done;
- }
-
- error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
- if (error)
- goto done;
-
- if (abort_rebase) {
- if (!rebase_in_progress) {
- error = got_error(GOT_ERR_NOT_REBASING);
- goto done;
- }
- error = got_worktree_rebase_continue(&resume_commit_id,
- &new_base_branch, &tmp_branch, &branch, &fileindex,
- worktree, repo);
- if (error)
- goto done;
- printf("Switching work tree to %s\n",
- got_ref_get_symref_target(new_base_branch));
- error = got_worktree_rebase_abort(worktree, fileindex, repo,
- new_base_branch, abort_progress, &upa);
- if (error)
- goto done;
- printf("Rebase of %s aborted\n", got_ref_get_name(branch));
- print_merge_progress_stats(&upa);
- goto done; /* nothing else to do */
- }
-
- if (continue_rebase) {
- if (!rebase_in_progress) {
- error = got_error(GOT_ERR_NOT_REBASING);
- goto done;
- }
- error = got_worktree_rebase_continue(&resume_commit_id,
- &new_base_branch, &tmp_branch, &branch, &fileindex,
- worktree, repo);
- if (error)
- goto done;
-
- error = rebase_commit(NULL, worktree, fileindex, tmp_branch,
- committer, resume_commit_id, allow_conflict, repo);
- if (error)
- goto done;
-
- yca_id = got_object_id_dup(resume_commit_id);
- if (yca_id == NULL) {
- error = got_error_from_errno("got_object_id_dup");
- goto done;
- }
- } else {
- error = got_ref_open(&branch, repo, argv[0], 0);
- if (error != NULL)
- goto done;
- if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
- error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
- "will not rebase a branch which lives outside "
- "the \"refs/heads/\" reference namespace");
- goto done;
- }
- }
-
- error = got_ref_resolve(&branch_head_commit_id, repo, branch);
- if (error)
- goto done;
-
- if (!continue_rebase) {
- struct got_object_id *base_commit_id;
-
- error = got_ref_open(&head_ref, repo,
- got_worktree_get_head_ref_name(worktree), 0);
- if (error)
- goto done;
- error = got_ref_resolve(&head_commit_id, repo, head_ref);
- if (error)
- goto done;
- base_commit_id = got_worktree_get_base_commit_id(worktree);
- if (got_object_id_cmp(base_commit_id, head_commit_id) != 0) {
- error = got_error(GOT_ERR_REBASE_OUT_OF_DATE);
- goto done;
- }
-
- error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
- base_commit_id, branch_head_commit_id, 1, repo,
- check_cancelled, NULL);
- if (error) {
- if (error->code == GOT_ERR_ANCESTRY) {
- error = got_error_msg(GOT_ERR_ANCESTRY,
- "specified branch shares no common "
- "ancestry with work tree's branch");
- }
- goto done;
- }
-
- if (got_object_id_cmp(base_commit_id, yca_id) == 0) {
- struct got_pathlist_head paths;
- printf("%s is already based on %s\n",
- got_ref_get_name(branch),
- got_worktree_get_head_ref_name(worktree));
- error = switch_head_ref(branch, branch_head_commit_id,
- worktree, repo);
- if (error)
- goto done;
- error = got_worktree_set_base_commit_id(worktree, repo,
- branch_head_commit_id);
- if (error)
- goto done;
- TAILQ_INIT(&paths);
- error = got_pathlist_append(&paths, "", NULL);
- if (error)
- goto done;
- error = got_worktree_checkout_files(worktree,
- &paths, repo, update_progress, &upa,
- check_cancelled, NULL);
- got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
- if (error)
- goto done;
- if (upa.did_something) {
- char *id_str;
- error = got_object_id_str(&id_str,
- branch_head_commit_id);
- if (error)
- goto done;
- printf("Updated to %s: %s\n",
- got_worktree_get_head_ref_name(worktree),
- id_str);
- free(id_str);
- } else
- printf("Already up-to-date\n");
- print_update_progress_stats(&upa);
- goto done;
- }
- }
-
- commit_id = branch_head_commit_id;
- error = got_object_open_as_commit(&commit, repo, commit_id);
- if (error)
- goto done;
-
- parent_ids = got_object_commit_get_parent_ids(commit);
- pid = STAILQ_FIRST(parent_ids);
- if (pid) {
- error = collect_commits(&commits, commit_id, &pid->id,
- yca_id, got_worktree_get_path_prefix(worktree),
- GOT_ERR_REBASE_PATH, repo);
- if (error)
- goto done;
- }
-
- got_object_commit_close(commit);
- commit = NULL;
-
- if (!continue_rebase) {
- error = got_worktree_rebase_prepare(&new_base_branch,
- &tmp_branch, &fileindex, worktree, branch, repo);
- if (error)
- goto done;
- }
-
- if (STAILQ_EMPTY(&commits)) {
- if (continue_rebase) {
- error = rebase_complete(worktree, fileindex,
- branch, tmp_branch, repo, create_backup);
- goto done;
- } else {
- /* Fast-forward the reference of the branch. */
- struct got_object_id *new_head_commit_id;
- char *id_str;
- error = got_ref_resolve(&new_head_commit_id, repo,
- new_base_branch);
- if (error)
- goto done;
- error = got_object_id_str(&id_str, new_head_commit_id);
- if (error)
- goto done;
- printf("Forwarding %s to commit %s\n",
- got_ref_get_name(branch), id_str);
- free(id_str);
- error = got_ref_change_ref(branch,
- new_head_commit_id);
- if (error)
- goto done;
- /* No backup needed since objects did not change. */
- create_backup = 0;
- }
- }
-
- pid = NULL;
- STAILQ_FOREACH(qid, &commits, entry) {
-
- commit_id = &qid->id;
- parent_id = pid ? &pid->id : yca_id;
- pid = qid;
-
- memset(&upa, 0, sizeof(upa));
- error = got_worktree_rebase_merge_files(&merged_paths,
- worktree, fileindex, parent_id, commit_id, repo,
- update_progress, &upa, check_cancelled, NULL);
- if (error)
- goto done;
-
- print_merge_progress_stats(&upa);
- if (upa.conflicts > 0 || upa.missing > 0 ||
- upa.not_deleted > 0 || upa.unversioned > 0) {
- if (upa.conflicts > 0) {
- error = show_rebase_merge_conflict(&qid->id,
- repo);
- if (error)
- goto done;
- }
- got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
- break;
- }
-
- error = rebase_commit(&merged_paths, worktree, fileindex,
- tmp_branch, committer, commit_id, 0, repo);
- got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
- if (error)
- goto done;
- }
-
- if (upa.conflicts > 0 || upa.missing > 0 ||
- upa.not_deleted > 0 || upa.unversioned > 0) {
- error = got_worktree_rebase_postpone(worktree, fileindex);
- if (error)
- goto done;
- if (upa.conflicts > 0 && upa.missing == 0 &&
- upa.not_deleted == 0 && upa.unversioned == 0) {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "conflicts must be resolved before rebasing "
- "can continue");
- } else if (upa.conflicts > 0) {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "conflicts must be resolved before rebasing "
- "can continue; changes destined for some "
- "files were not yet merged and should be "
- "merged manually if required before the "
- "rebase operation is continued");
- } else {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "changes destined for some files were not "
- "yet merged and should be merged manually "
- "if required before the rebase operation "
- "is continued");
- }
- } else
- error = rebase_complete(worktree, fileindex, branch,
- tmp_branch, repo, create_backup);
-done:
- free(cwd);
- free(committer);
- free(gitconfig_path);
- got_object_id_queue_free(&commits);
- free(branch_head_commit_id);
- free(resume_commit_id);
- free(head_commit_id);
- free(yca_id);
- if (commit)
- got_object_commit_close(commit);
- if (branch)
- got_ref_close(branch);
- if (new_base_branch)
- got_ref_close(new_base_branch);
- if (tmp_branch)
- got_ref_close(tmp_branch);
- if (head_ref)
- got_ref_close(head_ref);
- if (worktree)
- got_worktree_close(worktree);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- return error;
-}
-
-__dead static void
-usage_histedit(void)
-{
- fprintf(stderr, "usage: %s histedit [-aCcdeflmX] [-F histedit-script] "
- "[branch]\n", getprogname());
- exit(1);
-}
-
-#define GOT_HISTEDIT_PICK 'p'
-#define GOT_HISTEDIT_EDIT 'e'
-#define GOT_HISTEDIT_FOLD 'f'
-#define GOT_HISTEDIT_DROP 'd'
-#define GOT_HISTEDIT_MESG 'm'
-
-static const struct got_histedit_cmd {
- unsigned char code;
- const char *name;
- const char *desc;
-} got_histedit_cmds[] = {
- { GOT_HISTEDIT_PICK, "pick", "use commit" },
- { GOT_HISTEDIT_EDIT, "edit", "use commit but stop for amending" },
- { GOT_HISTEDIT_FOLD, "fold", "combine with next commit that will "
- "be used" },
- { GOT_HISTEDIT_DROP, "drop", "remove commit from history" },
- { GOT_HISTEDIT_MESG, "mesg",
- "single-line log message for commit above (open editor if empty)" },
-};
-
-struct got_histedit_list_entry {
- TAILQ_ENTRY(got_histedit_list_entry) entry;
- struct got_object_id *commit_id;
- const struct got_histedit_cmd *cmd;
- char *logmsg;
-};
-TAILQ_HEAD(got_histedit_list, got_histedit_list_entry);
-
-static const struct got_error *
-histedit_write_commit(struct got_object_id *commit_id, const char *cmdname,
- FILE *f, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- char *logmsg = NULL, *id_str = NULL;
- struct got_commit_object *commit = NULL;
- int n;
-
- err = got_object_open_as_commit(&commit, repo, commit_id);
- if (err)
- goto done;
-
- err = get_short_logmsg(&logmsg, 34, commit);
- if (err)
- goto done;
-
- err = got_object_id_str(&id_str, commit_id);
- if (err)
- goto done;
-
- n = fprintf(f, "%s %s %s\n", cmdname, id_str, logmsg);
- if (n < 0)
- err = got_ferror(f, GOT_ERR_IO);
-done:
- if (commit)
- got_object_commit_close(commit);
- free(id_str);
- free(logmsg);
- return err;
-}
-
-static const struct got_error *
-histedit_write_commit_list(struct got_object_id_queue *commits,
- FILE *f, int edit_logmsg_only, int fold_only, int drop_only,
- int edit_only, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_object_qid *qid;
- const char *histedit_cmd = NULL;
-
- if (STAILQ_EMPTY(commits))
- return got_error(GOT_ERR_EMPTY_HISTEDIT);
-
- STAILQ_FOREACH(qid, commits, entry) {
- histedit_cmd = got_histedit_cmds[0].name;
- if (drop_only)
- histedit_cmd = "drop";
- else if (edit_only)
- histedit_cmd = "edit";
- else if (fold_only && STAILQ_NEXT(qid, entry) != NULL)
- histedit_cmd = "fold";
- err = histedit_write_commit(&qid->id, histedit_cmd, f, repo);
- if (err)
- break;
- if (edit_logmsg_only) {
- int n = fprintf(f, "%c\n", GOT_HISTEDIT_MESG);
- if (n < 0) {
- err = got_ferror(f, GOT_ERR_IO);
- break;
- }
- }
- }
-
- return err;
-}
-
-static const struct got_error *
-write_cmd_list(FILE *f, const char *branch_name,
- struct got_object_id_queue *commits)
-{
- const struct got_error *err = NULL;
- size_t i;
- int n;
- char *id_str;
- struct got_object_qid *qid;
-
- qid = STAILQ_FIRST(commits);
- err = got_object_id_str(&id_str, &qid->id);
- if (err)
- return err;
-
- n = fprintf(f,
- "# Editing the history of branch '%s' starting at\n"
- "# commit %s\n"
- "# Commits will be processed in order from top to "
- "bottom of this file.\n", branch_name, id_str);
- if (n < 0) {
- err = got_ferror(f, GOT_ERR_IO);
- goto done;
- }
-
- n = fprintf(f, "# Available histedit commands:\n");
- if (n < 0) {
- err = got_ferror(f, GOT_ERR_IO);
- goto done;
- }
-
- for (i = 0; i < nitems(got_histedit_cmds); i++) {
- const struct got_histedit_cmd *cmd = &got_histedit_cmds[i];
- n = fprintf(f, "# %s (%c): %s\n", cmd->name, cmd->code,
- cmd->desc);
- if (n < 0) {
- err = got_ferror(f, GOT_ERR_IO);
- break;
- }
- }
-done:
- free(id_str);
- return err;
-}
-
-static const struct got_error *
-histedit_syntax_error(int lineno)
-{
- static char msg[42];
- int ret;
-
- ret = snprintf(msg, sizeof(msg), "histedit syntax error on line %d",
- lineno);
- if (ret < 0 || (size_t)ret >= sizeof(msg))
- return got_error(GOT_ERR_HISTEDIT_SYNTAX);
-
- return got_error_msg(GOT_ERR_HISTEDIT_SYNTAX, msg);
-}
-
-static const struct got_error *
-append_folded_commit_msg(char **new_msg, struct got_histedit_list_entry *hle,
- char *logmsg, struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_commit_object *folded_commit = NULL;
- char *id_str, *folded_logmsg = NULL;
-
- err = got_object_id_str(&id_str, hle->commit_id);
- if (err)
- return err;
-
- err = got_object_open_as_commit(&folded_commit, repo, hle->commit_id);
- if (err)
- goto done;
-
- err = got_object_commit_get_logmsg(&folded_logmsg, folded_commit);
- if (err)
- goto done;
- if (asprintf(new_msg, "%s%s# log message of folded commit %s: %s",
- logmsg ? logmsg : "", logmsg ? "\n" : "", id_str,
- folded_logmsg) == -1) {
- err = got_error_from_errno("asprintf");
- }
-done:
- if (folded_commit)
- got_object_commit_close(folded_commit);
- free(id_str);
- free(folded_logmsg);
- return err;
-}
-
-static struct got_histedit_list_entry *
-get_folded_commits(struct got_histedit_list_entry *hle)
-{
- struct got_histedit_list_entry *prev, *folded = NULL;
-
- prev = TAILQ_PREV(hle, got_histedit_list, entry);
- while (prev && (prev->cmd->code == GOT_HISTEDIT_FOLD ||
- prev->cmd->code == GOT_HISTEDIT_DROP)) {
- if (prev->cmd->code == GOT_HISTEDIT_FOLD)
- folded = prev;
- prev = TAILQ_PREV(prev, got_histedit_list, entry);
- }
-
- return folded;
-}
-
-static const struct got_error *
-histedit_edit_logmsg(struct got_histedit_list_entry *hle,
- struct got_repository *repo)
-{
- char *logmsg_path = NULL, *id_str = NULL, *orig_logmsg = NULL;
- char *logmsg = NULL, *new_msg = NULL, *editor = NULL;
- const struct got_error *err = NULL;
- struct got_commit_object *commit = NULL;
- int logmsg_len;
- int fd = -1;
- struct got_histedit_list_entry *folded = NULL;
-
- err = got_object_open_as_commit(&commit, repo, hle->commit_id);
- if (err)
- return err;
-
- folded = get_folded_commits(hle);
- if (folded) {
- while (folded != hle) {
- if (folded->cmd->code == GOT_HISTEDIT_DROP) {
- folded = TAILQ_NEXT(folded, entry);
- continue;
- }
- err = append_folded_commit_msg(&new_msg, folded,
- logmsg, repo);
- if (err)
- goto done;
- free(logmsg);
- logmsg = new_msg;
- folded = TAILQ_NEXT(folded, entry);
- }
- }
-
- err = got_object_id_str(&id_str, hle->commit_id);
- if (err)
- goto done;
- err = got_object_commit_get_logmsg(&orig_logmsg, commit);
- if (err)
- goto done;
- logmsg_len = asprintf(&new_msg,
- "%s\n# original log message of commit %s: %s",
- logmsg ? logmsg : "", id_str, orig_logmsg);
- if (logmsg_len == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
- free(logmsg);
- logmsg = new_msg;
-
- err = got_object_id_str(&id_str, hle->commit_id);
- if (err)
- goto done;
-
- err = got_opentemp_named_fd(&logmsg_path, &fd,
- GOT_TMPDIR_STR "/got-logmsg", "");
- if (err)
- goto done;
-
- if (write(fd, logmsg, logmsg_len) == -1) {
- err = got_error_from_errno2("write", logmsg_path);
- goto done;
- }
- if (close(fd) == -1) {
- err = got_error_from_errno2("close", logmsg_path);
- goto done;
- }
- fd = -1;
-
- err = get_editor(&editor);
- if (err)
- goto done;
-
- err = edit_logmsg(&hle->logmsg, editor, logmsg_path, logmsg,
- logmsg_len, 0);
- if (err) {
- if (err->code != GOT_ERR_COMMIT_MSG_EMPTY)
- goto done;
- err = NULL;
- hle->logmsg = strdup(new_msg);
- if (hle->logmsg == NULL)
- err = got_error_from_errno("strdup");
- }
-done:
- if (fd != -1 && close(fd) == -1 && err == NULL)
- err = got_error_from_errno2("close", logmsg_path);
- if (logmsg_path && unlink(logmsg_path) != 0 && err == NULL)
- err = got_error_from_errno2("unlink", logmsg_path);
- free(logmsg_path);
- free(logmsg);
- free(orig_logmsg);
- free(editor);
- if (commit)
- got_object_commit_close(commit);
- return err;
-}
-
-static const struct got_error *
-histedit_parse_list(struct got_histedit_list *histedit_cmds,
- FILE *f, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- char *line = NULL, *p, *end;
- size_t i, linesize = 0;
- ssize_t linelen;
- int lineno = 0, lastcmd = -1;
- const struct got_histedit_cmd *cmd;
- struct got_object_id *commit_id = NULL;
- struct got_histedit_list_entry *hle = NULL;
-
- for (;;) {
- linelen = getline(&line, &linesize, f);
- if (linelen == -1) {
- const struct got_error *getline_err;
- if (feof(f))
- break;
- getline_err = got_error_from_errno("getline");
- err = got_ferror(f, getline_err->code);
- break;
- }
- lineno++;
- p = line;
- while (isspace((unsigned char)p[0]))
- p++;
- if (p[0] == '#' || p[0] == '\0')
- continue;
- cmd = NULL;
- for (i = 0; i < nitems(got_histedit_cmds); i++) {
- cmd = &got_histedit_cmds[i];
- if (strncmp(cmd->name, p, strlen(cmd->name)) == 0 &&
- isspace((unsigned char)p[strlen(cmd->name)])) {
- p += strlen(cmd->name);
- break;
- }
- if (p[0] == cmd->code && isspace((unsigned char)p[1])) {
- p++;
- break;
- }
- }
- if (i == nitems(got_histedit_cmds)) {
- err = histedit_syntax_error(lineno);
- break;
- }
- while (isspace((unsigned char)p[0]))
- p++;
- if (cmd->code == GOT_HISTEDIT_MESG) {
- if (lastcmd != GOT_HISTEDIT_PICK &&
- lastcmd != GOT_HISTEDIT_EDIT) {
- err = got_error(GOT_ERR_HISTEDIT_CMD);
- break;
- }
- if (p[0] == '\0') {
- err = histedit_edit_logmsg(hle, repo);
- if (err)
- break;
- } else {
- hle->logmsg = strdup(p);
- if (hle->logmsg == NULL) {
- err = got_error_from_errno("strdup");
- break;
- }
- }
- lastcmd = cmd->code;
- continue;
- } else {
- end = p;
- while (end[0] && !isspace((unsigned char)end[0]))
- end++;
- *end = '\0';
-
- err = got_object_resolve_id_str(&commit_id, repo, p);
- if (err) {
- /* override error code */
- err = histedit_syntax_error(lineno);
- break;
- }
- }
- hle = malloc(sizeof(*hle));
- if (hle == NULL) {
- err = got_error_from_errno("malloc");
- break;
- }
- hle->cmd = cmd;
- hle->commit_id = commit_id;
- hle->logmsg = NULL;
- commit_id = NULL;
- TAILQ_INSERT_TAIL(histedit_cmds, hle, entry);
- lastcmd = cmd->code;
- }
-
- free(line);
- free(commit_id);
- return err;
-}
-
-static const struct got_error *
-histedit_check_script(struct got_histedit_list *histedit_cmds,
- struct got_object_id_queue *commits, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- struct got_object_qid *qid;
- struct got_histedit_list_entry *hle;
- static char msg[92];
- char *id_str;
-
- if (TAILQ_EMPTY(histedit_cmds))
- return got_error_msg(GOT_ERR_EMPTY_HISTEDIT,
- "histedit script contains no commands");
- if (STAILQ_EMPTY(commits))
- return got_error(GOT_ERR_EMPTY_HISTEDIT);
-
- TAILQ_FOREACH(hle, histedit_cmds, entry) {
- struct got_histedit_list_entry *hle2;
- TAILQ_FOREACH(hle2, histedit_cmds, entry) {
- if (hle == hle2)
- continue;
- if (got_object_id_cmp(hle->commit_id,
- hle2->commit_id) != 0)
- continue;
- err = got_object_id_str(&id_str, hle->commit_id);
- if (err)
- return err;
- snprintf(msg, sizeof(msg), "commit %s is listed "
- "more than once in histedit script", id_str);
- free(id_str);
- return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
- }
- }
-
- STAILQ_FOREACH(qid, commits, entry) {
- TAILQ_FOREACH(hle, histedit_cmds, entry) {
- if (got_object_id_cmp(&qid->id, hle->commit_id) == 0)
- break;
- }
- if (hle == NULL) {
- err = got_object_id_str(&id_str, &qid->id);
- if (err)
- return err;
- snprintf(msg, sizeof(msg),
- "commit %s missing from histedit script", id_str);
- free(id_str);
- return got_error_msg(GOT_ERR_HISTEDIT_CMD, msg);
- }
- }
-
- hle = TAILQ_LAST(histedit_cmds, got_histedit_list);
- if (hle && hle->cmd->code == GOT_HISTEDIT_FOLD)
- return got_error_msg(GOT_ERR_HISTEDIT_CMD,
- "last commit in histedit script cannot be folded");
-
- return NULL;
-}
-
-static const struct got_error *
-histedit_run_editor(struct got_histedit_list *histedit_cmds,
- const char *path, struct got_object_id_queue *commits,
- struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- char *editor;
- FILE *f = NULL;
-
- err = get_editor(&editor);
- if (err)
- return err;
-
- if (spawn_editor(editor, path) == -1) {
- err = got_error_from_errno("failed spawning editor");
- goto done;
- }
-
- f = fopen(path, "re");
- if (f == NULL) {
- err = got_error_from_errno("fopen");
- goto done;
- }
- err = histedit_parse_list(histedit_cmds, f, repo);
- if (err)
- goto done;
-
- err = histedit_check_script(histedit_cmds, commits, repo);
-done:
- if (f && fclose(f) == EOF && err == NULL)
- err = got_error_from_errno("fclose");
- free(editor);
- return err;
-}
-
-static const struct got_error *
-histedit_edit_list_retry(struct got_histedit_list *, const struct got_error *,
- struct got_object_id_queue *, const char *, const char *,
- struct got_repository *);
-
-static const struct got_error *
-histedit_edit_script(struct got_histedit_list *histedit_cmds,
- struct got_object_id_queue *commits, const char *branch_name,
- int edit_logmsg_only, int fold_only, int drop_only, int edit_only,
- struct got_repository *repo)
-{
- const struct got_error *err;
- FILE *f = NULL;
- char *path = NULL;
-
- err = got_opentemp_named(&path, &f, "got-histedit", "");
- if (err)
- return err;
-
- err = write_cmd_list(f, branch_name, commits);
- if (err)
- goto done;
-
- err = histedit_write_commit_list(commits, f, edit_logmsg_only,
- fold_only, drop_only, edit_only, repo);
- if (err)
- goto done;
-
- if (drop_only || edit_logmsg_only || fold_only || edit_only) {
- rewind(f);
- err = histedit_parse_list(histedit_cmds, f, repo);
- } else {
- if (fclose(f) == EOF) {
- err = got_error_from_errno("fclose");
- goto done;
- }
- f = NULL;
- err = histedit_run_editor(histedit_cmds, path, commits, repo);
- if (err) {
- if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
- err->code != GOT_ERR_HISTEDIT_CMD)
- goto done;
- err = histedit_edit_list_retry(histedit_cmds, err,
- commits, path, branch_name, repo);
- }
- }
-done:
- if (f && fclose(f) == EOF && err == NULL)
- err = got_error_from_errno("fclose");
- if (path && unlink(path) != 0 && err == NULL)
- err = got_error_from_errno2("unlink", path);
- free(path);
- return err;
-}
-
-static const struct got_error *
-histedit_save_list(struct got_histedit_list *histedit_cmds,
- struct got_worktree *worktree, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- char *path = NULL;
- FILE *f = NULL;
- struct got_histedit_list_entry *hle;
- struct got_commit_object *commit = NULL;
-
- err = got_worktree_get_histedit_script_path(&path, worktree);
- if (err)
- return err;
-
- f = fopen(path, "we");
- if (f == NULL) {
- err = got_error_from_errno2("fopen", path);
- goto done;
- }
- TAILQ_FOREACH(hle, histedit_cmds, entry) {
- err = histedit_write_commit(hle->commit_id, hle->cmd->name, f,
- repo);
- if (err)
- break;
-
- if (hle->logmsg) {
- int n = fprintf(f, "%c %s\n",
- GOT_HISTEDIT_MESG, hle->logmsg);
- if (n < 0) {
- err = got_ferror(f, GOT_ERR_IO);
- break;
- }
- }
- }
-done:
- if (f && fclose(f) == EOF && err == NULL)
- err = got_error_from_errno("fclose");
- free(path);
- if (commit)
- got_object_commit_close(commit);
- return err;
-}
-
-static void
-histedit_free_list(struct got_histedit_list *histedit_cmds)
-{
- struct got_histedit_list_entry *hle;
-
- while ((hle = TAILQ_FIRST(histedit_cmds))) {
- TAILQ_REMOVE(histedit_cmds, hle, entry);
- free(hle);
- }
-}
-
-static const struct got_error *
-histedit_load_list(struct got_histedit_list *histedit_cmds,
- const char *path, struct got_repository *repo)
-{
- const struct got_error *err = NULL;
- FILE *f = NULL;
-
- f = fopen(path, "re");
- if (f == NULL) {
- err = got_error_from_errno2("fopen", path);
- goto done;
- }
-
- err = histedit_parse_list(histedit_cmds, f, repo);
-done:
- if (f && fclose(f) == EOF && err == NULL)
- err = got_error_from_errno("fclose");
- return err;
-}
-
-static const struct got_error *
-histedit_edit_list_retry(struct got_histedit_list *histedit_cmds,
- const struct got_error *edit_err, struct got_object_id_queue *commits,
- const char *path, const char *branch_name, struct got_repository *repo)
-{
- const struct got_error *err = NULL, *prev_err = edit_err;
- int resp = ' ';
-
- while (resp != 'c' && resp != 'r' && resp != 'a') {
- printf("%s: %s\n(c)ontinue editing, (r)estart editing, "
- "or (a)bort: ", getprogname(), prev_err->msg);
- resp = getchar();
- if (resp == '\n')
- resp = getchar();
- if (resp == 'c') {
- histedit_free_list(histedit_cmds);
- err = histedit_run_editor(histedit_cmds, path, commits,
- repo);
- if (err) {
- if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
- err->code != GOT_ERR_HISTEDIT_CMD)
- break;
- prev_err = err;
- resp = ' ';
- continue;
- }
- break;
- } else if (resp == 'r') {
- histedit_free_list(histedit_cmds);
- err = histedit_edit_script(histedit_cmds,
- commits, branch_name, 0, 0, 0, 0, repo);
- if (err) {
- if (err->code != GOT_ERR_HISTEDIT_SYNTAX &&
- err->code != GOT_ERR_HISTEDIT_CMD)
- break;
- prev_err = err;
- resp = ' ';
- continue;
- }
- break;
- } else if (resp == 'a') {
- err = got_error(GOT_ERR_HISTEDIT_CANCEL);
- break;
- } else
- printf("invalid response '%c'\n", resp);
- }
-
- return err;
-}
-
-static const struct got_error *
-histedit_complete(struct got_worktree *worktree,
- struct got_fileindex *fileindex, struct got_reference *tmp_branch,
- struct got_reference *branch, struct got_repository *repo)
-{
- printf("Switching work tree to %s\n",
- got_ref_get_symref_target(branch));
- return got_worktree_histedit_complete(worktree, fileindex, tmp_branch,
- branch, repo);
-}
-
-static const struct got_error *
-show_histedit_progress(struct got_commit_object *commit,
- struct got_histedit_list_entry *hle, struct got_object_id *new_id)
-{
- const struct got_error *err;
- char *old_id_str = NULL, *new_id_str = NULL, *logmsg = NULL;
-
- err = got_object_id_str(&old_id_str, hle->commit_id);
- if (err)
- goto done;
-
- if (new_id) {
- err = got_object_id_str(&new_id_str, new_id);
- if (err)
- goto done;
- }
-
- old_id_str[12] = '\0';
- if (new_id_str)
- new_id_str[12] = '\0';
-
- if (hle->logmsg) {
- logmsg = strdup(hle->logmsg);
- if (logmsg == NULL) {
- err = got_error_from_errno("strdup");
- goto done;
- }
- trim_logmsg(logmsg, 42);
- } else {
- err = get_short_logmsg(&logmsg, 42, commit);
- if (err)
- goto done;
- }
-
- switch (hle->cmd->code) {
- case GOT_HISTEDIT_PICK:
- case GOT_HISTEDIT_EDIT:
- printf("%s -> %s: %s\n", old_id_str,
- new_id_str ? new_id_str : "no-op change", logmsg);
- break;
- case GOT_HISTEDIT_DROP:
- case GOT_HISTEDIT_FOLD:
- printf("%s -> %s commit: %s\n", old_id_str, hle->cmd->name,
- logmsg);
- break;
- default:
- break;
- }
-done:
- free(old_id_str);
- free(new_id_str);
- return err;
-}
-
-static const struct got_error *
-histedit_commit(struct got_pathlist_head *merged_paths,
- struct got_worktree *worktree, struct got_fileindex *fileindex,
- struct got_reference *tmp_branch, struct got_histedit_list_entry *hle,
- const char *committer, int allow_conflict, struct got_repository *repo)
-{
- const struct got_error *err;
- struct got_commit_object *commit;
- struct got_object_id *new_commit_id;
-
- if ((hle->cmd->code == GOT_HISTEDIT_EDIT || get_folded_commits(hle))
- && hle->logmsg == NULL) {
- err = histedit_edit_logmsg(hle, repo);
- if (err)
- return err;
- }
-
- err = got_object_open_as_commit(&commit, repo, hle->commit_id);
- if (err)
- return err;
-
- err = got_worktree_histedit_commit(&new_commit_id, merged_paths,
- worktree, fileindex, tmp_branch, committer, commit, hle->commit_id,
- hle->logmsg, allow_conflict, repo);
- if (err) {
- if (err->code != GOT_ERR_COMMIT_NO_CHANGES)
- goto done;
- err = show_histedit_progress(commit, hle, NULL);
- } else {
- err = show_histedit_progress(commit, hle, new_commit_id);
- free(new_commit_id);
- }
-done:
- got_object_commit_close(commit);
- return err;
-}
-
-static const struct got_error *
-histedit_skip_commit(struct got_histedit_list_entry *hle,
- struct got_worktree *worktree, struct got_repository *repo)
-{
- const struct got_error *error;
- struct got_commit_object *commit;
-
- error = got_worktree_histedit_skip_commit(worktree, hle->commit_id,
- repo);
- if (error)
- return error;
-
- error = got_object_open_as_commit(&commit, repo, hle->commit_id);
- if (error)
- return error;
-
- error = show_histedit_progress(commit, hle, NULL);
- got_object_commit_close(commit);
- return error;
-}
-
-static const struct got_error *
-check_local_changes(void *arg, unsigned char status,
- unsigned char staged_status, const char *path,
- struct got_object_id *blob_id, struct got_object_id *staged_blob_id,
- struct got_object_id *commit_id, int dirfd, const char *de_name)
-{
- int *have_local_changes = arg;
-
- switch (status) {
- case GOT_STATUS_ADD:
- case GOT_STATUS_DELETE:
- case GOT_STATUS_MODIFY:
- case GOT_STATUS_CONFLICT:
- *have_local_changes = 1;
- return got_error(GOT_ERR_CANCELLED);
- default:
- break;
- }
-
- switch (staged_status) {
- case GOT_STATUS_ADD:
- case GOT_STATUS_DELETE:
- case GOT_STATUS_MODIFY:
- *have_local_changes = 1;
- return got_error(GOT_ERR_CANCELLED);
- default:
- break;
- }
-
- return NULL;
-}
-
-static const struct got_error *
-cmd_histedit(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_worktree *worktree = NULL;
- struct got_fileindex *fileindex = NULL;
- struct got_repository *repo = NULL;
- char *cwd = NULL, *committer = NULL, *gitconfig_path = NULL;
- struct got_reference *branch = NULL;
- struct got_reference *tmp_branch = NULL;
- struct got_object_id *resume_commit_id = NULL;
- struct got_object_id *base_commit_id = NULL;
- struct got_object_id *head_commit_id = NULL;
- struct got_commit_object *commit = NULL;
- int ch, rebase_in_progress = 0, merge_in_progress = 0;
- struct got_update_progress_arg upa;
- int edit_in_progress = 0, abort_edit = 0, continue_edit = 0;
- int drop_only = 0, edit_logmsg_only = 0, fold_only = 0, edit_only = 0;
- int allow_conflict = 0, list_backups = 0, delete_backups = 0;
- const char *edit_script_path = NULL;
- struct got_object_id_queue commits;
- struct got_pathlist_head merged_paths;
- const struct got_object_id_queue *parent_ids;
- struct got_object_qid *pid;
- struct got_histedit_list histedit_cmds;
- struct got_histedit_list_entry *hle;
- int *pack_fds = NULL;
-
- STAILQ_INIT(&commits);
- TAILQ_INIT(&histedit_cmds);
- TAILQ_INIT(&merged_paths);
- memset(&upa, 0, sizeof(upa));
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "aCcdeF:flmX")) != -1) {
- switch (ch) {
- case 'a':
- abort_edit = 1;
- break;
- case 'C':
- allow_conflict = 1;
- break;
- case 'c':
- continue_edit = 1;
- break;
- case 'd':
- drop_only = 1;
- break;
- case 'e':
- edit_only = 1;
- break;
- case 'F':
- edit_script_path = optarg;
- break;
- case 'f':
- fold_only = 1;
- break;
- case 'l':
- list_backups = 1;
- break;
- case 'm':
- edit_logmsg_only = 1;
- break;
- case 'X':
- delete_backups = 1;
- break;
- default:
- usage_histedit();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (abort_edit && allow_conflict)
- option_conflict('a', 'C');
- if (abort_edit && continue_edit)
- option_conflict('a', 'c');
- if (edit_script_path && allow_conflict)
- option_conflict('F', 'C');
- if (edit_script_path && edit_logmsg_only)
- option_conflict('F', 'm');
- if (abort_edit && edit_logmsg_only)
- option_conflict('a', 'm');
- if (edit_logmsg_only && allow_conflict)
- option_conflict('m', 'C');
- if (continue_edit && edit_logmsg_only)
- option_conflict('c', 'm');
- if (abort_edit && fold_only)
- option_conflict('a', 'f');
- if (fold_only && allow_conflict)
- option_conflict('f', 'C');
- if (continue_edit && fold_only)
- option_conflict('c', 'f');
- if (fold_only && edit_logmsg_only)
- option_conflict('f', 'm');
- if (edit_script_path && fold_only)
- option_conflict('F', 'f');
- if (abort_edit && edit_only)
- option_conflict('a', 'e');
- if (continue_edit && edit_only)
- option_conflict('c', 'e');
- if (edit_only && edit_logmsg_only)
- option_conflict('e', 'm');
- if (edit_script_path && edit_only)
- option_conflict('F', 'e');
- if (fold_only && edit_only)
- option_conflict('f', 'e');
- if (drop_only && abort_edit)
- option_conflict('d', 'a');
- if (drop_only && allow_conflict)
- option_conflict('d', 'C');
- if (drop_only && continue_edit)
- option_conflict('d', 'c');
- if (drop_only && edit_logmsg_only)
- option_conflict('d', 'm');
- if (drop_only && edit_only)
- option_conflict('d', 'e');
- if (drop_only && edit_script_path)
- option_conflict('d', 'F');
- if (drop_only && fold_only)
- option_conflict('d', 'f');
- if (list_backups) {
- if (abort_edit)
- option_conflict('l', 'a');
- if (allow_conflict)
- option_conflict('l', 'C');
- if (continue_edit)
- option_conflict('l', 'c');
- if (edit_script_path)
- option_conflict('l', 'F');
- if (edit_logmsg_only)
- option_conflict('l', 'm');
- if (drop_only)
- option_conflict('l', 'd');
- if (fold_only)
- option_conflict('l', 'f');
- if (edit_only)
- option_conflict('l', 'e');
- if (delete_backups)
- option_conflict('l', 'X');
- if (argc != 0 && argc != 1)
- usage_histedit();
- } else if (delete_backups) {
- if (abort_edit)
- option_conflict('X', 'a');
- if (allow_conflict)
- option_conflict('X', 'C');
- if (continue_edit)
- option_conflict('X', 'c');
- if (drop_only)
- option_conflict('X', 'd');
- if (edit_script_path)
- option_conflict('X', 'F');
- if (edit_logmsg_only)
- option_conflict('X', 'm');
- if (fold_only)
- option_conflict('X', 'f');
- if (edit_only)
- option_conflict('X', 'e');
- if (list_backups)
- option_conflict('X', 'l');
- if (argc != 0 && argc != 1)
- usage_histedit();
- } else if (allow_conflict && !continue_edit)
- errx(1, "-C option requires -c");
- else if (argc != 0)
- usage_histedit();
-
- /*
- * This command cannot apply unveil(2) in all cases because the
- * user may choose to run an editor to edit the histedit script
- * and to edit individual commit log messages.
- * unveil(2) traverses exec(2); if an editor is used we have to
- * apply unveil after edit script and log messages have been written.
- * XXX TODO: Make use of unveil(2) where possible.
- */
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (list_backups || delete_backups) {
- if (error->code != GOT_ERR_NOT_WORKTREE)
- goto done;
- } else {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error,
- "histedit", cwd);
- goto done;
- }
- }
-
- if (list_backups || delete_backups) {
- error = got_repo_open(&repo,
- worktree ? got_worktree_get_repo_path(worktree) : cwd,
- NULL, pack_fds);
- if (error != NULL)
- goto done;
- error = apply_unveil(got_repo_get_path(repo), 0,
- worktree ? got_worktree_get_root_path(worktree) : NULL);
- if (error)
- goto done;
- error = process_backup_refs(
- GOT_WORKTREE_HISTEDIT_BACKUP_REF_PREFIX,
- argc == 1 ? argv[0] : NULL, delete_backups, repo);
- goto done; /* nothing else to do */
- }
-
- error = get_gitconfig_path(&gitconfig_path);
- if (error)
- goto done;
- error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
- gitconfig_path, pack_fds);
- if (error != NULL)
- goto done;
-
- if (worktree != NULL && !list_backups && !delete_backups) {
- error = worktree_has_logmsg_ref("histedit", worktree, repo);
- if (error)
- goto done;
- }
-
- error = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
- if (error)
- goto done;
- if (rebase_in_progress) {
- error = got_error(GOT_ERR_REBASING);
- goto done;
- }
-
- error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
- repo);
- if (error)
- goto done;
- if (merge_in_progress) {
- error = got_error(GOT_ERR_MERGE_BUSY);
- goto done;
- }
-
- error = got_worktree_histedit_in_progress(&edit_in_progress, worktree);
- if (error)
- goto done;
-
- if (edit_in_progress && edit_logmsg_only) {
- error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
- "histedit operation is in progress in this "
- "work tree and must be continued or aborted "
- "before the -m option can be used");
- goto done;
- }
- if (edit_in_progress && drop_only) {
- error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
- "histedit operation is in progress in this "
- "work tree and must be continued or aborted "
- "before the -d option can be used");
- goto done;
- }
- if (edit_in_progress && fold_only) {
- error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
- "histedit operation is in progress in this "
- "work tree and must be continued or aborted "
- "before the -f option can be used");
- goto done;
- }
- if (edit_in_progress && edit_only) {
- error = got_error_msg(GOT_ERR_HISTEDIT_BUSY,
- "histedit operation is in progress in this "
- "work tree and must be continued or aborted "
- "before the -e option can be used");
- goto done;
- }
-
- if (edit_in_progress && abort_edit) {
- error = got_worktree_histedit_continue(&resume_commit_id,
- &tmp_branch, &branch, &base_commit_id, &fileindex,
- worktree, repo);
- if (error)
- goto done;
- printf("Switching work tree to %s\n",
- got_ref_get_symref_target(branch));
- error = got_worktree_histedit_abort(worktree, fileindex, repo,
- branch, base_commit_id, abort_progress, &upa);
- if (error)
- goto done;
- printf("Histedit of %s aborted\n",
- got_ref_get_symref_target(branch));
- print_merge_progress_stats(&upa);
- goto done; /* nothing else to do */
- } else if (abort_edit) {
- error = got_error(GOT_ERR_NOT_HISTEDIT);
- goto done;
- }
-
- error = get_author(&committer, repo, worktree);
- if (error)
- goto done;
-
- if (continue_edit) {
- char *path;
-
- if (!edit_in_progress) {
- error = got_error(GOT_ERR_NOT_HISTEDIT);
- goto done;
- }
-
- error = got_worktree_get_histedit_script_path(&path, worktree);
- if (error)
- goto done;
-
- error = histedit_load_list(&histedit_cmds, path, repo);
- free(path);
- if (error)
- goto done;
-
- error = got_worktree_histedit_continue(&resume_commit_id,
- &tmp_branch, &branch, &base_commit_id, &fileindex,
- worktree, repo);
- if (error)
- goto done;
-
- error = got_ref_resolve(&head_commit_id, repo, branch);
- if (error)
- goto done;
-
- error = got_object_open_as_commit(&commit, repo,
- head_commit_id);
- if (error)
- goto done;
- parent_ids = got_object_commit_get_parent_ids(commit);
- pid = STAILQ_FIRST(parent_ids);
- if (pid == NULL) {
- error = got_error(GOT_ERR_EMPTY_HISTEDIT);
- goto done;
- }
- error = collect_commits(&commits, head_commit_id, &pid->id,
- base_commit_id, got_worktree_get_path_prefix(worktree),
- GOT_ERR_HISTEDIT_PATH, repo);
- got_object_commit_close(commit);
- commit = NULL;
- if (error)
- goto done;
- } else {
- if (edit_in_progress) {
- error = got_error(GOT_ERR_HISTEDIT_BUSY);
- goto done;
- }
-
- error = got_ref_open(&branch, repo,
- got_worktree_get_head_ref_name(worktree), 0);
- if (error != NULL)
- goto done;
-
- if (strncmp(got_ref_get_name(branch), "refs/heads/", 11) != 0) {
- error = got_error_msg(GOT_ERR_COMMIT_BRANCH,
- "will not edit commit history of a branch outside "
- "the \"refs/heads/\" reference namespace");
- goto done;
- }
-
- error = got_ref_resolve(&head_commit_id, repo, branch);
- got_ref_close(branch);
- branch = NULL;
- if (error)
- goto done;
-
- error = got_object_open_as_commit(&commit, repo,
- head_commit_id);
- if (error)
- goto done;
- parent_ids = got_object_commit_get_parent_ids(commit);
- pid = STAILQ_FIRST(parent_ids);
- if (pid == NULL) {
- error = got_error(GOT_ERR_EMPTY_HISTEDIT);
- goto done;
- }
- error = collect_commits(&commits, head_commit_id, &pid->id,
- got_worktree_get_base_commit_id(worktree),
- got_worktree_get_path_prefix(worktree),
- GOT_ERR_HISTEDIT_PATH, repo);
- got_object_commit_close(commit);
- commit = NULL;
- if (error)
- goto done;
-
- if (STAILQ_EMPTY(&commits)) {
- error = got_error(GOT_ERR_EMPTY_HISTEDIT);
- goto done;
- }
-
- error = got_worktree_histedit_prepare(&tmp_branch, &branch,
- &base_commit_id, &fileindex, worktree, repo);
- if (error)
- goto done;
-
- if (edit_script_path) {
- error = histedit_load_list(&histedit_cmds,
- edit_script_path, repo);
- if (error) {
- got_worktree_histedit_abort(worktree, fileindex,
- repo, branch, base_commit_id,
- abort_progress, &upa);
- print_merge_progress_stats(&upa);
- goto done;
- }
- } else {
- const char *branch_name;
- branch_name = got_ref_get_symref_target(branch);
- if (strncmp(branch_name, "refs/heads/", 11) == 0)
- branch_name += 11;
- error = histedit_edit_script(&histedit_cmds, &commits,
- branch_name, edit_logmsg_only, fold_only,
- drop_only, edit_only, repo);
- if (error) {
- got_worktree_histedit_abort(worktree, fileindex,
- repo, branch, base_commit_id,
- abort_progress, &upa);
- print_merge_progress_stats(&upa);
- goto done;
- }
-
- }
-
- error = histedit_save_list(&histedit_cmds, worktree,
- repo);
- if (error) {
- got_worktree_histedit_abort(worktree, fileindex,
- repo, branch, base_commit_id,
- abort_progress, &upa);
- print_merge_progress_stats(&upa);
- goto done;
- }
-
- }
-
- error = histedit_check_script(&histedit_cmds, &commits, repo);
- if (error)
- goto done;
-
- TAILQ_FOREACH(hle, &histedit_cmds, entry) {
- if (resume_commit_id) {
- if (got_object_id_cmp(hle->commit_id,
- resume_commit_id) != 0)
- continue;
-
- resume_commit_id = NULL;
- if (hle->cmd->code == GOT_HISTEDIT_DROP ||
- hle->cmd->code == GOT_HISTEDIT_FOLD) {
- error = histedit_skip_commit(hle, worktree,
- repo);
- if (error)
- goto done;
- } else {
- struct got_pathlist_head paths;
- int have_changes = 0;
-
- TAILQ_INIT(&paths);
- error = got_pathlist_append(&paths, "", NULL);
- if (error)
- goto done;
- error = got_worktree_status(worktree, &paths,
- repo, 0, check_local_changes, &have_changes,
- check_cancelled, NULL);
- got_pathlist_free(&paths,
- GOT_PATHLIST_FREE_NONE);
- if (error) {
- if (error->code != GOT_ERR_CANCELLED)
- goto done;
- if (sigint_received || sigpipe_received)
- goto done;
- }
- if (have_changes) {
- error = histedit_commit(NULL, worktree,
- fileindex, tmp_branch, hle,
- committer, allow_conflict, repo);
- if (error)
- goto done;
- } else {
- error = got_object_open_as_commit(
- &commit, repo, hle->commit_id);
- if (error)
- goto done;
- error = show_histedit_progress(commit,
- hle, NULL);
- got_object_commit_close(commit);
- commit = NULL;
- if (error)
- goto done;
- }
- }
- continue;
- }
-
- if (hle->cmd->code == GOT_HISTEDIT_DROP) {
- error = histedit_skip_commit(hle, worktree, repo);
- if (error)
- goto done;
- continue;
- }
-
- error = got_object_open_as_commit(&commit, repo,
- hle->commit_id);
- if (error)
- goto done;
- parent_ids = got_object_commit_get_parent_ids(commit);
- pid = STAILQ_FIRST(parent_ids);
-
- error = got_worktree_histedit_merge_files(&merged_paths,
- worktree, fileindex, &pid->id, hle->commit_id, repo,
- update_progress, &upa, check_cancelled, NULL);
- if (error)
- goto done;
- got_object_commit_close(commit);
- commit = NULL;
-
- print_merge_progress_stats(&upa);
- if (upa.conflicts > 0 || upa.missing > 0 ||
- upa.not_deleted > 0 || upa.unversioned > 0) {
- if (upa.conflicts > 0) {
- error = show_rebase_merge_conflict(
- hle->commit_id, repo);
- if (error)
- goto done;
- }
- got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
- break;
- }
-
- if (hle->cmd->code == GOT_HISTEDIT_EDIT) {
- char *id_str;
- error = got_object_id_str(&id_str, hle->commit_id);
- if (error)
- goto done;
- printf("Stopping histedit for amending commit %s\n",
- id_str);
- free(id_str);
- got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
- error = got_worktree_histedit_postpone(worktree,
- fileindex);
- goto done;
- }
-
- if (hle->cmd->code == GOT_HISTEDIT_FOLD) {
- error = histedit_skip_commit(hle, worktree, repo);
- if (error)
- goto done;
- continue;
- }
-
- error = histedit_commit(&merged_paths, worktree, fileindex,
- tmp_branch, hle, committer, allow_conflict, repo);
- got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH);
- if (error)
- goto done;
- }
-
- if (upa.conflicts > 0 || upa.missing > 0 ||
- upa.not_deleted > 0 || upa.unversioned > 0) {
- error = got_worktree_histedit_postpone(worktree, fileindex);
- if (error)
- goto done;
- if (upa.conflicts > 0 && upa.missing == 0 &&
- upa.not_deleted == 0 && upa.unversioned == 0) {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "conflicts must be resolved before histedit "
- "can continue");
- } else if (upa.conflicts > 0) {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "conflicts must be resolved before histedit "
- "can continue; changes destined for some "
- "files were not yet merged and should be "
- "merged manually if required before the "
- "histedit operation is continued");
- } else {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "changes destined for some files were not "
- "yet merged and should be merged manually "
- "if required before the histedit operation "
- "is continued");
- }
- } else
- error = histedit_complete(worktree, fileindex, tmp_branch,
- branch, repo);
-done:
- free(cwd);
- free(committer);
- free(gitconfig_path);
- got_object_id_queue_free(&commits);
- histedit_free_list(&histedit_cmds);
- free(head_commit_id);
- free(base_commit_id);
- free(resume_commit_id);
- if (commit)
- got_object_commit_close(commit);
- if (branch)
- got_ref_close(branch);
- if (tmp_branch)
- got_ref_close(tmp_branch);
- if (worktree)
- got_worktree_close(worktree);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- return error;
-}
-
-__dead static void
-usage_integrate(void)
-{
- fprintf(stderr, "usage: %s integrate branch\n", getprogname());
- exit(1);
-}
-
-static const struct got_error *
-cmd_integrate(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_repository *repo = NULL;
- struct got_worktree *worktree = NULL;
- char *cwd = NULL, *refname = NULL, *base_refname = NULL;
- const char *branch_arg = NULL;
- struct got_reference *branch_ref = NULL, *base_branch_ref = NULL;
- struct got_fileindex *fileindex = NULL;
- struct got_object_id *commit_id = NULL, *base_commit_id = NULL;
- int ch;
- struct got_update_progress_arg upa;
- int *pack_fds = NULL;
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "")) != -1) {
- switch (ch) {
- default:
- usage_integrate();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc != 1)
- usage_integrate();
- branch_arg = argv[0];
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error, "integrate",
- cwd);
- goto done;
- }
-
- error = check_rebase_or_histedit_in_progress(worktree);
- if (error)
- goto done;
-
- error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
- NULL, pack_fds);
- if (error != NULL)
- goto done;
-
- error = apply_unveil(got_repo_get_path(repo), 0,
- got_worktree_get_root_path(worktree));
- if (error)
- goto done;
-
- error = check_merge_in_progress(worktree, repo);
- if (error)
- goto done;
-
- if (asprintf(&refname, "refs/heads/%s", branch_arg) == -1) {
- error = got_error_from_errno("asprintf");
- goto done;
- }
-
- error = got_worktree_integrate_prepare(&fileindex, &branch_ref,
- &base_branch_ref, worktree, refname, repo);
- if (error)
- goto done;
-
- refname = strdup(got_ref_get_name(branch_ref));
- if (refname == NULL) {
- error = got_error_from_errno("strdup");
- got_worktree_integrate_abort(worktree, fileindex, repo,
- branch_ref, base_branch_ref);
- goto done;
- }
- base_refname = strdup(got_ref_get_name(base_branch_ref));
- if (base_refname == NULL) {
- error = got_error_from_errno("strdup");
- got_worktree_integrate_abort(worktree, fileindex, repo,
- branch_ref, base_branch_ref);
- goto done;
- }
- if (strncmp(base_refname, "refs/heads/", 11) != 0) {
- error = got_error(GOT_ERR_INTEGRATE_BRANCH);
- got_worktree_integrate_abort(worktree, fileindex, repo,
- branch_ref, base_branch_ref);
- goto done;
- }
-
- error = got_ref_resolve(&commit_id, repo, branch_ref);
- if (error)
- goto done;
-
- error = got_ref_resolve(&base_commit_id, repo, base_branch_ref);
- if (error)
- goto done;
-
- if (got_object_id_cmp(commit_id, base_commit_id) == 0) {
- error = got_error_msg(GOT_ERR_SAME_BRANCH,
- "specified branch has already been integrated");
- got_worktree_integrate_abort(worktree, fileindex, repo,
- branch_ref, base_branch_ref);
- goto done;
- }
-
- error = check_linear_ancestry(commit_id, base_commit_id, 1, repo);
- if (error) {
- if (error->code == GOT_ERR_ANCESTRY)
- error = got_error(GOT_ERR_REBASE_REQUIRED);
- got_worktree_integrate_abort(worktree, fileindex, repo,
- branch_ref, base_branch_ref);
- goto done;
- }
-
- memset(&upa, 0, sizeof(upa));
- error = got_worktree_integrate_continue(worktree, fileindex, repo,
- branch_ref, base_branch_ref, update_progress, &upa,
- check_cancelled, NULL);
- if (error)
- goto done;
-
- printf("Integrated %s into %s\n", refname, base_refname);
- print_update_progress_stats(&upa);
-done:
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (worktree)
- got_worktree_close(worktree);
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- free(cwd);
- free(base_commit_id);
- free(commit_id);
- free(refname);
- free(base_refname);
- return error;
-}
-
-__dead static void
-usage_merge(void)
-{
- fprintf(stderr, "usage: %s merge [-aCcn] [branch]\n", getprogname());
- exit(1);
-}
-
-static const struct got_error *
-cmd_merge(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_worktree *worktree = NULL;
- struct got_repository *repo = NULL;
- struct got_fileindex *fileindex = NULL;
- char *cwd = NULL, *id_str = NULL, *author = NULL;
- char *gitconfig_path = NULL;
- struct got_reference *branch = NULL, *wt_branch = NULL;
- struct got_object_id *branch_tip = NULL, *yca_id = NULL;
- struct got_object_id *wt_branch_tip = NULL;
- int ch, merge_in_progress = 0, abort_merge = 0, continue_merge = 0;
- int allow_conflict = 0, prefer_fast_forward = 1, interrupt_merge = 0;
- struct got_update_progress_arg upa;
- struct got_object_id *merge_commit_id = NULL;
- char *branch_name = NULL;
- int *pack_fds = NULL;
-
- memset(&upa, 0, sizeof(upa));
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "aCcMn")) != -1) {
- switch (ch) {
- case 'a':
- abort_merge = 1;
- break;
- case 'C':
- allow_conflict = 1;
- break;
- case 'c':
- continue_merge = 1;
- break;
- case 'M':
- prefer_fast_forward = 0;
- break;
- case 'n':
- interrupt_merge = 1;
- break;
- default:
- usage_merge();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (abort_merge) {
- if (continue_merge)
- option_conflict('a', 'c');
- if (!prefer_fast_forward)
- option_conflict('a', 'M');
- if (interrupt_merge)
- option_conflict('a', 'n');
- } else if (continue_merge) {
- if (!prefer_fast_forward)
- option_conflict('c', 'M');
- if (interrupt_merge)
- option_conflict('c', 'n');
- }
- if (allow_conflict) {
- if (!continue_merge)
- errx(1, "-C option requires -c");
- }
- if (abort_merge || continue_merge) {
- if (argc != 0)
- usage_merge();
- } else if (argc != 1)
- usage_merge();
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error,
- "merge", cwd);
- goto done;
- }
-
- error = get_gitconfig_path(&gitconfig_path);
- if (error)
- goto done;
- error = got_repo_open(&repo,
- worktree ? got_worktree_get_repo_path(worktree) : cwd,
- gitconfig_path, pack_fds);
- if (error != NULL)
- goto done;
-
- if (worktree != NULL) {
- error = worktree_has_logmsg_ref("merge", worktree, repo);
- if (error)
- goto done;
- }
-
- error = apply_unveil(got_repo_get_path(repo), 0,
- worktree ? got_worktree_get_root_path(worktree) : NULL);
- if (error)
- goto done;
-
- error = check_rebase_or_histedit_in_progress(worktree);
- if (error)
- goto done;
-
- error = got_worktree_merge_in_progress(&merge_in_progress, worktree,
- repo);
- if (error)
- goto done;
-
- if (merge_in_progress && !(abort_merge || continue_merge)) {
- error = got_error(GOT_ERR_MERGE_BUSY);
- goto done;
- }
-
- if (!merge_in_progress && (abort_merge || continue_merge)) {
- error = got_error(GOT_ERR_NOT_MERGING);
- goto done;
- }
-
- if (abort_merge) {
- error = got_worktree_merge_continue(&branch_name,
- &branch_tip, &fileindex, worktree, repo);
- if (error)
- goto done;
- error = got_worktree_merge_abort(worktree, fileindex, repo,
- abort_progress, &upa);
- if (error)
- goto done;
- printf("Merge of %s aborted\n", branch_name);
- goto done; /* nothing else to do */
- }
-
- if (strncmp(got_worktree_get_head_ref_name(worktree),
- "refs/heads/", 11) != 0) {
- error = got_error_fmt(GOT_ERR_COMMIT_BRANCH,
- "work tree's current branch %s is outside the "
- "\"refs/heads/\" reference namespace; "
- "update -b required",
- got_worktree_get_head_ref_name(worktree));
- goto done;
- }
-
- error = get_author(&author, repo, worktree);
- if (error)
- goto done;
-
- error = got_ref_open(&wt_branch, repo,
- got_worktree_get_head_ref_name(worktree), 0);
- if (error)
- goto done;
- error = got_ref_resolve(&wt_branch_tip, repo, wt_branch);
- if (error)
- goto done;
-
- if (continue_merge) {
- struct got_object_id *base_commit_id;
- base_commit_id = got_worktree_get_base_commit_id(worktree);
- if (got_object_id_cmp(wt_branch_tip, base_commit_id) != 0) {
- error = got_error(GOT_ERR_MERGE_COMMIT_OUT_OF_DATE);
- goto done;
- }
- error = got_worktree_merge_continue(&branch_name,
- &branch_tip, &fileindex, worktree, repo);
- if (error)
- goto done;
- } else {
- error = got_ref_open(&branch, repo, argv[0], 0);
- if (error != NULL)
- goto done;
- branch_name = strdup(got_ref_get_name(branch));
- if (branch_name == NULL) {
- error = got_error_from_errno("strdup");
- goto done;
- }
- error = got_ref_resolve(&branch_tip, repo, branch);
- if (error)
- goto done;
- }
-
- error = got_commit_graph_find_youngest_common_ancestor(&yca_id,
- wt_branch_tip, branch_tip, 0, repo,
- check_cancelled, NULL);
- if (error && error->code != GOT_ERR_ANCESTRY)
- goto done;
-
- if (!continue_merge) {
- error = check_path_prefix(wt_branch_tip, branch_tip,
- got_worktree_get_path_prefix(worktree),
- GOT_ERR_MERGE_PATH, repo);
- if (error)
- goto done;
- error = got_worktree_merge_prepare(&fileindex, worktree, repo);
- if (error)
- goto done;
- if (prefer_fast_forward && yca_id &&
- got_object_id_cmp(wt_branch_tip, yca_id) == 0) {
- struct got_pathlist_head paths;
- if (interrupt_merge) {
- error = got_error_fmt(GOT_ERR_BAD_OPTION,
- "there are no changes to merge since %s "
- "is already based on %s; merge cannot be "
- "interrupted for amending; -n",
- branch_name, got_ref_get_name(wt_branch));
- goto done;
- }
- printf("Forwarding %s to %s\n",
- got_ref_get_name(wt_branch), branch_name);
- error = got_ref_change_ref(wt_branch, branch_tip);
- if (error)
- goto done;
- error = got_ref_write(wt_branch, repo);
- if (error)
- goto done;
- error = got_worktree_set_base_commit_id(worktree, repo,
- branch_tip);
- if (error)
- goto done;
- TAILQ_INIT(&paths);
- error = got_pathlist_append(&paths, "", NULL);
- if (error)
- goto done;
- error = got_worktree_checkout_files(worktree,
- &paths, repo, update_progress, &upa,
- check_cancelled, NULL);
- got_pathlist_free(&paths, GOT_PATHLIST_FREE_NONE);
- if (error)
- goto done;
- if (upa.did_something) {
- char *id_str;
- error = got_object_id_str(&id_str, branch_tip);
- if (error)
- goto done;
- printf("Updated to commit %s\n", id_str);
- free(id_str);
- } else
- printf("Already up-to-date\n");
- print_update_progress_stats(&upa);
- goto done;
- }
- error = got_worktree_merge_write_refs(worktree, branch, repo);
- if (error)
- goto done;
-
- error = got_worktree_merge_branch(worktree, fileindex,
- yca_id, branch_tip, repo, update_progress, &upa,
- check_cancelled, NULL);
- if (error)
- goto done;
- print_merge_progress_stats(&upa);
- if (!upa.did_something) {
- error = got_worktree_merge_abort(worktree, fileindex,
- repo, abort_progress, &upa);
- if (error)
- goto done;
- printf("Already up-to-date\n");
- goto done;
- }
- }
-
- if (interrupt_merge) {
- error = got_worktree_merge_postpone(worktree, fileindex);
- if (error)
- goto done;
- printf("Merge of %s interrupted on request\n", branch_name);
- } else if (upa.conflicts > 0 || upa.missing > 0 ||
- upa.not_deleted > 0 || upa.unversioned > 0) {
- error = got_worktree_merge_postpone(worktree, fileindex);
- if (error)
- goto done;
- if (upa.conflicts > 0 && upa.missing == 0 &&
- upa.not_deleted == 0 && upa.unversioned == 0) {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "conflicts must be resolved before merging "
- "can continue");
- } else if (upa.conflicts > 0) {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "conflicts must be resolved before merging "
- "can continue; changes destined for some "
- "files were not yet merged and "
- "should be merged manually if required before the "
- "merge operation is continued");
- } else {
- error = got_error_msg(GOT_ERR_CONFLICTS,
- "changes destined for some "
- "files were not yet merged and should be "
- "merged manually if required before the "
- "merge operation is continued");
- }
- goto done;
- } else {
- error = got_worktree_merge_commit(&merge_commit_id, worktree,
- fileindex, author, NULL, 1, branch_tip, branch_name,
- allow_conflict, repo, continue_merge ? print_status : NULL,
- NULL);
- if (error)
- goto done;
- error = got_worktree_merge_complete(worktree, fileindex, repo);
- if (error)
- goto done;
- error = got_object_id_str(&id_str, merge_commit_id);
- if (error)
- goto done;
- printf("Merged %s into %s: %s\n", branch_name,
- got_worktree_get_head_ref_name(worktree),
- id_str);
-
- }
-done:
- free(gitconfig_path);
- free(id_str);
- free(merge_commit_id);
- free(author);
- free(branch_tip);
- free(branch_name);
- free(yca_id);
- if (branch)
- got_ref_close(branch);
- if (wt_branch)
- got_ref_close(wt_branch);
- if (worktree)
- got_worktree_close(worktree);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- return error;
-}
-
-__dead static void
-usage_stage(void)
-{
- fprintf(stderr, "usage: %s stage [-lpS] [-F response-script] "
- "[path ...]\n", getprogname());
- exit(1);
-}
-
-static const struct got_error *
-print_stage(void *arg, unsigned char status, unsigned char staged_status,
- const char *path, struct got_object_id *blob_id,
- struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
- int dirfd, const char *de_name)
-{
- const struct got_error *err = NULL;
- char *id_str = NULL;
-
- if (staged_status != GOT_STATUS_ADD &&
- staged_status != GOT_STATUS_MODIFY &&
- staged_status != GOT_STATUS_DELETE)
- return NULL;
-
- if (staged_status == GOT_STATUS_ADD ||
- staged_status == GOT_STATUS_MODIFY)
- err = got_object_id_str(&id_str, staged_blob_id);
- else
- err = got_object_id_str(&id_str, blob_id);
- if (err)
- return err;
-
- printf("%s %c %s\n", id_str, staged_status, path);
- free(id_str);
- return NULL;
-}
-
-static const struct got_error *
-cmd_stage(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_repository *repo = NULL;
- struct got_worktree *worktree = NULL;
- char *cwd = NULL;
- struct got_pathlist_head paths;
- int ch, list_stage = 0, pflag = 0, allow_bad_symlinks = 0;
- FILE *patch_script_file = NULL;
- const char *patch_script_path = NULL;
- struct choose_patch_arg cpa;
- int *pack_fds = NULL;
-
- TAILQ_INIT(&paths);
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "F:lpS")) != -1) {
- switch (ch) {
- case 'F':
- patch_script_path = optarg;
- break;
- case 'l':
- list_stage = 1;
- break;
- case 'p':
- pflag = 1;
- break;
- case 'S':
- allow_bad_symlinks = 1;
- break;
- default:
- usage_stage();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (list_stage && (pflag || patch_script_path))
- errx(1, "-l option cannot be used with other options");
- if (patch_script_path && !pflag)
- errx(1, "-F option can only be used together with -p option");
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error, "stage", cwd);
- goto done;
- }
-
- error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
- NULL, pack_fds);
- if (error != NULL)
- goto done;
-
- if (patch_script_path) {
- patch_script_file = fopen(patch_script_path, "re");
- if (patch_script_file == NULL) {
- error = got_error_from_errno2("fopen",
- patch_script_path);
- goto done;
- }
- }
- error = apply_unveil(got_repo_get_path(repo), 0,
- got_worktree_get_root_path(worktree));
- if (error)
- goto done;
-
- error = check_merge_in_progress(worktree, repo);
- if (error)
- goto done;
-
- error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
- if (error)
- goto done;
-
- if (list_stage)
- error = got_worktree_status(worktree, &paths, repo, 0,
- print_stage, NULL, check_cancelled, NULL);
- else {
- cpa.patch_script_file = patch_script_file;
- cpa.action = "stage";
- error = got_worktree_stage(worktree, &paths,
- pflag ? NULL : print_status, NULL,
- pflag ? choose_patch : NULL, &cpa,
- allow_bad_symlinks, repo);
- }
-done:
- if (patch_script_file && fclose(patch_script_file) == EOF &&
- error == NULL)
- error = got_error_from_errno2("fclose", patch_script_path);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (worktree)
- got_worktree_close(worktree);
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
- free(cwd);
- return error;
-}
-
-__dead static void
-usage_unstage(void)
-{
- fprintf(stderr, "usage: %s unstage [-p] [-F response-script] "
- "[path ...]\n", getprogname());
- exit(1);
-}
-
-
-static const struct got_error *
-cmd_unstage(int argc, char *argv[])
-{
- const struct got_error *error = NULL;
- struct got_repository *repo = NULL;
- struct got_worktree *worktree = NULL;
- char *cwd = NULL;
- struct got_pathlist_head paths;
- int ch, pflag = 0;
- struct got_update_progress_arg upa;
- FILE *patch_script_file = NULL;
- const char *patch_script_path = NULL;
- struct choose_patch_arg cpa;
- int *pack_fds = NULL;
-
- TAILQ_INIT(&paths);
-
-#ifndef PROFILE
- if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
- "unveil", NULL) == -1)
- err(1, "pledge");
-#endif
-
- while ((ch = getopt(argc, argv, "F:p")) != -1) {
- switch (ch) {
- case 'F':
- patch_script_path = optarg;
- break;
- case 'p':
- pflag = 1;
- break;
- default:
- usage_unstage();
- /* NOTREACHED */
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (patch_script_path && !pflag)
- errx(1, "-F option can only be used together with -p option");
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- error = got_error_from_errno("getcwd");
- goto done;
- }
-
- error = got_repo_pack_fds_open(&pack_fds);
- if (error != NULL)
- goto done;
-
- error = got_worktree_open(&worktree, cwd);
- if (error) {
- if (error->code == GOT_ERR_NOT_WORKTREE)
- error = wrap_not_worktree_error(error, "unstage", cwd);
- goto done;
- }
-
- error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
- NULL, pack_fds);
- if (error != NULL)
- goto done;
-
- if (patch_script_path) {
- patch_script_file = fopen(patch_script_path, "re");
- if (patch_script_file == NULL) {
- error = got_error_from_errno2("fopen",
- patch_script_path);
- goto done;
- }
- }
-
- error = apply_unveil(got_repo_get_path(repo), 0,
- got_worktree_get_root_path(worktree));
- if (error)
- goto done;
-
- error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
- if (error)
- goto done;
-
- cpa.patch_script_file = patch_script_file;
- cpa.action = "unstage";
- memset(&upa, 0, sizeof(upa));
- error = got_worktree_unstage(worktree, &paths, update_progress,
- &upa, pflag ? choose_patch : NULL, &cpa, repo);
- if (!error)
- print_merge_progress_stats(&upa);
-done:
- if (patch_script_file && fclose(patch_script_file) == EOF &&
- error == NULL)
- error = got_error_from_errno2("fclose", patch_script_path);
- if (repo) {
- const struct got_error *close_err = got_repo_close(repo);
- if (error == NULL)
- error = close_err;
- }
- if (worktree)
- got_worktree_close(worktree);
- if (pack_fds) {
- const struct got_error *pack_err =
- got_repo_pack_fds_close(pack_fds);
- if (error == NULL)
- error = pack_err;
- }
- got_pathlist_free(&paths, GOT_PATHLIST_FREE_PATH);
- free(cwd);
+ if (wanted_ref != NULL && !found)
+ err = got_error_fmt(GOT_ERR_NOT_REF, "%s", wanted_ref);
+
+done:
+ free(id);
+ free(uuidstr);
+ got_ref_list_free(&refs);
+ got_pathlist_free(&paths, GOT_PATHLIST_FREE_ALL);
+ if (refs_idmap)
+ got_reflist_object_id_map_free(refs_idmap);
+ if (commit)
+ got_object_commit_close(commit);
+ return err;
+}
+
+/*
+ * Create new temp "logmsg" ref of the backed-out or cherrypicked commit
+ * identified by id for log messages to prepopulate the editor on commit.
+ */
+static const struct got_error *
+logmsg_ref(struct got_object_id *id, const char *prefix,
+ struct got_worktree *worktree, struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ char *idstr, *ref = NULL, *refname = NULL;
+ int histedit_in_progress;
+ int rebase_in_progress, merge_in_progress;
+
+ /*
+ * Silenty refuse to create merge reference if any histedit, merge,
+ * or rebase operation is in progress.
+ */
+ err = got_worktree_histedit_in_progress(&histedit_in_progress,
+ worktree);
+ if (err)
+ return err;
+ if (histedit_in_progress)
+ return NULL;
+
+ err = got_worktree_rebase_in_progress(&rebase_in_progress, worktree);
+ if (err)
+ return err;
+ if (rebase_in_progress)
+ return NULL;
+
+ err = got_worktree_merge_in_progress(&merge_in_progress, worktree,
+ repo);
+ if (err)
+ return err;
+ if (merge_in_progress)
+ return NULL;
+
+ err = got_object_id_str(&idstr, id);
+ if (err)
+ return err;
+
+ err = got_worktree_get_logmsg_ref_name(&refname, worktree, prefix);
+ if (err)
+ goto done;
+
+ if (asprintf(&ref, "%s-%s", refname, idstr) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ err = create_ref(ref, got_worktree_get_base_commit_id(worktree),
+ -1, repo);
+done:
+ free(ref);
+ free(idstr);
+ free(refname);
+ return err;
+}
+
+__dead static void
+usage_cherrypick(void)
+{
+ fprintf(stderr, "usage: %s cherrypick [-lX] [commit-id]\n",
+ getprogname());
+ exit(1);
+}
+
+static const struct got_error *
+cmd_cherrypick(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ struct got_worktree *worktree = NULL;
+ struct got_repository *repo = NULL;
+ char *cwd = NULL, *commit_id_str = NULL;
+ struct got_object_id *commit_id = NULL;
+ struct got_commit_object *commit = NULL;
+ struct got_object_qid *pid;
+ int ch, list_refs = 0, remove_refs = 0;
+ struct got_update_progress_arg upa;
+ int *pack_fds = NULL;
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
+ "unveil", NULL) == -1)
+ err(1, "pledge");
+#endif
+
+ while ((ch = getopt(argc, argv, "lX")) != -1) {
+ switch (ch) {
+ case 'l':
+ list_refs = 1;
+ break;
+ case 'X':
+ remove_refs = 1;
+ break;
+ default:
+ usage_cherrypick();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (list_refs || remove_refs) {
+ if (argc != 0 && argc != 1)
+ usage_cherrypick();
+ } else if (argc != 1)
+ usage_cherrypick();
+ if (list_refs && remove_refs)
+ option_conflict('l', 'X');
+
+ cwd = getcwd(NULL, 0);
+ if (cwd == NULL) {
+ error = got_error_from_errno("getcwd");
+ goto done;
+ }
+
+ error = got_repo_pack_fds_open(&pack_fds);
+ if (error != NULL)
+ goto done;
+
+ error = got_worktree_open(&worktree, cwd);
+ if (error) {
+ if (list_refs || remove_refs) {
+ if (error->code != GOT_ERR_NOT_WORKTREE)
+ goto done;
+ } else {
+ if (error->code == GOT_ERR_NOT_WORKTREE)
+ error = wrap_not_worktree_error(error,
+ "cherrypick", cwd);
+ goto done;
+ }
+ }
+
+ error = got_repo_open(&repo,
+ worktree ? got_worktree_get_repo_path(worktree) : cwd,
+ NULL, pack_fds);
+ if (error != NULL)
+ goto done;
+
+ error = apply_unveil(got_repo_get_path(repo), 0,
+ worktree ? got_worktree_get_root_path(worktree) : NULL);
+ if (error)
+ goto done;
+
+ if (list_refs || remove_refs) {
+ error = process_logmsg_refs(GOT_WORKTREE_CHERRYPICK_REF_PREFIX,
+ GOT_WORKTREE_CHERRYPICK_REF_PREFIX_LEN,
+ argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
+ goto done;
+ }
+
+ error = got_repo_match_object_id(&commit_id, NULL, argv[0],
+ GOT_OBJ_TYPE_COMMIT, NULL, repo);
+ if (error)
+ goto done;
+ error = got_object_id_str(&commit_id_str, commit_id);
+ if (error)
+ goto done;
+
+ error = got_object_open_as_commit(&commit, repo, commit_id);
+ if (error)
+ goto done;
+ pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
+ memset(&upa, 0, sizeof(upa));
+ error = got_worktree_merge_files(worktree, pid ? &pid->id : NULL,
+ commit_id, repo, update_progress, &upa, check_cancelled,
+ NULL);
+ if (error != NULL)
+ goto done;
+
+ if (upa.did_something) {
+ error = logmsg_ref(commit_id,
+ GOT_WORKTREE_CHERRYPICK_REF_PREFIX, worktree, repo);
+ if (error)
+ goto done;
+ printf("Merged commit %s\n", commit_id_str);
+ }
+ print_merge_progress_stats(&upa);
+done:
+ free(cwd);
+ if (commit)
+ got_object_commit_close(commit);
+ free(commit_id_str);
+ if (worktree)
+ got_worktree_close(worktree);
+ if (repo) {
+ const struct got_error *close_err = got_repo_close(repo);
+ if (error == NULL)
+ error = close_err;
+ }
+ if (pack_fds) {
+ const struct got_error *pack_err =
+ got_repo_pack_fds_close(pack_fds);
+ if (error == NULL)
+ error = pack_err;
+ }
+
+ return error;
+}
+
+__dead static void
+usage_backout(void)
+{
+ fprintf(stderr, "usage: %s backout [-lX] [commit-id]\n", getprogname());
+ exit(1);
+}
+
+static const struct got_error *
+cmd_backout(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ struct got_worktree *worktree = NULL;
+ struct got_repository *repo = NULL;
+ char *cwd = NULL, *commit_id_str = NULL;
+ struct got_object_id *commit_id = NULL;
+ struct got_commit_object *commit = NULL;
+ struct got_object_qid *pid;
+ int ch, list_refs = 0, remove_refs = 0;
+ struct got_update_progress_arg upa;
+ int *pack_fds = NULL;
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
+ "unveil", NULL) == -1)
+ err(1, "pledge");
+#endif
+
+ while ((ch = getopt(argc, argv, "lX")) != -1) {
+ switch (ch) {
+ case 'l':
+ list_refs = 1;
+ break;
+ case 'X':
+ remove_refs = 1;
+ break;
+ default:
+ usage_backout();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (list_refs || remove_refs) {
+ if (argc != 0 && argc != 1)
+ usage_backout();
+ } else if (argc != 1)
+ usage_backout();
+ if (list_refs && remove_refs)
+ option_conflict('l', 'X');
+
+ cwd = getcwd(NULL, 0);
+ if (cwd == NULL) {
+ error = got_error_from_errno("getcwd");
+ goto done;
+ }
+
+ error = got_repo_pack_fds_open(&pack_fds);
+ if (error != NULL)
+ goto done;
+
+ error = got_worktree_open(&worktree, cwd);
+ if (error) {
+ if (list_refs || remove_refs) {
+ if (error->code != GOT_ERR_NOT_WORKTREE)
+ goto done;
+ } else {
+ if (error->code == GOT_ERR_NOT_WORKTREE)
+ error = wrap_not_worktree_error(error,
+ "backout", cwd);
+ goto done;
+ }
+ }
+
+ error = got_repo_open(&repo,
+ worktree ? got_worktree_get_repo_path(worktree) : cwd,
+ NULL, pack_fds);
+ if (error != NULL)
+ goto done;
+
+ error = apply_unveil(got_repo_get_path(repo), 0,
+ worktree ? got_worktree_get_root_path(worktree) : NULL);
+ if (error)
+ goto done;
+
+ if (list_refs || remove_refs) {
+ error = process_logmsg_refs(GOT_WORKTREE_BACKOUT_REF_PREFIX,
+ GOT_WORKTREE_BACKOUT_REF_PREFIX_LEN,
+ argc == 1 ? argv[0] : NULL, remove_refs, worktree, repo);
+ goto done;
+ }
+
+ error = got_repo_match_object_id(&commit_id, NULL, argv[0],
+ GOT_OBJ_TYPE_COMMIT, NULL, repo);
+ if (error)
+ goto done;
+ error = got_object_id_str(&commit_id_str, commit_id);
+ if (error)
+ goto done;
+
+ error = got_object_open_as_commit(&commit, repo, commit_id);
+ if (error)
+ goto done;
+ pid = STAILQ_FIRST(got_object_commit_get_parent_ids(commit));
+ if (pid == NULL) {
+ error = got_error(GOT_ERR_ROOT_COMMIT);
+ goto done;
+ }
+
+ memset(&upa, 0, sizeof(upa));
+ error = got_worktree_merge_files(worktree, commit_id, &pid->id,
+ repo, update_progress, &upa, check_cancelled, NULL);
+ if (error != NULL)
+ goto done;
+
+ if (upa.did_something) {
+ error = logmsg_ref(commit_id, GOT_WORKTREE_BACKOUT_REF_PREFIX,
+ worktree, repo);
+ if (error)
+ goto done;
+ printf("Backed out commit %s\n", commit_id_str);
+ }
+ print_merge_progress_stats(&upa);
+done:
+ free(cwd);
+ if (commit)
+ got_object_commit_close(commit);
+ free(commit_id_str);
+ if (worktree)
+ got_worktree_close(worktree);
+ if (repo) {
+ const struct got_error *close_err = got_repo_close(repo);
+ if (error == NULL)
+ error = close_err;
+ }
+ if (pack_fds) {
+ const struct got_error *pack_err =
+ got_repo_pack_fds_close(pack_fds);
+ if (error == NULL)
+ error = pack_err;
+ }
return error;
}