From: Michael Rappazzo <rappazzo@gmail.com>
To: gitster@pobox.com, sunshine@sunshineco.com, dturner@twopensource.com
Cc: git@vger.kernel.org, Michael Rappazzo <rappazzo@gmail.com>
Subject: [PATCH v7 1/3] worktree: add top-level worktree.c
Date: Fri, 4 Sep 2015 17:39:27 -0400 [thread overview]
Message-ID: <1441402769-35897-2-git-send-email-rappazzo@gmail.com> (raw)
In-Reply-To: <1441402769-35897-1-git-send-email-rappazzo@gmail.com>
Including functions to get the list of all worktrees, and to get
a specific worktree (primary or linked).
Signed-off-by: Michael Rappazzo <rappazzo@gmail.com>
---
Makefile | 1 +
worktree.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
worktree.h | 48 +++++++++++++++++++
3 files changed, 206 insertions(+)
create mode 100644 worktree.c
create mode 100644 worktree.h
diff --git a/Makefile b/Makefile
index e326fa0..0131fed 100644
--- a/Makefile
+++ b/Makefile
@@ -807,6 +807,7 @@ LIB_OBJS += version.o
LIB_OBJS += versioncmp.o
LIB_OBJS += walker.o
LIB_OBJS += wildmatch.o
+LIB_OBJS += worktree.o
LIB_OBJS += wrapper.o
LIB_OBJS += write_or_die.o
LIB_OBJS += ws.o
diff --git a/worktree.c b/worktree.c
new file mode 100644
index 0000000..33d2e57
--- /dev/null
+++ b/worktree.c
@@ -0,0 +1,157 @@
+#include "worktree.h"
+#include "cache.h"
+#include "git-compat-util.h"
+#include "refs.h"
+#include "strbuf.h"
+
+void worktree_release(struct worktree *worktree)
+{
+ if (worktree) {
+ free(worktree->path);
+ free(worktree->git_dir);
+ if (!worktree->is_detached) {
+ /* could be headless */
+ free(worktree->head_ref);
+ }
+ free(worktree);
+ }
+}
+
+void worktree_list_release(struct worktree_list *worktree_list)
+{
+ while (worktree_list) {
+ struct worktree_list *next = worktree_list->next;
+ worktree_release(worktree_list->worktree);
+ free (worktree_list);
+ worktree_list = next;
+ }
+}
+
+/*
+ * read 'path_to_ref' into 'ref'. Also set is_detached to 1 if the ref is detatched
+ *
+ * return 1 if the ref is not a proper ref, 0 otherwise (success)
+ */
+int _parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
+{
+ if (!strbuf_readlink(ref, path_to_ref, 0)) {
+ if (!starts_with(ref->buf, "refs/") || check_refname_format(ref->buf, 0)) {
+ /* invalid ref - something is awry with this repo */
+ return 1;
+ }
+ } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
+ if (starts_with(ref->buf, "ref:")) {
+ strbuf_remove(ref, 0, strlen("ref:"));
+ strbuf_trim(ref);
+ } else if (is_detached) {
+ *is_detached = 1;
+ }
+ }
+ return 0;
+}
+
+struct worktree *get_worktree(const char *id)
+{
+ struct worktree *worktree = NULL;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf worktree_path = STRBUF_INIT;
+ struct strbuf git_dir = STRBUF_INIT;
+ struct strbuf head_ref = STRBUF_INIT;
+ int is_bare = 0;
+ int is_detached = 0;
+
+ if (id) {
+ strbuf_addf(&git_dir, "%s/worktrees/%s", absolute_path(get_git_common_dir()), id);
+ strbuf_addf(&path, "%s/gitdir", git_dir.buf);
+ if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0) {
+ /* invalid git_dir file */
+ goto done;
+ }
+ strbuf_rtrim(&worktree_path);
+ if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
+ strbuf_reset(&worktree_path);
+ strbuf_addstr(&worktree_path, absolute_path("."));
+ strbuf_strip_suffix(&worktree_path, "/.");
+ }
+
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
+ } else {
+ strbuf_addf(&git_dir, "%s", absolute_path(get_git_common_dir()));
+ strbuf_addf(&worktree_path, "%s", absolute_path(get_git_common_dir()));
+ is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
+ if (is_bare)
+ strbuf_strip_suffix(&worktree_path, "/.");
+
+ strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
+ }
+
+ /*
+ * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so for linked worktrees,
+ * `resolve_ref_unsafe()` won't work (it uses git_path). Parse the ref ourselves.
+ */
+ if (_parse_ref(path.buf, &head_ref, &is_detached))
+ goto done;
+
+ worktree = malloc(sizeof(struct worktree));
+ worktree->path = strbuf_detach(&worktree_path, NULL);
+ worktree->git_dir = strbuf_detach(&git_dir, NULL);
+ worktree->is_bare = is_bare;
+ worktree->head_ref = NULL;
+ worktree->is_detached = is_detached;
+ if (strlen(head_ref.buf) > 0) {
+ if (!is_detached) {
+ resolve_ref_unsafe(head_ref.buf, 0, worktree->head_sha1, NULL);
+ worktree->head_ref = strbuf_detach(&head_ref, NULL);
+ } else {
+ get_sha1_hex(head_ref.buf, worktree->head_sha1);
+ }
+ }
+done:
+ strbuf_release(&path);
+ strbuf_release(&git_dir);
+ strbuf_release(&head_ref);
+ strbuf_release(&worktree_path);
+ return worktree;
+}
+
+struct worktree_list *get_worktree_list()
+{
+ struct worktree_list *list = NULL;
+ struct worktree_list *current_entry = NULL;
+ struct worktree *current_worktree = NULL;
+ struct strbuf path = STRBUF_INIT;
+ DIR *dir;
+ struct dirent *d;
+
+ current_worktree = get_worktree(NULL);
+ if (current_worktree) {
+ list = malloc(sizeof(struct worktree_list));
+ list->worktree = current_worktree;
+ list->next = NULL;
+ current_entry = list;
+ }
+ strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
+ dir = opendir(path.buf);
+ strbuf_release(&path);
+ if (dir) {
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ current_worktree = get_worktree(d->d_name);
+ if (current_worktree) {
+ current_entry->next = malloc(sizeof(struct worktree_list));
+ current_entry = current_entry->next;
+ current_entry->worktree = current_worktree;
+ current_entry->next = NULL;
+ }
+ }
+ closedir(dir);
+ }
+
+done:
+
+ return list;
+}
+
diff --git a/worktree.h b/worktree.h
new file mode 100644
index 0000000..2bc0ab8
--- /dev/null
+++ b/worktree.h
@@ -0,0 +1,48 @@
+#ifndef WORKTREE_H
+#define WORKTREE_H
+
+struct worktree {
+ char *path;
+ char *git_dir;
+ char *head_ref;
+ unsigned char head_sha1[20];
+ int is_detached;
+ int is_bare;
+};
+
+struct worktree_list {
+ struct worktree *worktree;
+ struct worktree_list *next;
+};
+
+/* Functions for acting on the information about worktrees. */
+
+/*
+ * Get the list of all worktrees. The primary worktree will always be
+ * the first in the list followed by any other (linked) worktrees created
+ * by `git worktree add`. No specific ordering is done on the linked
+ * worktrees.
+ *
+ * The caller is responsible for freeing the memory from the returned list.
+ * (See worktree_list_release for this purpose).
+ */
+extern struct worktree_list *get_worktree_list();
+
+/*
+ * generic method to get a worktree
+ * - if 'id' is NULL, get the from $GIT_COMMON_DIR
+ * - if 'id' is not NULL, get the worktree found in $GIT_COMMON_DIR/worktrees/id if
+ * such a worktree exists
+ *
+ * The caller is responsible for freeing the memory from the returned
+ * worktree. (See worktree_release for this purpose)
+ */
+struct worktree *get_worktree(const char *id);
+
+/*
+ * Free up the memory for a worktree_list/worktree
+ */
+extern void worktree_list_release(struct worktree_list *);
+extern void worktree_release(struct worktree *);
+
+#endif
--
2.5.0
next prev parent reply other threads:[~2015-09-04 21:39 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-04 21:39 [PATCH v7 0/3] worktree: worktree.c functions and list builtin command Michael Rappazzo
2015-09-04 21:39 ` Michael Rappazzo [this message]
2015-09-10 20:04 ` [PATCH v7 1/3] worktree: add top-level worktree.c Junio C Hamano
2015-09-11 10:33 ` Mike Rappazzo
2015-09-13 2:39 ` Eric Sunshine
2015-09-13 6:27 ` Eric Sunshine
2015-09-14 12:20 ` Mike Rappazzo
2015-09-14 17:41 ` Junio C Hamano
2015-09-16 20:42 ` Eric Sunshine
2015-09-16 20:32 ` Eric Sunshine
2015-09-16 20:49 ` Mike Rappazzo
2015-09-22 1:05 ` Eric Sunshine
2015-09-22 1:17 ` Junio C Hamano
2015-09-04 21:39 ` [PATCH v7 2/3] worktree: move/refactor find_shared_symref from branch.c Michael Rappazzo
2015-09-11 16:16 ` Junio C Hamano
2015-09-11 21:43 ` Mike Rappazzo
2015-09-11 21:52 ` Junio C Hamano
2015-09-11 23:10 ` Eric Sunshine
2015-09-12 2:33 ` Mike Rappazzo
2015-09-13 3:19 ` Eric Sunshine
2015-09-14 17:44 ` Mike Rappazzo
2015-09-16 21:09 ` Eric Sunshine
2015-09-16 21:36 ` Mike Rappazzo
2015-09-22 1:07 ` Eric Sunshine
2015-09-04 21:39 ` [PATCH v7 3/3] worktree: add 'list' command Michael Rappazzo
2015-09-11 22:02 ` Junio C Hamano
2015-09-13 4:25 ` 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=1441402769-35897-2-git-send-email-rappazzo@gmail.com \
--to=rappazzo@gmail.com \
--cc=dturner@twopensource.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).