fetch-pack.c: use oidset to check existence of loose object
[gitweb.git] / builtin / worktree.c
index 9591f10442226753b128c8e9f567c7f6d206b2bc..4e7c98758fcfb93c19e3dbf58fc3167829e998b8 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "checkout.h"
 #include "config.h"
 #include "builtin.h"
 #include "dir.h"
@@ -13,7 +14,7 @@
 #include "worktree.h"
 
 static const char * const worktree_usage[] = {
-       N_("git worktree add [<options>] <path> [<branch>]"),
+       N_("git worktree add [<options>] <path> [<commit-ish>]"),
        N_("git worktree list [<options>]"),
        N_("git worktree lock [<options>] <path>"),
        N_("git worktree prune [<options>]"),
@@ -32,8 +33,19 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
+static int guess_remote;
 static timestamp_t expire;
 
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "worktree.guessremote")) {
+               guess_remote = git_config_bool(var, value);
+               return 0;
+       }
+
+       return git_default_config(var, value, cb);
+}
+
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
        struct stat st;
@@ -333,9 +345,23 @@ static int add_worktree(const char *path, const char *refname,
         * Hook failure does not warrant worktree deletion, so run hook after
         * is_junk is cleared, but do return appropriate code when hook fails.
         */
-       if (!ret && opts->checkout)
-               ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
-                                 oid_to_hex(&commit->object.oid), "1", NULL);
+       if (!ret && opts->checkout) {
+               const char *hook = find_hook("post-checkout");
+               if (hook) {
+                       const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
+                       cp.git_cmd = 0;
+                       cp.no_stdin = 1;
+                       cp.stdout_to_stderr = 1;
+                       cp.dir = path;
+                       cp.env = env;
+                       cp.argv = NULL;
+                       argv_array_pushl(&cp.args, absolute_path(hook),
+                                        oid_to_hex(&null_oid),
+                                        oid_to_hex(&commit->object.oid),
+                                        "1", NULL);
+                       ret = run_command(&cp);
+               }
+       }
 
        argv_array_clear(&child_env);
        strbuf_release(&sb);
@@ -351,6 +377,7 @@ static int add(int ac, const char **av, const char *prefix)
        const char *new_branch_force = NULL;
        char *path;
        const char *branch;
+       const char *opt_track = NULL;
        struct option options[] = {
                OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -360,6 +387,11 @@ static int add(int ac, const char **av, const char *prefix)
                OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
                OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+               OPT_PASSTHRU(0, "track", &opt_track, NULL,
+                            N_("set up tracking mode (see git-branch(1))"),
+                            PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
+               OPT_BOOL(0, "guess-remote", &guess_remote,
+                        N_("try to match the new branch name with a remote-tracking branch")),
                OPT_END()
        };
 
@@ -394,6 +426,28 @@ static int add(int ac, const char **av, const char *prefix)
                int n;
                const char *s = worktree_basename(path, &n);
                opts.new_branch = xstrndup(s, n);
+               if (guess_remote) {
+                       struct object_id oid;
+                       const char *remote =
+                               unique_tracking_name(opts.new_branch, &oid);
+                       if (remote)
+                               branch = remote;
+               }
+       }
+
+       if (ac == 2 && !opts.new_branch && !opts.detach) {
+               struct object_id oid;
+               struct commit *commit;
+               const char *remote;
+
+               commit = lookup_commit_reference_by_name(branch);
+               if (!commit) {
+                       remote = unique_tracking_name(branch, &oid);
+                       if (remote) {
+                               opts.new_branch = branch;
+                               branch = remote;
+                       }
+               }
        }
 
        if (opts.new_branch) {
@@ -404,9 +458,13 @@ static int add(int ac, const char **av, const char *prefix)
                        argv_array_push(&cp.args, "--force");
                argv_array_push(&cp.args, opts.new_branch);
                argv_array_push(&cp.args, branch);
+               if (opt_track)
+                       argv_array_push(&cp.args, opt_track);
                if (run_command(&cp))
                        return -1;
                branch = opts.new_branch;
+       } else if (opt_track) {
+               die(_("--[no-]track can only be used if a new branch is created"));
        }
 
        UNLEAK(path);
@@ -567,7 +625,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
                OPT_END()
        };
 
-       git_config(git_default_config, NULL);
+       git_config(git_worktree_config, NULL);
 
        if (ac < 2)
                usage_with_options(worktree_usage, options);