diff options
Diffstat (limited to 'branch.c')
-rw-r--r-- | branch.c | 180 |
1 files changed, 123 insertions, 57 deletions
@@ -1,15 +1,22 @@ #include "git-compat-util.h" -#include "cache.h" +#include "advice.h" #include "config.h" #include "branch.h" +#include "environment.h" +#include "gettext.h" +#include "hex.h" +#include "object-name.h" +#include "path.h" #include "refs.h" #include "refspec.h" #include "remote.h" +#include "repository.h" #include "sequencer.h" #include "commit.h" #include "worktree.h" #include "submodule-config.h" #include "run-command.h" +#include "strmap.h" struct tracking { struct refspec_item spec; @@ -31,7 +38,7 @@ static int find_tracked_branch(struct remote *remote, void *priv) if (!remote_find_tracking(remote, &tracking->spec)) { switch (++tracking->matches) { case 1: - string_list_append(tracking->srcs, tracking->spec.src); + string_list_append_nodup(tracking->srcs, tracking->spec.src); tracking->remote = remote->name; break; case 2: @@ -227,7 +234,7 @@ static int inherit_tracking(struct tracking *tracking, const char *orig_ref) return -1; } - tracking->remote = xstrdup(branch->remote_name); + tracking->remote = branch->remote_name; for (i = 0; i < branch->merge_nr; i++) string_list_append(tracking->srcs, branch->merge_name[i]); return 0; @@ -327,7 +334,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref, if (!skip_prefix(tracking.srcs->items[0].string, "refs/heads/", &tracked_branch) || strcmp(tracked_branch, new_ref)) - return; + goto cleanup; } if (tracking.srcs->nr < 1) @@ -363,12 +370,93 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name) */ int validate_branchname(const char *name, struct strbuf *ref) { - if (strbuf_check_branch_ref(ref, name)) - die(_("'%s' is not a valid branch name"), name); + if (strbuf_check_branch_ref(ref, name)) { + int code = die_message(_("'%s' is not a valid branch name"), name); + advise_if_enabled(ADVICE_REF_SYNTAX, + _("See `man git check-ref-format`")); + exit(code); + } return ref_exists(ref->buf); } +static int initialized_checked_out_branches; +static struct strmap current_checked_out_branches = STRMAP_INIT; + +static void prepare_checked_out_branches(void) +{ + int i = 0; + struct worktree **worktrees; + + if (initialized_checked_out_branches) + return; + initialized_checked_out_branches = 1; + + worktrees = get_worktrees(); + + while (worktrees[i]) { + char *old; + struct wt_status_state state = { 0 }; + struct worktree *wt = worktrees[i++]; + struct string_list update_refs = STRING_LIST_INIT_DUP; + + if (wt->is_bare) + continue; + + if (wt->head_ref) { + old = strmap_put(¤t_checked_out_branches, + wt->head_ref, + xstrdup(wt->path)); + free(old); + } + + if (wt_status_check_rebase(wt, &state) && + (state.rebase_in_progress || state.rebase_interactive_in_progress) && + state.branch) { + struct strbuf ref = STRBUF_INIT; + strbuf_addf(&ref, "refs/heads/%s", state.branch); + old = strmap_put(¤t_checked_out_branches, + ref.buf, + xstrdup(wt->path)); + free(old); + strbuf_release(&ref); + } + wt_status_state_free_buffers(&state); + + if (wt_status_check_bisect(wt, &state) && + state.bisecting_from) { + struct strbuf ref = STRBUF_INIT; + strbuf_addf(&ref, "refs/heads/%s", state.bisecting_from); + old = strmap_put(¤t_checked_out_branches, + ref.buf, + xstrdup(wt->path)); + free(old); + strbuf_release(&ref); + } + wt_status_state_free_buffers(&state); + + if (!sequencer_get_update_refs_state(get_worktree_git_dir(wt), + &update_refs)) { + struct string_list_item *item; + for_each_string_list_item(item, &update_refs) { + old = strmap_put(¤t_checked_out_branches, + item->string, + xstrdup(wt->path)); + free(old); + } + string_list_clear(&update_refs, 1); + } + } + + free_worktrees(worktrees); +} + +const char *branch_checked_out(const char *refname) +{ + prepare_checked_out_branches(); + return strmap_get(¤t_checked_out_branches, refname); +} + /* * Check if a branch 'name' can be created as a new branch; die otherwise. * 'force' can be used when it is OK for the named branch already exists. @@ -377,9 +465,7 @@ int validate_branchname(const char *name, struct strbuf *ref) */ int validate_new_branchname(const char *name, struct strbuf *ref, int force) { - struct worktree **worktrees; - const struct worktree *wt; - + const char *path; if (!validate_branchname(name, ref)) return 0; @@ -387,13 +473,10 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force) die(_("a branch named '%s' already exists"), ref->buf + strlen("refs/heads/")); - worktrees = get_worktrees(); - wt = find_shared_symref(worktrees, "HEAD", ref->buf); - if (wt && !wt->is_bare) + if ((path = branch_checked_out(ref->buf))) die(_("cannot force update the branch '%s' " - "checked out at '%s'"), - ref->buf + strlen("refs/heads/"), wt->path); - free_worktrees(worktrees); + "used by worktree at '%s'"), + ref->buf + strlen("refs/heads/"), path); return 1; } @@ -402,9 +485,12 @@ static int check_tracking_branch(struct remote *remote, void *cb_data) { char *tracking_branch = cb_data; struct refspec_item query; + int res; memset(&query, 0, sizeof(struct refspec_item)); query.dst = tracking_branch; - return !remote_find_tracking(remote, &query); + res = !remote_find_tracking(remote, &query); + free(query.src); + return res; } static int validate_remote_tracking_branch(char *ref) @@ -458,7 +544,7 @@ static void dwim_branch_start(struct repository *r, const char *start_name, explicit_tracking = 1; real_ref = NULL; - if (get_oid_mb(start_name, &oid)) { + if (repo_get_oid_mb(r, start_name, &oid)) { if (explicit_tracking) { int code = die_message(_(upstream_missing), start_name); advise_if_enabled(ADVICE_SET_UPSTREAM_FAILURE, @@ -468,7 +554,8 @@ static void dwim_branch_start(struct repository *r, const char *start_name, die(_("not a valid object name: '%s'"), start_name); } - switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) { + switch (repo_dwim_ref(r, start_name, strlen(start_name), &oid, + &real_ref, 0)) { case 0: /* Not branching from any existing branch */ if (explicit_tracking) @@ -559,9 +646,10 @@ void dwim_and_setup_tracking(struct repository *r, const char *new_ref, const char *orig_ref, enum branch_track track, int quiet) { - char *real_orig_ref; + char *real_orig_ref = NULL; dwim_branch_start(r, orig_ref, track, &real_orig_ref, NULL); setup_tracking(new_ref, real_orig_ref, track, quiet); + free(real_orig_ref); } /** @@ -650,7 +738,7 @@ static int submodule_create_branch(struct repository *r, } void create_branches_recursively(struct repository *r, const char *name, - const char *start_commitish, + const char *start_committish, const char *tracking_name, int force, int reflog, int quiet, enum branch_track track, int dry_run) @@ -660,8 +748,8 @@ void create_branches_recursively(struct repository *r, const char *name, struct object_id super_oid; struct submodule_entry_list submodule_entry_list; - /* Perform dwim on start_commitish to get super_oid and branch_point. */ - dwim_branch_start(r, start_commitish, BRANCH_TRACK_NEVER, + /* Perform dwim on start_committish to get super_oid and branch_point. */ + dwim_branch_start(r, start_committish, BRANCH_TRACK_NEVER, &branch_point, &super_oid); /* @@ -683,8 +771,8 @@ void create_branches_recursively(struct repository *r, const char *name, _("submodule '%s': unable to find submodule"), submodule_entry_list.entries[i].submodule->name); if (advice_enabled(ADVICE_SUBMODULES_NOT_UPDATED)) - advise(_("You may try updating the submodules using 'git checkout %s && git submodule update --init'"), - start_commitish); + advise(_("You may try updating the submodules using 'git checkout --no-recurse-submodules %s && git submodule update --init'"), + start_committish); exit(code); } @@ -699,7 +787,7 @@ void create_branches_recursively(struct repository *r, const char *name, name); } - create_branch(the_repository, name, start_commitish, force, 0, reflog, quiet, + create_branch(r, name, start_committish, force, 0, reflog, quiet, BRANCH_TRACK_NEVER, dry_run); if (dry_run) return; @@ -733,8 +821,9 @@ void remove_merge_branch_state(struct repository *r) unlink(git_path_merge_rr(r)); unlink(git_path_merge_msg(r)); unlink(git_path_merge_mode(r)); - unlink(git_path_auto_merge(r)); - save_autostash(git_path_merge_autostash(r)); + refs_delete_ref(get_main_ref_store(r), "", "AUTO_MERGE", + NULL, REF_NO_DEREF); + save_autostash_ref(r, "MERGE_AUTOSTASH"); } void remove_branch_state(struct repository *r, int verbose) @@ -747,40 +836,17 @@ void remove_branch_state(struct repository *r, int verbose) void die_if_checked_out(const char *branch, int ignore_current_worktree) { struct worktree **worktrees = get_worktrees(); - const struct worktree *wt; - - wt = find_shared_symref(worktrees, "HEAD", branch); - if (wt && (!ignore_current_worktree || !wt->is_current)) { - skip_prefix(branch, "refs/heads/", &branch); - die(_("'%s' is already checked out at '%s'"), branch, wt->path); - } - - free_worktrees(worktrees); -} - -int replace_each_worktree_head_symref(const char *oldref, const char *newref, - const char *logmsg) -{ - int ret = 0; - struct worktree **worktrees = get_worktrees(); - int i; - - for (i = 0; worktrees[i]; i++) { - struct ref_store *refs; - if (worktrees[i]->is_detached) - continue; - if (!worktrees[i]->head_ref) - continue; - if (strcmp(oldref, worktrees[i]->head_ref)) + for (int i = 0; worktrees[i]; i++) { + if (worktrees[i]->is_current && ignore_current_worktree) continue; - refs = get_worktree_ref_store(worktrees[i]); - if (refs_create_symref(refs, "HEAD", newref, logmsg)) - ret = error(_("HEAD of working tree %s is not updated"), - worktrees[i]->path); + if (is_shared_symref(worktrees[i], "HEAD", branch)) { + skip_prefix(branch, "refs/heads/", &branch); + die(_("'%s' is already used by worktree at '%s'"), + branch, worktrees[i]->path); + } } free_worktrees(worktrees); - return ret; } |