Re: [PATCH v3 21/25] checkout: support checking out into a new working directory
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
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
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
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);