git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/PATCH] Add multiple workdir support to branch/checkout
@ 2011-10-05  3:43 Jay Soffian
  2011-10-05  3:48 ` Jay Soffian
                   ` (3 more replies)
  0 siblings, 4 replies; 35+ messages in thread
From: Jay Soffian @ 2011-10-05  3:43 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian

When using 'git new-workdir', there is no safety mechanism to prevent the
same branch from being checked out twice, nor to prevent a checked out
branch from being deleted.

By teaching 'checkout' to record the workdir path using
'branch.<name>.checkout' when switching branches, we can easily check if a
branch is already checked out in another workdir before switching to that
branch. Similarly, we can now add a check before deleting a branch.

Allow 'checkout -f' to force the checkout and issue a warning
instead of an error.

Guard this behavior behind 'core.recordCheckouts', which we will
teach 'git new-workdir' to set in a followup commit.

Note: when switching away from a branch, we set 'branch.<name>.checkout'
to the empty string, instead of deleting it entirely, since git_config()
otherwise leaves behind an empty section which it does not re-use.

Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
 builtin/branch.c   |   10 ++++++++++
 builtin/checkout.c |   39 +++++++++++++++++++++++++++++++++++++++
 remote.c           |    4 ++++
 remote.h           |    1 +
 4 files changed, 54 insertions(+), 0 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index f49596f826..6ce1a5b133 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -182,6 +182,16 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 			ret = 1;
 			continue;
 		}
+		if (kinds == REF_LOCAL_BRANCH) {
+			struct branch *branch = branch_get(bname.buf);
+			if (branch->work_tree && strlen(branch->work_tree)) {
+				error(_("Cannot delete the branch '%s' "
+					"which is currently checked out in '%s'"),
+				      bname.buf, branch->work_tree);
+				ret = 1;
+				continue;
+			}
+		}
 
 		free(name);
 
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 5e356a6c61..26259a41a7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -33,6 +33,7 @@ struct checkout_opts {
 	int force_detach;
 	int writeout_stage;
 	int writeout_error;
+	int record_checkouts;
 
 	/* not set by parse_options */
 	int branch_exists;
@@ -709,12 +710,35 @@ static void orphaned_commit_warning(struct commit *commit)
 	for_each_ref(clear_commit_marks_from_one_ref, NULL);
 }
 
+static void record_checkout(const char *name, const char *work_tree)
+{
+	struct strbuf key = STRBUF_INIT;
+	strbuf_addf(&key, "branch.%s.checkout", name);
+	git_config_set(key.buf, work_tree);
+	strbuf_release(&key);
+}
+
+static void check_if_checked_out(struct checkout_opts *opts, const char *name)
+{
+	struct branch *branch = branch_get(name);
+	if (branch->work_tree && strlen(branch->work_tree) &&
+	    strcmp(branch->work_tree, get_git_work_tree())) {
+		if (opts->force)
+			warning(_("branch '%s' is currently checked out"
+				  " in '%s'"), name, branch->work_tree);
+		else
+			die(_("branch '%s' is currently checked out"
+			      " in '%s'"), name, branch->work_tree);
+	}
+}
+
 static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 {
 	int ret = 0;
 	struct branch_info old;
 	unsigned char rev[20];
 	int flag;
+
 	memset(&old, 0, sizeof(old));
 	old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
 	old.commit = lookup_commit_reference_gently(rev, 1);
@@ -734,6 +758,9 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 		parse_commit(new->commit);
 	}
 
+	if (opts->record_checkouts)
+		check_if_checked_out(opts, new->name);
+
 	ret = merge_working_tree(opts, &old, new);
 	if (ret)
 		return ret;
@@ -743,6 +770,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 
 	update_refs_for_switch(opts, &old, new);
 
+	if (opts->record_checkouts) {
+		const char *work_tree = get_git_work_tree();
+		struct branch *branch = branch_get(old.name);
+		if (branch->work_tree && !strcmp(branch->work_tree, work_tree))
+			record_checkout(old.name, "");
+		record_checkout(new->name, work_tree);
+	}
+
 	ret = post_checkout_hook(old.commit, new->commit, 1);
 	free((char *)old.path);
 	return ret || opts->writeout_error;
@@ -756,6 +791,10 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
+	if (!strcmp(var, "core.recordcheckouts")) {
+		struct checkout_opts *opts = cb;
+		opts->record_checkouts = git_config_bool(var, value);
+	}
 	if (!prefixcmp(var, "submodule."))
 		return parse_submodule_config_option(var, value);
 
diff --git a/remote.c b/remote.c
index b8ecfa5d95..2bc063dae8 100644
--- a/remote.c
+++ b/remote.c
@@ -364,6 +364,10 @@ static int handle_config(const char *key, const char *value, void *cb)
 			if (!value)
 				return config_error_nonbool(key);
 			add_merge(branch, xstrdup(value));
+		} else if (!strcmp(subkey, ".checkout")) {
+			if (!value)
+				return config_error_nonbool(key);
+			branch->work_tree = xstrdup(value);
 		}
 		return 0;
 	}
diff --git a/remote.h b/remote.h
index 9a30a9dba6..4103ec7e31 100644
--- a/remote.h
+++ b/remote.h
@@ -126,6 +126,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec);
 struct branch {
 	const char *name;
 	const char *refname;
+	const char *work_tree;
 
 	const char *remote_name;
 	struct remote *remote;
-- 
1.7.7.4.g39e02c

^ permalink raw reply related	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2011-10-08 23:00 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-10-05  3:43 [RFC/PATCH] Add multiple workdir support to branch/checkout Jay Soffian
2011-10-05  3:48 ` Jay Soffian
2011-10-05  4:02 ` Nguyen Thai Ngoc Duy
2011-10-05 13:11   ` Jay Soffian
2011-10-05 16:46     ` Junio C Hamano
2011-10-05 17:17       ` Jay Soffian
2011-10-05 18:19         ` Junio C Hamano
2011-10-05 19:11           ` Jay Soffian
2011-10-05 20:00             ` Andreas Krey
2011-10-05 20:50               ` Jay Soffian
2011-10-05 21:30                 ` Jonathan Nieder
2011-10-05 21:52                   ` Jay Soffian
2011-10-05 21:57                     ` Jonathan Nieder
2011-10-05 21:29             ` Junio C Hamano
2011-10-05 21:49               ` Jay Soffian
2011-10-05 19:14           ` Jay Soffian
2011-10-05 22:47           ` Nguyen Thai Ngoc Duy
2011-10-05 22:56             ` Junio C Hamano
2011-10-05 23:11               ` Nguyen Thai Ngoc Duy
2011-10-05 23:49                 ` Junio C Hamano
2011-10-06  0:33                   ` Jay Soffian
2011-10-06  0:43                     ` Junio C Hamano
2011-10-06  0:57                       ` Jay Soffian
2011-10-06  1:15                         ` Junio C Hamano
2011-10-06  1:38                           ` Jay Soffian
2011-10-06  1:57                             ` Junio C Hamano
2011-10-06  4:02                               ` Jay Soffian
2011-10-06  2:06                   ` Nguyen Thai Ngoc Duy
2011-10-06 11:25           ` Bernhard R. Link
2011-10-06 14:42           ` Jeff King
2011-10-05 22:38       ` Nguyen Thai Ngoc Duy
2011-10-05  4:07 ` Junio C Hamano
2011-10-05 15:24   ` Jay Soffian
2011-10-05 16:01     ` Jay Soffian
2011-10-08 22:55 ` Julián Landerreche

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).