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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Including functions to get the list of all worktrees, and to get
a specific worktree (primary or linked).

Signed-off-by: Michael Rappazzo <rappazzo@xxxxxxxxx>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]