git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 3/3] Teach "git branch" about --new-workdir
@ 2007-07-22 18:56 Johannes Schindelin
  2007-07-22 19:16 ` Daniel Barkalow
  2007-07-22 21:09 ` Julian Phillips
  0 siblings, 2 replies; 46+ messages in thread
From: Johannes Schindelin @ 2007-07-22 18:56 UTC (permalink / raw)
  To: git, gitster


Inspired by contrib/workdir/git-new-workdir, the flag --new-workdir (or
its shortcut, -n) can be used to create a new working directory for the
newly created branch.  All information, except which branch is checked
out (and therefore also the index), will be symlinked from the current
repository.

Example:

	$ git branch -n ~/my-new-topic xy-problem

will create a branch called "xy-problem", which is initially identical
to the current branch, and check out the new branch in ~/my-new-topic/.
You will be able to cherry-pick from, log and diff with the branch
"xy-problem" in the current repository, since most of the metadata is
shared.

Conversely, you can access all the branches in the current repository
from ~/my-new-topic/, too.

A word of warning: switching to _same_ branch that is checked out in
the other repository is asking for trouble.  You are really working not
only on the same object database, but also the same (i.e. not copied)
refs namespace.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
	IMHO this is a better syntax than what is in contrib/, and "git
	branch" is probably the right place for such a thing, from a
	user's perspective.

 Documentation/git-branch.txt |    7 +++-
 builtin-branch.c             |   79 +++++++++++++++++++++++++++++++++++++++--
 t/t3200-branch.sh            |   11 ++++++
 3 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index bc6aa88..a05c795 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,7 +10,8 @@ SYNOPSIS
 [verse]
 'git-branch' [--color | --no-color] [-r | -a]
 	   [-v [--abbrev=<length> | --no-abbrev]]
-'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
+'git-branch' [--track | --no-track] [-l] [-f] <branchname>
+	   [-n <dir> | --new-workdir <dir>] [<start-point>]
 'git-branch' (-m | -M) [<oldbranch>] <newbranch>
 'git-branch' (-d | -D) [-r] <branchname>...
 
@@ -91,6 +92,10 @@ OPTIONS
 --no-abbrev::
 	Display the full sha1s in output listing rather than abbreviating them.
 
+-n\|--new-workdir <dir>::
+	Set up a new working directory which shares all information with the
+	current repository, except which branch is checked out.
+
 <branchname>::
 	The name of the branch to create or delete.
 	The new branch name must pass all checks defined by
diff --git a/builtin-branch.c b/builtin-branch.c
index 5f5c182..cba5fac 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -11,9 +11,10 @@
 #include "commit.h"
 #include "builtin.h"
 #include "remote.h"
+#include "unpack-trees.h"
 
 static const char builtin_branch_usage[] =
-  "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
+  "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] [-n <dir> | --new-workdir <dir>] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
 
 #define REF_UNKNOWN_TYPE    0x00
 #define REF_LOCAL_BRANCH    0x01
@@ -500,6 +501,67 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 		die("Branch is renamed, but update of config-file failed");
 }
 
+static struct lock_file lockfile;
+
+/* This function free()s path */
+static int create_new_workdir(char *path, const char *branch_name)
+{
+	static const char *links[] = {
+		"config", "refs", "logs/refs", "objects", "info", "hooks",
+		"packed-refs", "remotes", "rr-cache", NULL
+	};
+	const char *git_dir = get_git_dir(), *absolute;
+	struct stat st;
+	unsigned char sha1[20];
+	char *ref;
+	struct object *object;
+	struct unpack_trees_options opts;
+	struct object_list trees;
+	int i, fd;
+
+	if (!has_symlinks)
+		return error("New workdir not possible without symlinks");
+	/* make sure that GIT_DIR is an absolute path */
+	if ((absolute = make_absolute_path(git_dir)) != git_dir &&
+			setup_new_git_dir(absolute))
+		return 1;
+	if (!lstat(git_path("config"), &st) && S_ISLNK(st.st_mode))
+		return error("Will not create a workdir from a workdir");
+	if (safe_create_leading_directories(path) ||
+			mkdir(path, 0777) || chdir(path))
+		return error("Could not create '%s'", path);
+	if (mkdir(DEFAULT_GIT_DIR_ENVIRONMENT, 0777) ||
+			chdir(DEFAULT_GIT_DIR_ENVIRONMENT) ||
+			mkdir("logs", 0777))
+		return error("Could not set up '%s/%s'",
+				path, DEFAULT_GIT_DIR_ENVIRONMENT);
+	for (i = 0; links[i]; i++)
+		if (symlink(git_path(links[i]), links[i]))
+			return error("Could not link '%s'", links[i]);
+	if (chdir("..") || setup_new_git_dir(DEFAULT_GIT_DIR_ENVIRONMENT))
+		return error("Error setting up new workdir");
+	fd = hold_locked_index(&lockfile, 1);
+	if (fd < 0)
+		return error("Could not lock index");
+	if (dwim_ref(branch_name, strlen(branch_name), sha1, &ref) != 1 ||
+			create_symref("HEAD", ref, "new workdir") ||
+			!(object = parse_object(sha1)) ||
+			object->type != OBJ_COMMIT)
+		return error("Could not checkout HEAD");
+	free(ref);
+	trees.item = &((struct commit *)object)->tree->object;
+	trees.next = NULL;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.update = 1;
+	opts.merge = 1;
+	opts.head_idx = 1;
+	opts.fn = oneway_merge;
+	return unpack_trees(&trees, &opts) ||
+		write_cache(fd, active_cache, active_nr) ||
+		close(fd) || commit_locked_index(&lockfile);
+}
+
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, force_delete = 0, force_create = 0;
@@ -507,6 +569,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
 	int reflog = 0, track;
 	int kinds = REF_LOCAL_BRANCH;
