Re: [PATCH v3 21/25] checkout: support checking out into a new working directory

2014-02-26 Thread Eric Sunshine
On Tue, Feb 18, 2014 at 8:40 AM, Nguyễn Thái Ngọc Duy pclo...@gmail.com wrote:
 git checkout --to sets up a new working directory with a .git file
 pointing to $GIT_DIR/repos/id. It then executes git checkout again
 on the new worktree with the same arguments except --to is taken
 out. The second checkout execution, which is not contaminated with any
 info from the current repository, will actually check out and
 everything that normal git checkout does.

 Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
 ---
 diff --git a/builtin/checkout.c b/builtin/checkout.c
 index 0570e41..2b856a6 100644
 --- a/builtin/checkout.c
 +++ b/builtin/checkout.c
 @@ -806,6 +814,74 @@ static int switch_branches(const struct checkout_opts 
 *opts,
 return ret || writeout_error;
  }

 +static int prepare_linked_checkout(const struct checkout_opts *opts,
 +  struct branch_info *new)
 +{
 +   struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
 +   struct strbuf sb = STRBUF_INIT;
 +   const char *path = opts-new_worktree;
 +   struct stat st;
 +   const char *name;
 +   struct child_process cp;
 +   int counter = 0, len;
 +
 +   if (!new-commit)
 +   die(_(no branch specified));
 +
 +   len = strlen(path);
 +   if (!len || is_dir_sep(path[len - 1]))
 +   die(_('--to' argument '%s' cannot end with a slash), path);

What is the purpose of this restriction?

 +   for (name = path + len - 1; name  path; name--)
 +   if (is_dir_sep(*name)) {
 +   name++;
 +   break;
 +   }
 +   strbuf_addstr(sb_repo, git_path(repos/%s, name));
 +   len = sb_repo.len;
 +   if (safe_create_leading_directories_const(sb_repo.buf))
 +   die_errno(_(could not create leading directories of '%s'),
 + sb_repo.buf);
 +   while (!stat(sb_repo.buf, st)) {
 +   counter++;
 +   strbuf_setlen(sb_repo, len);
 +   strbuf_addf(sb_repo, %d, counter);
 +   }
 +   name = sb_repo.buf + len - strlen(name);
 +   if (mkdir(sb_repo.buf, 0777))
 +   die_errno(_(could not create directory of '%s'), 
 sb_repo.buf);
 +
 +   strbuf_addf(sb_git, %s/.git, path);
 +   if (safe_create_leading_directories_const(sb_git.buf))
 +   die_errno(_(could not create leading directories of '%s'),
 + sb_git.buf);
 +
 +   write_file(sb_git.buf, 1, gitdir: %s/repos/%s\n,
 +  real_path(get_git_dir()), name);
 +   /*
 +* This is to keep resolve_ref() happy. We need a valid HEAD
 +* or is_git_directory() will reject the directory. Any valid
 +* value would do because this value will be ignored and
 +* replaced at the next (real) checkout.
 +*/
 +   strbuf_addf(sb, %s/HEAD, sb_repo.buf);
 +   write_file(sb.buf, 1, %s\n, sha1_to_hex(new-commit-object.sha1));
 +   strbuf_reset(sb);
 +   strbuf_addf(sb, %s/commondir, sb_repo.buf);
 +   write_file(sb.buf, 1, ../..\n);
 +
 +   if (!opts-quiet)
 +   fprintf_ln(stderr, _(Enter %s (identifier %s)), path, name);
 +
 +   setenv(GIT_CHECKOUT_NEW_WORKTREE, 1, 1);
 +   setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
 +   setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
 +   memset(cp, 0, sizeof(cp));
 +   cp.git_cmd = 1;
 +   cp.argv = opts-saved_argv;
 +   return run_command(cp);
 +}
 +
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 21/25] checkout: support checking out into a new working directory

2014-02-26 Thread Duy Nguyen
On Thu, Feb 27, 2014 at 3:06 AM, Eric Sunshine sunsh...@sunshineco.com wrote:
 +   len = strlen(path);
 +   if (!len || is_dir_sep(path[len - 1]))
 +   die(_('--to' argument '%s' cannot end with a slash), path);

 What is the purpose of this restriction?

Laziness on my part :) Because the following loop searches backward to
get the `basename $path`, trailing slash would make it return empty
base name. I could have just removed the trailing slash here instead
of dying though. Thanks for catching.


 +   for (name = path + len - 1; name  path; name--)
 +   if (is_dir_sep(*name)) {
 +   name++;
 +   break;
 +   }



-- 
Duy
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v3 21/25] checkout: support checking out into a new working directory

