From dcf692625ac569fefbe52269061230f4fde10e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2014 15:24:27 +0700 Subject: path.c: make get_pathname() call sites return const char * MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before the previous commit, get_pathname returns an array of PATH_MAX length. Even if git_path() and similar functions does not use the whole array, git_path() caller can, in theory. After the commit, get_pathname() may return a buffer that has just enough room for the returned string and git_path() caller should never write beyond that. Make git_path(), mkpath() and git_path_submodule() return a const buffer to make sure callers do not write in it at all. This could have been part of the previous commit, but the "const" conversion is too much distraction from the core changes in path.c. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 5410dacea0..c600ec14c7 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -589,7 +589,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (opts->new_branch_log && !log_all_ref_updates) { int temp; char log_file[PATH_MAX]; - char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); + const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); temp = log_all_ref_updates; log_all_ref_updates = 1; -- cgit v1.2.1 From 1a83c240f2fe7a0caa94009f066d15fa677d32cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2014 15:24:28 +0700 Subject: git_snpath(): retire and replace with strbuf_git_path() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the previous patch, git_snpath() is modified to allocate a new strbuf buffer because vsnpath() needs that. But that makes it awkward because git_snpath() receives a pre-allocated buffer from outside and has to copy data back. Rename it to strbuf_git_path() and make it receive strbuf directly. Using git_path() in update_refs_for_switch() which used to call git_snpath() is safe because that function and all of its callers do not keep any pointer to the round-robin buffer pool allocated by get_pathname(). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index c600ec14c7..195aca7257 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -588,18 +588,21 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (opts->new_orphan_branch) { if (opts->new_branch_log && !log_all_ref_updates) { int temp; - char log_file[PATH_MAX]; - const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); + struct strbuf log_file = STRBUF_INIT; + int ret; + const char *ref_name; + ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); temp = log_all_ref_updates; log_all_ref_updates = 1; - if (log_ref_setup(ref_name, log_file, sizeof(log_file))) { + ret = log_ref_setup(ref_name, &log_file); + log_all_ref_updates = temp; + strbuf_release(&log_file); + if (ret) { fprintf(stderr, _("Can not do reflog for '%s'\n"), opts->new_orphan_branch); - log_all_ref_updates = temp; return; } - log_all_ref_updates = temp; } } else -- cgit v1.2.1 From 529fef20cf94dbd5c16f7a239ffc2b06f3cf8bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2014 15:24:47 +0700 Subject: checkout: support checking out into a new working directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "git checkout --to" sets up a new working directory with a .git file pointing to $GIT_DIR/worktrees/. 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. Helped-by: Marc Branchaud Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 2 deletions(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 195aca7257..797e14df41 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, @@ -249,6 +253,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); @@ -484,7 +491,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); init_tree_desc(&trees[0], tree->buffer, tree->size); @@ -800,7 +807,8 @@ static int switch_branches(const struct checkout_opts *opts, return ret; } - if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) + if (!opts->quiet && !old.path && old.commit && + new->commit != old.commit && !opts->new_worktree_mode) orphaned_commit_warning(old.commit, new->commit); update_refs_for_switch(opts, &old, new); @@ -810,6 +818,76 @@ 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, *name; + struct stat st; + struct child_process cp; + int counter = 0, len; + + if (!new->commit) + die(_("no branch specified")); + if (file_exists(path)) + die(_("'%s' already exists"), path); + + len = strlen(path); + while (len && is_dir_sep(path[len - 1])) + len--; + + for (name = path + len - 1; name > path; name--) + if (is_dir_sep(*name)) { + name++; + break; + } + strbuf_addstr(&sb_repo, + git_path("worktrees/%.*s", (int)(path + len - name), 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 = strrchr(sb_repo.buf, '/') + 1; + 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/worktrees/%s\n", + real_path(get_git_common_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); +} + static int git_checkout_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.ignoresubmodules")) { @@ -1071,6 +1149,9 @@ static int checkout_branch(struct checkout_opts *opts, die(_("Cannot switch branch to a non-commit '%s'"), new->name); + if (opts->new_worktree) + return prepare_linked_checkout(opts, new); + if (!new->commit && opts->new_branch) { unsigned char rev[20]; int flag; @@ -1113,6 +1194,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) N_("do not limit pathspecs to sparse entries only")), OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch, N_("second guess 'git checkout no-such-branch'")), + OPT_FILENAME(0, "to", &opts.new_worktree, + N_("check a branch out in a separate working directory")), OPT_END(), }; @@ -1121,6 +1204,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.overwrite_ignore = 1; opts.prefix = prefix; + opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2)); + memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1)); + gitmodules_config(); git_config(git_checkout_config, &opts); @@ -1129,6 +1215,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); + /* recursive execution from checkout_new_worktree() */ + opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL; + if (opts.new_worktree_mode) + opts.new_worktree = NULL; + if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); -- cgit v1.2.1 From 23af91d102e1efaff33b77ab7746356835a3d600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2014 15:24:48 +0700 Subject: prune: strategies for linked checkouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (alias R=$GIT_COMMON_DIR/worktrees/) - linked checkouts are supposed to keep its location in $R/gitdir up to date. The use case is auto fixup after a manual checkout move. - linked checkouts are supposed to update mtime of $R/gitdir. If $R/gitdir's mtime is older than a limit, and it points to nowhere, worktrees/ is to be pruned. - If $R/locked exists, worktrees/ is not supposed to be pruned. If $R/locked exists and $R/gitdir's mtime is older than a really long limit, warn about old unused repo. - "git checkout --to" is supposed to make a hard link named $R/link pointing to the .git file on supported file systems to help detect the user manually deleting the checkout. If $R/link exists and its link count is greated than 1, the repo is kept. Helped-by: Marc Branchaud Helped-by: Eric Sunshine Helped-by: Johannes Sixt Signed-off-by: Marc Branchaud Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 797e14df41..645135a812 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -826,7 +826,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, const char *path = opts->new_worktree, *name; struct stat st; struct child_process cp; - int counter = 0, len; + int counter = 0, len, ret; if (!new->commit) die(_("no branch specified")); @@ -857,11 +857,21 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (mkdir(sb_repo.buf, 0777)) die_errno(_("could not create directory of '%s'"), sb_repo.buf); + /* + * lock the incomplete repo so prune won't delete it, unlock + * after the preparation is over. + */ + strbuf_addf(&sb, "%s/locked", sb_repo.buf); + write_file(sb.buf, 1, "initializing\n"); + 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); + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); + write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf)); write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n", real_path(get_git_common_dir()), name); /* @@ -870,6 +880,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, * value would do because this value will be ignored and * replaced at the next (real) checkout. */ + strbuf_reset(&sb); 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); @@ -885,7 +896,11 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, memset(&cp, 0, sizeof(cp)); cp.git_cmd = 1; cp.argv = opts->saved_argv; - return run_command(&cp); + ret = run_command(&cp); + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/locked", sb_repo.buf); + unlink_or_warn(sb.buf); + return ret; } static int git_checkout_config(const char *var, const char *value, void *cb) -- cgit v1.2.1 From 5883034c61c60e04db16520a759ec67a34ffaea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2014 15:24:49 +0700 Subject: checkout: reject if the branch is already checked out elsewhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One branch obviously can't be checked out at two places (but detached heads are ok). Give the user a choice in this case: --detach, -b new-branch, switch branch in the other checkout first or simply 'cd' and continue to work there. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 2 deletions(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 645135a812..01a28b4864 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -430,6 +430,11 @@ struct branch_info { const char *name; /* The short name used */ const char *path; /* The full name of a real branch */ struct commit *commit; /* The named commit */ + /* + * if not null the branch is detached because it's already + * checked out in this checkout + */ + char *checkout; }; static void setup_branch_path(struct branch_info *branch) @@ -958,12 +963,78 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1) return NULL; } +static void check_linked_checkout(struct branch_info *new, const char *id) +{ + struct strbuf sb = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + struct strbuf gitdir = STRBUF_INIT; + const char *start, *end; + + if (id) + strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id); + else + strbuf_addf(&path, "%s/HEAD", get_git_common_dir()); + + if (strbuf_read_file(&sb, path.buf, 0) < 0 || + !skip_prefix(sb.buf, "ref:", &start)) + goto done; + while (isspace(*start)) + start++; + end = start; + while (*end && !isspace(*end)) + end++; + if (strncmp(start, new->path, end - start) || new->path[end - start] != '\0') + goto done; + if (id) { + strbuf_reset(&path); + strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); + if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) + goto done; + strbuf_rtrim(&gitdir); + } else + strbuf_addstr(&gitdir, get_git_common_dir()); + die(_("'%s' is already checked out at '%s'"), new->name, gitdir.buf); +done: + strbuf_release(&path); + strbuf_release(&sb); + strbuf_release(&gitdir); +} + +static void check_linked_checkouts(struct branch_info *new) +{ + struct strbuf path = STRBUF_INIT; + DIR *dir; + struct dirent *d; + + strbuf_addf(&path, "%s/worktrees", get_git_common_dir()); + if ((dir = opendir(path.buf)) == NULL) { + strbuf_release(&path); + return; + } + + /* + * $GIT_COMMON_DIR/HEAD is practically outside + * $GIT_DIR so resolve_ref_unsafe() won't work (it + * uses git_path). Parse the ref ourselves. + */ + check_linked_checkout(new, NULL); + + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + check_linked_checkout(new, d->d_name); + } + strbuf_release(&path); + closedir(dir); +} + static int parse_branchname_arg(int argc, const char **argv, int dwim_new_local_branch_ok, struct branch_info *new, struct tree **source_tree, unsigned char rev[20], - const char **new_branch) + const char **new_branch, + int force_detach) { int argcount = 0; unsigned char branch_rev[20]; @@ -1085,6 +1156,16 @@ static int parse_branchname_arg(int argc, const char **argv, else new->path = NULL; /* not an existing branch */ + if (new->path && !force_detach && !*new_branch) { + unsigned char sha1[20]; + int flag; + char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); + if (head_ref && + (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) + check_linked_checkouts(new); + free(head_ref); + } + new->commit = lookup_commit_reference_gently(rev, 1); if (!new->commit) { /* not a commit */ @@ -1289,7 +1370,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) !opts.new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, &new, &opts.source_tree, - rev, &opts.new_branch); + rev, &opts.new_branch, + opts.force_detach); argv += n; argc -= n; } -- cgit v1.2.1 From 3b8925c78bdbea49c846e350e66c13923441fff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 30 Nov 2014 15:24:50 +0700 Subject: checkout: clean up half-prepared directories in --to mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 01a28b4864..5dfdbda1a6 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -20,6 +20,7 @@ #include "resolve-undo.h" #include "submodule.h" #include "argv-array.h" +#include "sigchain.h" static const char * const checkout_usage[] = { N_("git checkout [options] "), @@ -823,6 +824,35 @@ static int switch_branches(const struct checkout_opts *opts, return ret || writeout_error; } +static char *junk_work_tree; +static char *junk_git_dir; +static int is_junk; +static pid_t junk_pid; + +static void remove_junk(void) +{ + struct strbuf sb = STRBUF_INIT; + if (!is_junk || getpid() != junk_pid) + return; + if (junk_git_dir) { + strbuf_addstr(&sb, junk_git_dir); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } + if (junk_work_tree) { + strbuf_addstr(&sb, junk_work_tree); + remove_dir_recursively(&sb, 0); + } + strbuf_release(&sb); +} + +static void remove_junk_on_signal(int signo) +{ + remove_junk(); + sigchain_pop(signo); + raise(signo); +} + static int prepare_linked_checkout(const struct checkout_opts *opts, struct branch_info *new) { @@ -859,8 +889,15 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, strbuf_addf(&sb_repo, "%d", counter); } name = strrchr(sb_repo.buf, '/') + 1; + + junk_pid = getpid(); + atexit(remove_junk); + sigchain_push_common(remove_junk_on_signal); + if (mkdir(sb_repo.buf, 0777)) die_errno(_("could not create directory of '%s'"), sb_repo.buf); + junk_git_dir = xstrdup(sb_repo.buf); + is_junk = 1; /* * lock the incomplete repo so prune won't delete it, unlock @@ -873,6 +910,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (safe_create_leading_directories_const(sb_git.buf)) die_errno(_("could not create leading directories of '%s'"), sb_git.buf); + junk_work_tree = xstrdup(path); strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); @@ -902,9 +940,19 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, cp.git_cmd = 1; cp.argv = opts->saved_argv; ret = run_command(&cp); + if (!ret) { + is_junk = 0; + free(junk_work_tree); + free(junk_git_dir); + junk_work_tree = NULL; + junk_git_dir = NULL; + } strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); + strbuf_release(&sb); + strbuf_release(&sb_repo); + strbuf_release(&sb_git); return ret; } -- cgit v1.2.1 From 3473ad0cf6c90917813aba978dada6c10352baaa Mon Sep 17 00:00:00 2001 From: Dennis Kaarsemaker Date: Sun, 30 Nov 2014 15:24:56 +0700 Subject: checkout: don't require a work tree when checking out into a new one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For normal use cases, it does not make sense for 'checkout' to work on a bare repository, without a worktree. But "checkout --to" is an exception because it _creates_ a new worktree. Allow this option to run on bare repositories. People who check out from a bare repository should remember that core.logallrefupdates is off by default and it should be turned back on. `--to` cannot do this automatically behind the user's back because some user may deliberately want no reflog. For people interested in repository setup/discovery code, is_bare_repository_cfg (aka "core.bare") is unchanged by this patch, which means 'true' by default for bare repos. Fortunately when we get the repo through a linked checkout, is_bare_repository_cfg is never used. So all is still good. [nd: commit message] Signed-off-by: Dennis Kaarsemaker Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 5dfdbda1a6..8c5276cf44 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1364,6 +1364,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.new_worktree_mode) opts.new_worktree = NULL; + if (!opts.new_worktree) + setup_work_tree(); + if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); -- cgit v1.2.1 From ee4fb8435e2186323c6a91d26d1572da12b5d033 Mon Sep 17 00:00:00 2001 From: Max Kirillov Date: Sun, 30 Nov 2014 15:24:58 +0700 Subject: checkout: do not fail if target is an empty directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-recursive checkout creates empty directpries in place of submodules. If then I try to "checkout --to" submodules there, it refuses to do so, because directory already exists. Fix by allowing checking out to empty directory. Add test and modify the existing one so that it uses non-empty directory. Signed-off-by: Max Kirillov Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 8c5276cf44..953b76370b 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -865,7 +865,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (!new->commit) die(_("no branch specified")); - if (file_exists(path)) + if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); len = strlen(path); -- cgit v1.2.1 From 10f102be211cf1ba61c983fb463665147052daa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 3 Jan 2015 16:41:25 +0700 Subject: checkout: pass whole struct to parse_branchname_arg instead of individual flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index 953b76370b..d8717efb07 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1079,11 +1079,12 @@ static void check_linked_checkouts(struct branch_info *new) static int parse_branchname_arg(int argc, const char **argv, int dwim_new_local_branch_ok, struct branch_info *new, - struct tree **source_tree, - unsigned char rev[20], - const char **new_branch, - int force_detach) + struct checkout_opts *opts, + unsigned char rev[20]) { + struct tree **source_tree = &opts->source_tree; + const char **new_branch = &opts->new_branch; + int force_detach = opts->force_detach; int argcount = 0; unsigned char branch_rev[20]; const char *arg; @@ -1420,9 +1421,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.track == BRANCH_TRACK_UNSPECIFIED && !opts.new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, - &new, &opts.source_tree, - rev, &opts.new_branch, - opts.force_detach); + &new, &opts, rev); argv += n; argc -= n; } -- cgit v1.2.1 From 1d0fa898eaa879d2fa4785164c3261dd40fe5f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 3 Jan 2015 16:41:26 +0700 Subject: checkout: add --ignore-other-wortrees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noticed-by: Mark Levedahl Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/checkout.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'builtin/checkout.c') diff --git a/builtin/checkout.c b/builtin/checkout.c index d8717efb07..8b2bf2093b 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -37,6 +37,7 @@ struct checkout_opts { int writeout_stage; int overwrite_ignore; int ignore_skipworktree; + int ignore_other_worktrees; const char *new_branch; const char *new_branch_force; @@ -1210,7 +1211,8 @@ static int parse_branchname_arg(int argc, const char **argv, int flag; char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) + (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) && + !opts->ignore_other_worktrees) check_linked_checkouts(new); free(head_ref); } @@ -1341,6 +1343,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) N_("second guess 'git checkout no-such-branch'")), OPT_FILENAME(0, "to", &opts.new_worktree, N_("check a branch out in a separate working directory")), + OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees, + N_("do not check if another worktree is holding the given ref")), OPT_END(), }; -- cgit v1.2.1