+	char *new_workdir = NULL;
 	int i;
 
 	git_config(git_branch_config);
@@ -587,11 +650,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			branch_use_color = 0;
 			continue;
 		}
+		if (!strcmp(arg, "--new-workdir") || !strcmp(arg, "-n")) {
+			if (++i >= argc)
+				usage(builtin_branch_usage);
+			new_workdir = xstrdup(argv[i]);
+			continue;
+		}
 		usage(builtin_branch_usage);
 	}
 
 	if ((delete && rename) || (delete && force_create) ||
-	    (rename && force_create))
+	    (rename && force_create) || (new_workdir && (delete || rename)))
 		usage(builtin_branch_usage);
 
 	head = resolve_ref("HEAD", head_sha1, 0, NULL);
@@ -615,10 +684,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		rename_branch(head, argv[i], force_rename);
 	else if (rename && (i == argc - 2))
 		rename_branch(argv[i], argv[i + 1], force_rename);
-	else if (i == argc - 1 || i == argc - 2)
+	else if (i == argc - 1 || i == argc - 2) {
 		create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head,
 			      force_create, reflog, track);
-	else
+		if (new_workdir)
+			return create_new_workdir(new_workdir, argv[i]);
+	} else
 		usage(builtin_branch_usage);
 
 	return 0;
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index ef1eeb7..d9ec82a 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -202,4 +202,15 @@ test_expect_success \
 	 test -f .git/logs/refs/heads/g/h/i &&
 	 diff expect .git/logs/refs/heads/g/h/i'
 
+test_expect_success 'create new workdir' '
+	git branch -n new-workdir forked &&
+	test -d new-workdir &&
+	(cd new-workdir &&
+	 test $HEAD = $(git rev-parse HEAD) &&
+	 test refs/heads/forked = $(git symbolic-ref HEAD) &&
+	 git fsck &&
+	 git diff-files --quiet &&
+	 git diff-index --quiet --cached HEAD)
+'
+
 test_done
-- 
1.5.3.rc2.29.gc4640f

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

end of thread, other threads:[~2007-07-26 11:51 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-22 18:56 [PATCH 3/3] Teach "git branch" about --new-workdir Johannes Schindelin
2007-07-22 19:16 ` Daniel Barkalow
2007-07-22 19:24   ` Johannes Schindelin
2007-07-22 21:09 ` Julian Phillips
2007-07-22 21:25   ` Johannes Schindelin
2007-07-22 21:50     ` Julian Phillips
2007-07-22 21:59       ` Johannes Schindelin
2007-07-22 22:24         ` Julian Phillips
2007-07-22 22:46           ` Jakub Narebski
2007-07-22 23:37             ` Johannes Schindelin
2007-07-22 23:02           ` Johannes Schindelin
2007-07-23  3:56             ` Shawn O. Pearce
2007-07-23  4:45               ` Junio C Hamano
2007-07-23  5:14                 ` Shawn O. Pearce
2007-07-23  5:22                   ` Shawn O. Pearce
2007-07-23 10:32                 ` Johannes Schindelin
2007-07-23 10:42                   ` Johannes Schindelin
2007-07-24  8:19                 ` Marius Storm-Olsen
2007-07-24  9:02                   ` Johannes Schindelin
2007-07-24  9:47                     ` Junio C Hamano
2007-07-24 11:07                       ` Johannes Schindelin
2007-07-24 11:14                       ` Marius Storm-Olsen
2007-07-24 12:06                         ` Julian Phillips
2007-07-24 12:28                           ` Marius Storm-Olsen
2007-07-24 12:37                           ` Johannes Schindelin
2007-07-24 13:47                             ` Josef Weidendorfer
2007-07-24 13:54                               ` Johannes Schindelin
2007-07-24 14:21                                 ` Josef Weidendorfer
2007-07-25  0:09                           ` Jakub Narebski
2007-07-24 12:42                         ` Johannes Schindelin
2007-07-24 13:26                           ` Marius Storm-Olsen
2007-07-24 13:29                             ` Marius Storm-Olsen
2007-07-24 13:33                             ` Johannes Schindelin
2007-07-24 18:02                               ` Marius Storm-Olsen
2007-07-24 18:30                                 ` Johannes Schindelin
2007-07-24 19:36                                   ` Marius Storm-Olsen
2007-07-24 23:15                                     ` Alex Riesen
2007-07-25  6:47                                       ` Marius Storm-Olsen
2007-07-25  9:39                                         ` Johannes Schindelin
2007-07-25 10:22                                           ` Steven Grimm
2007-07-25 11:05                                           ` Andy Parkins
2007-07-25 12:10                                             ` Marius Storm-Olsen
2007-07-25 14:09                                               ` Johannes Schindelin
2007-07-25 20:40                                             ` Linus Torvalds
2007-07-26 11:51                                               ` Christian MICHON
2007-07-23  8:31             ` Julian Phillips

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).