From: Michael Rappazzo <rappazzo@gmail.com>
To: gitster@pobox.com, sunshine@sunshineco.com
Cc: git@vger.kernel.org, Michael Rappazzo <rappazzo@gmail.com>
Subject: [PATCH v2] worktree: list operation
Date: Sat, 8 Aug 2015 20:19:59 -0400 [thread overview]
Message-ID: <1439079599-87904-2-git-send-email-rappazzo@gmail.com> (raw)
In-Reply-To: <1439079599-87904-1-git-send-email-rappazzo@gmail.com>
'git worktree list' will list the main worktree followed by any linked
worktrees which were created using 'git worktree add'. The option
'--main-only' will restrict the list to only the main worktree.
---
Documentation/git-worktree.txt | 9 ++++-
builtin/worktree.c | 84 ++++++++++++++++++++++++++++++++++++++----
t/t2027-worktree-list.sh | 68 ++++++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+), 9 deletions(-)
create mode 100755 t/t2027-worktree-list.sh
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 3387e2f..2b6b543 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -11,6 +11,7 @@ SYNOPSIS
[verse]
'git worktree add' [-f] [--detach] [-b <new-branch>] <path> [<branch>]
'git worktree prune' [-n] [-v] [--expire <expire>]
+'git worktree list' [--main-only]
DESCRIPTION
-----------
@@ -59,6 +60,10 @@ prune::
Prune working tree information in $GIT_DIR/worktrees.
+list::
+
+List the main worktree followed by all of the linked worktrees.
+
OPTIONS
-------
@@ -86,6 +91,9 @@ OPTIONS
With `prune`, do not remove anything; just report what it would
remove.
+--main-only::
+ With `list`, only list the main worktree.
+
-v::
--verbose::
With `prune`, report all removals.
@@ -167,7 +175,6 @@ performed manually, such as:
- `remove` to remove a linked worktree and its administrative files (and
warn if the worktree is dirty)
- `mv` to move or rename a worktree and update its administrative files
-- `list` to list linked worktrees
- `lock` to prevent automatic pruning of administrative files (for instance,
for a worktree on a portable device)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 6a264ee..8c4a82a 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -10,6 +10,7 @@
static const char * const worktree_usage[] = {
N_("git worktree add [<options>] <path> <branch>"),
N_("git worktree prune [<options>]"),
+ N_("git worktree list [<options>]"),
NULL
};
@@ -36,7 +37,7 @@ static int prune_worktree(const char *id, struct strbuf *reason)
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
if (fd < 0) {
strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
- id, strerror(errno));
+ id, strerror(errno));
return 1;
}
len = st.st_size;
@@ -59,7 +60,7 @@ static int prune_worktree(const char *id, struct strbuf *reason)
* accessed since?
*/
if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
- st_link.st_nlink > 1)
+ st_link.st_nlink > 1)
return 0;
if (st.st_mtime <= expire) {
strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
@@ -187,11 +188,11 @@ static int add_worktree(const char *path, const char **child_argv)
name = worktree_basename(path, &len);
strbuf_addstr(&sb_repo,
- git_path("worktrees/%.*s", (int)(path + len - name), name));
+ 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);
+ sb_repo.buf);
while (!stat(sb_repo.buf, &st)) {
counter++;
strbuf_setlen(&sb_repo, len);
@@ -218,14 +219,14 @@ static int add_worktree(const char *path, const char **child_argv)
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);
+ sb_git.buf);
junk_work_tree = xstrdup(path);
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);
+ 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
@@ -280,9 +281,9 @@ static int add(int ac, const char **av, const char *prefix)
struct option options[] = {
OPT__FORCE(&force, N_("checkout <branch> even if already checked out in other worktree")),
OPT_STRING('b', NULL, &new_branch, N_("branch"),
- N_("create a new branch")),
+ N_("create a new branch")),
OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
- N_("create or reset a branch")),
+ N_("create or reset a branch")),
OPT_BOOL(0, "detach", &detach, N_("detach HEAD at named commit")),
OPT_END()
};
@@ -316,6 +317,71 @@ static int add(int ac, const char **av, const char *prefix)
return add_worktree(path, cmd.argv);
}
+static int list(int ac, const char **av, const char *prefix)
+{
+ int main_only = 0;
+ struct option options[] = {
+ OPT_BOOL(0, "main-only", &main_only, N_("only list the main worktree")),
+ OPT_END()
+ };
+
+ ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+ if (ac)
+ usage_with_options(worktree_usage, options);
+
+ const char *work_tree;
+ work_tree = get_git_work_tree();
+ if (!work_tree)
+ die("This operation must be run in a work tree");
+
+ struct strbuf worktree_git_path = STRBUF_INIT;
+ strbuf_addf(&worktree_git_path, _("%s/.git"), work_tree);
+
+ struct strbuf main_work_tree = STRBUF_INIT;
+ if (is_directory(worktree_git_path.buf)) {
+ /* This is the main tree */
+ strbuf_addstr(&main_work_tree, work_tree);
+ } else {
+ const char *git_dir = get_git_dir();
+ strbuf_addf(&main_work_tree, "%.*s", (int)(strstr(git_dir, "/.git/") - git_dir), git_dir);
+ }
+ printf("%s\n", main_work_tree.buf);
+
+ if (!main_only) {
+ chdir( main_work_tree.buf );
+ if ( is_directory(git_path("worktrees")) ) {
+ DIR *dir = opendir( git_path("worktrees") );
+ if (dir != NULL) {
+ struct dirent *d;
+ struct stat st;
+ char *path;
+ int fd, len;
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+ if (stat(git_path("worktrees/%s/gitdir", d->d_name), &st))
+ continue;
+ fd = open(git_path("worktrees/%s/gitdir", d->d_name), O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ len = st.st_size;
+ path = xmalloc(len + 1);
+ read_in_full(fd, path, len);
+ close(fd);
+
+ printf("%.*s\n", (int)(strstr(path, "/.git") - path), path);
+ free(path);
+ }
+ }
+ closedir(dir);
+ }
+ }
+ strbuf_release(&main_work_tree);
+ strbuf_release(&worktree_git_path);
+ return 0;
+}
+
int cmd_worktree(int ac, const char **av, const char *prefix)
{
struct option options[] = {
@@ -328,5 +394,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);
}
diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh
new file mode 100755
index 0000000..998b34f
--- /dev/null
+++ b/t/t2027-worktree-list.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='test git worktree list'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init
+'
+
+
+test_expect_success '"list" all worktrees from main' '
+ orig_path=$PWD &&
+ git rev-parse --show-toplevel >expect &&
+ git worktree add --detach here master &&
+ (
+ cd here &&
+ git rev-parse --show-toplevel >>"$orig_path/expect" &&
+ cd "$orig_path" &&
+ git worktree list >actual &&
+ test_cmp expect actual &&
+ rm -rf here &&
+ git worktree prune
+ )
+'
+test_expect_success '"list" all worktrees from linked' '
+ orig_path=$PWD &&
+ git rev-parse --show-toplevel >expect &&
+ git worktree add --detach here master &&
+ (
+ cd here &&
+ git rev-parse --show-toplevel >>"$orig_path/expect" &&
+ git worktree list >actual &&
+ test_cmp "$orig_path/expect" actual &&
+ cd "$orig_path" &&
+ rm -rf here &&
+ git worktree prune
+ )
+'
+
+test_expect_success '"list" main worktree from main' '
+ orig_path=$PWD &&
+ git rev-parse --show-toplevel >expect &&
+ git worktree add --detach here master &&
+ (
+ cd here &&
+ cd "$orig_path" &&
+ git worktree list --main-only >actual &&
+ test_cmp expect actual &&
+ rm -rf here &&
+ git worktree prune
+ )
+'
+test_expect_success '"list" main worktree from linked' '
+ orig_path=$PWD &&
+ git rev-parse --show-toplevel >expect &&
+ git worktree add --detach here master &&
+ (
+ cd here &&
+ git worktree list --main-only >actual &&
+ test_cmp "$orig_path/expect" actual &&
+ cd "$orig_path" &&
+ rm -rf here &&
+ git worktree prune
+ )
+'
+
+test_done
--
2.5.0
next prev parent reply other threads:[~2015-08-09 0:20 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-08-09 0:19 [PATCH v2] worktree: list operation Michael Rappazzo
2015-08-09 0:19 ` Michael Rappazzo [this message]
2015-08-09 6:51 ` Andreas Schwab
2015-08-09 7:45 ` Eric Sunshine
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1439079599-87904-2-git-send-email-rappazzo@gmail.com \
--to=rappazzo@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=sunshine@sunshineco.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.