t9107-git-svn-migrate.sh: use the $( ... ) construct for command substitution
[gitweb.git] / builtin / worktree.c
index 79d088c4a891066b65dc2edb0de2a46ae2d5c8b0..475b9581a5583166c43199f1662b510842c59c7d 100644 (file)
@@ -7,10 +7,14 @@
 #include "refs.h"
 #include "run-command.h"
 #include "sigchain.h"
+#include "refs.h"
+#include "utf8.h"
+#include "worktree.h"
 
 static const char * const worktree_usage[] = {
-       N_("git worktree add [<options>] <path> <branch>"),
+       N_("git worktree add [<options>] <path> [<branch>]"),
        N_("git worktree prune [<options>]"),
+       N_("git worktree list [<options>]"),
        NULL
 };
 
@@ -192,7 +196,6 @@ static int add_worktree(const char *path, const char *refname,
        int counter = 0, len, ret;
        struct strbuf symref = STRBUF_INIT;
        struct commit *commit = NULL;
-       unsigned char rev[20];
 
        if (file_exists(path) && !is_empty_dir(path))
                die(_("'%s' already exists"), path);
@@ -238,7 +241,7 @@ static int add_worktree(const char *path, const char *refname,
         * after the preparation is over.
         */
        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
-       write_file(sb.buf, 1, "initializing\n");
+       write_file(sb.buf, "initializing");
 
        strbuf_addf(&sb_git, "%s/.git", path);
        if (safe_create_leading_directories_const(sb_git.buf))
@@ -248,32 +251,25 @@ static int add_worktree(const char *path, const char *refname,
 
        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",
+       write_file(sb.buf, "%s", real_path(sb_git.buf));
+       write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
                   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. Moreover, HEAD
-        * in the new worktree must resolve to the same value as HEAD in
-        * the current tree since the command invoked to populate the new
-        * worktree will be handed the branch/ref specified by the user.
-        * For instance, if the user asks for the new worktree to be based
-        * at HEAD~5, then the resolved HEAD~5 in the new worktree must
-        * match the resolved HEAD~5 in the current tree in order to match
-        * the user's expectation.
+        * or is_git_directory() will reject the directory. Any value which
+        * looks like an object ID will do since it will be immediately
+        * replaced by the symbolic-ref or update-ref invocation in the new
+        * worktree.
         */
-       if (!resolve_ref_unsafe("HEAD", 0, rev, NULL))
-               die(_("unable to resolve HEAD"));
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-       write_file(sb.buf, 1, "%s\n", sha1_to_hex(rev));
+       write_file(sb.buf, "0000000000000000000000000000000000000000");
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
-       write_file(sb.buf, 1, "../..\n");
+       write_file(sb.buf, "../..");
 
        fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name);
 
-       setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
        argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
        argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
        memset(&cp, 0, sizeof(cp));
@@ -281,7 +277,7 @@ static int add_worktree(const char *path, const char *refname,
 
        if (commit)
                argv_array_pushl(&cp.args, "update-ref", "HEAD",
-                                sha1_to_hex(commit->object.sha1), NULL);
+                                oid_to_hex(&commit->object.oid), NULL);
        else
                argv_array_pushl(&cp.args, "symbolic-ref", "HEAD",
                                 symref.buf, NULL);
@@ -292,7 +288,7 @@ static int add_worktree(const char *path, const char *refname,
 
        cp.argv = NULL;
        argv_array_clear(&cp.args);
-       argv_array_push(&cp.args, "checkout");
+       argv_array_pushl(&cp.args, "reset", "--hard", NULL);
        cp.env = child_env.argv;
        ret = run_command(&cp);
        if (!ret) {
@@ -366,6 +362,89 @@ static int add(int ac, const char **av, const char *prefix)
        return add_worktree(path, branch, &opts);
 }
 
+static void show_worktree_porcelain(struct worktree *wt)
+{
+       printf("worktree %s\n", wt->path);
+       if (wt->is_bare)
+               printf("bare\n");
+       else {
+               printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
+               if (wt->is_detached)
+                       printf("detached\n");
+               else
+                       printf("branch %s\n", wt->head_ref);
+       }
+       printf("\n");
+}
+
+static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int cur_path_len = strlen(wt->path);
+       int path_adj = cur_path_len - utf8_strwidth(wt->path);
+
+       strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path);
+       if (wt->is_bare)
+               strbuf_addstr(&sb, "(bare)");
+       else {
+               strbuf_addf(&sb, "%-*s ", abbrev_len,
+                               find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
+               if (!wt->is_detached)
+                       strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
+               else
+                       strbuf_addstr(&sb, "(detached HEAD)");
+       }
+       printf("%s\n", sb.buf);
+
+       strbuf_release(&sb);
+}
+
+static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
+{
+       int i;
+
+       for (i = 0; wt[i]; i++) {
+               int sha1_len;
+               int path_len = strlen(wt[i]->path);
+
+               if (path_len > *maxlen)
+                       *maxlen = path_len;
+               sha1_len = strlen(find_unique_abbrev(wt[i]->head_sha1, *abbrev));
+               if (sha1_len > *abbrev)
+                       *abbrev = sha1_len;
+       }
+}
+
+static int list(int ac, const char **av, const char *prefix)
+{
+       int porcelain = 0;
+
+       struct option options[] = {
+               OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
+               OPT_END()
+       };
+
+       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       if (ac)
+               usage_with_options(worktree_usage, options);
+       else {
+               struct worktree **worktrees = get_worktrees();
+               int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
+
+               if (!porcelain)
+                       measure_widths(worktrees, &abbrev, &path_maxlen);
+
+               for (i = 0; worktrees[i]; i++) {
+                       if (porcelain)
+                               show_worktree_porcelain(worktrees[i]);
+                       else
+                               show_worktree(worktrees[i], path_maxlen, abbrev);
+               }
+               free_worktrees(worktrees);
+       }
+       return 0;
+}
+
 int cmd_worktree(int ac, const char **av, const char *prefix)
 {
        struct option options[] = {
@@ -378,5 +457,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
                return add(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "prune"))
                return prune(ac - 1, av + 1, prefix);
+       if (!strcmp(av[1], "list"))
+               return list(ac - 1, av + 1, prefix);
        usage_with_options(worktree_usage, options);
 }