summaryrefslogtreecommitdiff
path: root/branch.c
diff options
context:
space:
mode:
Diffstat (limited to 'branch.c')
-rw-r--r--branch.c180
1 files changed, 123 insertions, 57 deletions
diff --git a/branch.c b/branch.c
index 4c8523c..e4a738f 100644
--- a/branch.c
+++ b/branch.c
@@ -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(&current_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(&current_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(&current_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(&current_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(&current_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;
}