git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v7 1/3] worktree: add top-level worktree.c

From
Michael Rappazzo <rappazzo@gmail.com>
Date
Sep 4, 2015, 21:39 UTC
Message-ID
<1441402769-35897-2-git-send-email-rappazzo@gmail.com>
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
Previous: Michael RappazzoNext: Junio C Hamano
Message 2 of 27 in “worktree: worktree.c functions and list builtin command”
  1. 0/3 worktree: worktree.c functions and list builtin commandMichael Rappazzo, Sep 4, 2015
  2. 1/3 worktree: add top-level worktree.cMichael Rappazzo, Sep 4, 2015
  3. Junio C HamanoSep 10, 2015
  4. Mike RappazzoSep 11, 2015
  5. Eric SunshineSep 13, 2015
  6. Eric SunshineSep 13, 2015
  7. Mike RappazzoSep 14, 2015
  8. Junio C HamanoSep 14, 2015
  9. Eric SunshineSep 16, 2015
  10. Eric SunshineSep 16, 2015
  11. Mike RappazzoSep 16, 2015
  12. Eric SunshineSep 22, 2015
  13. Junio C HamanoSep 22, 2015
  14. 2/3 worktree: move/refactor find_shared_symref from branch.cMichael Rappazzo, Sep 4, 2015
  15. Junio C HamanoSep 11, 2015
  16. Mike RappazzoSep 11, 2015
  17. Junio C HamanoSep 11, 2015
  18. Eric SunshineSep 11, 2015
  19. Mike RappazzoSep 12, 2015
  20. Eric SunshineSep 13, 2015
  21. Mike RappazzoSep 14, 2015
  22. Eric SunshineSep 16, 2015
  23. Mike RappazzoSep 16, 2015
  24. Eric SunshineSep 22, 2015
  25. 3/3 worktree: add 'list' commandMichael Rappazzo, Sep 4, 2015
  26. Junio C HamanoSep 11, 2015
  27. Eric SunshineSep 13, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.