2014-02-26 Thread Eric Sunshine
On Wed, Feb 26, 2014 at 6:19 PM, Duy Nguyen pclo...@gmail.com wrote:
 On Thu, Feb 27, 2014 at 3:06 AM, Eric Sunshine sunsh...@sunshineco.com 
 wrote:
 +   len = strlen(path);
 +   if (!len || is_dir_sep(path[len - 1]))
 +   die(_('--to' argument '%s' cannot end with a slash), 
 path);

 What is the purpose of this restriction?

 Laziness on my part :) Because the following loop searches backward to
 get the `basename $path`, trailing slash would make it return empty
 base name. I could have just removed the trailing slash here instead
 of dying though. Thanks for catching.

Thanks for the explanation. I thought that that might be the case.

 +   for (name = path + len - 1; name  path; name--)
 +   if (is_dir_sep(*name)) {
 +   name++;
 +   break;
 +   }
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v3 21/25] checkout: support checking out into a new working directory

2014-02-18 Thread Nguyễn Thái Ngọc Duy
git checkout --to sets up a new working directory with a .git file
pointing to $GIT_DIR/repos/id. It then executes git checkout again
on the new worktree with the same arguments except --to is taken
out. The second checkout execution, which is not contaminated with any
info from the current repository, will actually check out and
everything that normal git checkout does.

Signed-off-by: Nguyễn Thái Ngọc Duy pclo...@gmail.com
---
 Documentation/git-checkout.txt | 34 +
 Documentation/gitrepository-layout.txt |  3 ++
 builtin/checkout.c | 93 +-
 t/t2025-checkout-to.sh (new +x)| 39 ++
 4 files changed, 167 insertions(+), 2 deletions(-)
 create mode 100755 t/t2025-checkout-to.sh

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 33ad2ad..fcf73b2 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -225,6 +225,13 @@ This means that you can use `git checkout -p` to 
selectively discard
 edits from your current working tree. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
+--to=path::
+   Check out a new branch in a separate working directory at
+   `path`. A new working directory is linked to the current
+   repository, sharing everything except working directory
+   specific files such as HEAD, index... See MULTIPLE CHECKOUT
+   MODE section for more information.
+
 branch::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with refs/heads/, is a valid ref), then that
@@ -388,6 +395,33 @@ $ git reflog -2 HEAD # or
 $ git log -g -2 HEAD
 
 
+MULTIPLE CHECKOUT MODE
+---
+Normally a working directory is attached to repository. When git
+checkout --to is used, a new working directory is attached to the
+current repository. This new working directory is called linked
+checkout as compared to the main checkout prepared by git init or
+git clone. A repository has one main checkout and zero or more
+linked checkouts.
+
+All checkouts share the same repository. Linked checkouts see the
+repository a bit different from the main checkout. When the checkout
+new reads the path $GIT_DIR/HEAD for example, the actual path
+returned could be $GIT_DIR/repos/new/HEAD. This ensures checkouts
+won't step on each other.
+
+Each linked checkout has a private space in $GIT_DIR/repos, usually
+named after the base name of the working directory with a number added
+to make it unique. The linked checkout's $GIT_DIR points to this
+private space while $GIT_COMMON_DIR points to the main checkout's
+$GIT_DIR. These settings are done by git checkout --to.
+
+Because in this mode $GIT_DIR becomes a lightweight virtual file
+system where a path could be rewritten to some place else, accessing
+$GIT_DIR from scripts should use `git rev-parse --git-path` to resolve
+a path instead of using it directly unless the path is known to be
+private to the working directory.
+
 EXAMPLES
 
 
diff --git a/Documentation/gitrepository-layout.txt 
b/Documentation/gitrepository-layout.txt
index 9bfe0f1..495a937 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -218,6 +218,9 @@ commondir::
 modules::
Contains the git-repositories of the submodules.
 
+repos::
+   Contains worktree specific information of linked checkouts.
+
 SEE ALSO
 
 linkgit:git-init[1],
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 0570e41..2b856a6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -48,6 +48,10 @@ struct checkout_opts {
const char *prefix;
struct pathspec pathspec;
struct tree *source_tree;
+
+   const char *new_worktree;
+   const char **saved_argv;
+   int new_worktree_mode;
 };
 
 static int post_checkout_hook(struct commit *old, struct commit *new,
@@ -250,6 +254,9 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_(Cannot update paths and switch to branch '%s' at the 
same time.),
opts-new_branch);
 
+   if (opts-new_worktree)
+   die(_('%s' cannot be used with updating paths), --to);
+
if (opts-patch_mode)
return run_add_interactive(revision, --patch=checkout,
   opts-pathspec);
@@ -486,7 +493,7 @@ static int merge_working_tree(const struct checkout_opts 
*opts,
topts.dir-flags |= DIR_SHOW_IGNORED;
setup_standard_excludes(topts.dir);
}
-   tree = parse_tree_indirect(old-commit ?
+   tree = parse_tree_indirect(old-commit  
!opts-new_worktree_mode ?
   old-commit-object.sha1 :
   EMPTY_TREE_SHA1_BIN);