Commit Diff


commit - f89000039096e46ad7d5f9abfad6749034d02a41
commit + 1ea7ccc6f361b1b07c3f3c301b033ea50b17cf54
blob - 95a7aa49c8f72ab6c542ca943a37c5fc2cca1932
blob + f0ef0abde989951cff823fdaf07ad4a0528aca1f
--- gotadmin/gotadmin.1
+++ gotadmin/gotadmin.1
@@ -68,6 +68,9 @@ are as follows:
 Use the repository at the specified path.
 If not specified, assume the repository is located at or above the current
 working directory.
+If this directory is a
+.Xr got 1
+work tree, use the repository path associated with this work tree.
 .El
 .It Cm pack Oo Fl a Oc Oo Fl r Ar repository-path Oc Oo Fl x Ar reference Oc Op Ar reference ...
 Generate a new pack file and a corresponding pack file index.
@@ -101,6 +104,9 @@ Unless this option is specified, only loose objects wi
 Use the repository at the specified path.
 If not specified, assume the repository is located at or above the current
 working directory.
+If this directory is a
+.Xr got 1
+work tree, use the repository path associated with this work tree.
 .It Fl x Ar reference
 Exclude objects reachable via the specified
 .Ar reference
@@ -274,6 +280,9 @@ remove any files from disk.
 Use the repository at the specified path.
 If not specified, assume the repository is located at or above the current
 working directory.
+If this directory is a
+.Xr got 1
+work tree, use the repository path associated with this work tree.
 .It Fl q
 Suppress progress reporting and disk space summary output.
 .El
blob - a86569641450d3ae54d222474ede59531dbe00ef
blob + 938baf072aa45776f93886d1d0df6671f3de3c56
--- gotadmin/gotadmin.c
+++ gotadmin/gotadmin.c
@@ -41,6 +41,7 @@
 #include "got_path.h"
 #include "got_privsep.h"
 #include "got_opentemp.h"
+#include "got_worktree.h"
 
 #ifndef nitems
 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
@@ -231,10 +232,43 @@ usage_info(void)
 }
 
 static const struct got_error *
+get_repo_path(char **repo_path)
+{
+	const struct got_error *err = NULL;
+	struct got_worktree *worktree = NULL;
+	char *cwd;
+
+	*repo_path = NULL;
+
+	cwd = getcwd(NULL, 0);
+	if (cwd == NULL)
+		return got_error_from_errno("getcwd");
+
+	err = got_worktree_open(&worktree, cwd);
+	if (err) {
+		if (err->code != GOT_ERR_NOT_WORKTREE)
+			goto done;
+		err = NULL;
+	}
+
+	if (worktree)
+		*repo_path = strdup(got_worktree_get_repo_path(worktree));
+	else
+		*repo_path = strdup(cwd);
+	if (*repo_path == NULL)
+		err = got_error_from_errno("strdup");
+done:
+	if (worktree)
+		got_worktree_close(worktree);
+	free(cwd);
+	return err;
+}
+
+static const struct got_error *
 cmd_info(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
-	char *cwd = NULL, *repo_path = NULL;
+	char *repo_path = NULL;
 	struct got_repository *repo = NULL;
 	const struct got_gotconfig *gotconfig = NULL;
 	int ch, npackfiles, npackedobj, nobj;
@@ -264,13 +298,12 @@ cmd_info(int argc, char *argv[])
 	    NULL) == -1)
 		err(1, "pledge");
 #endif
-	cwd = getcwd(NULL, 0);
-	if (cwd == NULL) {
-		error = got_error_from_errno("getcwd");
-		goto done;
+	if (repo_path == NULL) {
+		error = get_repo_path(&repo_path);
+		if (error)
+			goto done;
 	}
-
-	error = got_repo_open(&repo, repo_path ? repo_path : cwd, NULL);
+	error = got_repo_open(&repo, repo_path, NULL);
 	if (error)
 		goto done;
 
@@ -332,7 +365,7 @@ cmd_info(int argc, char *argv[])
 done:
 	if (repo)
 		got_repo_close(repo);
-	free(cwd);
+	free(repo_path);
 	return error;
 }
 
@@ -520,7 +553,7 @@ static const struct got_error *
 cmd_pack(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
-	char *cwd = NULL, *repo_path = NULL;
+	char *repo_path = NULL;
 	struct got_repository *repo = NULL;
 	int ch, i, loose_obj_only = 1;
 	struct got_object_id *pack_hash = NULL;
@@ -570,13 +603,12 @@ cmd_pack(int argc, char *argv[])
 	    NULL) == -1)
 		err(1, "pledge");
 #endif
-	cwd = getcwd(NULL, 0);
-	if (cwd == NULL) {
-		error = got_error_from_errno("getcwd");
-		goto done;
+	if (repo_path == NULL) {
+		error = get_repo_path(&repo_path);
+		if (error)
+			goto done;
 	}
-
-	error = got_repo_open(&repo, repo_path ? repo_path : cwd, NULL);
+	error = got_repo_open(&repo, repo_path, NULL);
 	if (error)
 		goto done;
 
@@ -650,7 +682,7 @@ done:
 	got_ref_list_free(&include_refs);
 	free(id_str);
 	free(pack_hash);
-	free(cwd);
+	free(repo_path);
 	return error;
 }
 
@@ -1001,7 +1033,7 @@ static const struct got_error *
 cmd_cleanup(int argc, char *argv[])
 {
 	const struct got_error *error = NULL;
-	char *cwd = NULL, *repo_path = NULL;
+	char *repo_path = NULL;
 	struct got_repository *repo = NULL;
 	int ch, dry_run = 0, npacked = 0, verbosity = 0;
 	int remove_lonely_packidx = 0, ignore_mtime = 0;
@@ -1049,13 +1081,12 @@ cmd_cleanup(int argc, char *argv[])
 	    NULL) == -1)
 		err(1, "pledge");
 #endif
-	cwd = getcwd(NULL, 0);
-	if (cwd == NULL) {
-		error = got_error_from_errno("getcwd");
-		goto done;
+	if (repo_path == NULL) {
+		error = get_repo_path(&repo_path);
+		if (error)
+			goto done;
 	}
-
-	error = got_repo_open(&repo, repo_path ? repo_path : cwd, NULL);
+	error = got_repo_open(&repo, repo_path, NULL);
 	if (error)
 		goto done;
 
@@ -1120,6 +1151,6 @@ cmd_cleanup(int argc, char *argv[])
 done:
 	if (repo)
 		got_repo_close(repo);
-	free(cwd);
+	free(repo_path);
 	return error;
 }
blob - f3111e0b046c0a015967d9caba4e8e93a57c937d
blob + f9980ca34fe6e0e1c431bd61173a4af6fed70df8
--- lib/worktree.c
+++ lib/worktree.c
@@ -112,70 +112,6 @@ done:
 	if (fclose(tmpfile) == EOF && err == NULL)
 		err = got_error_from_errno2("fclose", tmppath);
 	free(tmppath);
-	return err;
-}
-
-static const struct got_error *
-read_meta_file(char **content, const char *path_got, const char *name)
-{
-	const struct got_error *err = NULL;
-	char *path;
-	int fd = -1;
-	ssize_t n;
-	struct stat sb;
-
-	*content = NULL;
-
-	if (asprintf(&path, "%s/%s", path_got, name) == -1) {
-		err = got_error_from_errno("asprintf");
-		path = NULL;
-		goto done;
-	}
-
-	fd = open(path, O_RDONLY | O_NOFOLLOW);
-	if (fd == -1) {
-		if (errno == ENOENT)
-			err = got_error_path(path, GOT_ERR_WORKTREE_META);
-		else
-			err = got_error_from_errno2("open", path);
-		goto done;
-	}
-	if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
-		err = (errno == EWOULDBLOCK ? got_error(GOT_ERR_WORKTREE_BUSY)
-		    : got_error_from_errno2("flock", path));
-		goto done;
-	}
-
-	if (fstat(fd, &sb) != 0) {
-		err = got_error_from_errno2("fstat", path);
-		goto done;
-	}
-	*content = calloc(1, sb.st_size);
-	if (*content == NULL) {
-		err = got_error_from_errno("calloc");
-		goto done;
-	}
-
-	n = read(fd, *content, sb.st_size);
-	if (n != sb.st_size) {
-		err = (n == -1 ? got_error_from_errno2("read", path) :
-		    got_error_path(path, GOT_ERR_WORKTREE_META));
-		goto done;
-	}
-	if ((*content)[sb.st_size - 1] != '\n') {
-		err = got_error_path(path, GOT_ERR_WORKTREE_META);
-		goto done;
-	}
-	(*content)[sb.st_size - 1] = '\0';
-
-done:
-	if (fd != -1 && close(fd) == -1 && err == NULL)
-		err = got_error_from_errno2("close", path_got);
-	free(path);
-	if (err) {
-		free(*content);
-		*content = NULL;
-	}
 	return err;
 }
 
@@ -318,226 +254,7 @@ done:
 	return err;
 }
 
-static const struct got_error *
-open_worktree(struct got_worktree **worktree, const char *path)
-{
-	const struct got_error *err = NULL;
-	char *path_got;
-	char *formatstr = NULL;
-	char *uuidstr = NULL;
-	char *path_lock = NULL;
-	char *base_commit_id_str = NULL;
-	int version, fd = -1;
-	const char *errstr;
-	struct got_repository *repo = NULL;
-	uint32_t uuid_status;
-
-	*worktree = NULL;
-
-	if (asprintf(&path_got, "%s/%s", path, GOT_WORKTREE_GOT_DIR) == -1) {
-		err = got_error_from_errno("asprintf");
-		path_got = NULL;
-		goto done;
-	}
-
-	if (asprintf(&path_lock, "%s/%s", path_got, GOT_WORKTREE_LOCK) == -1) {
-		err = got_error_from_errno("asprintf");
-		path_lock = NULL;
-		goto done;
-	}
-
-	fd = open(path_lock, O_RDWR | O_EXLOCK | O_NONBLOCK);
-	if (fd == -1) {
-		err = (errno == EWOULDBLOCK ? got_error(GOT_ERR_WORKTREE_BUSY)
-		    : got_error_from_errno2("open", path_lock));
-		goto done;
-	}
-
-	err = read_meta_file(&formatstr, path_got, GOT_WORKTREE_FORMAT);
-	if (err)
-		goto done;
-
-	version = strtonum(formatstr, 1, INT_MAX, &errstr);
-	if (errstr) {
-		err = got_error_msg(GOT_ERR_WORKTREE_META,
-		    "could not parse work tree format version number");
-		goto done;
-	}
-	if (version != GOT_WORKTREE_FORMAT_VERSION) {
-		err = got_error(GOT_ERR_WORKTREE_VERS);
-		goto done;
-	}
-
-	*worktree = calloc(1, sizeof(**worktree));
-	if (*worktree == NULL) {
-		err = got_error_from_errno("calloc");
-		goto done;
-	}
-	(*worktree)->lockfd = -1;
-
-	(*worktree)->root_path = realpath(path, NULL);
-	if ((*worktree)->root_path == NULL) {
-		err = got_error_from_errno2("realpath", path);
-		goto done;
-	}
-	err = read_meta_file(&(*worktree)->repo_path, path_got,
-	    GOT_WORKTREE_REPOSITORY);
-	if (err)
-		goto done;
-
-	err = read_meta_file(&(*worktree)->path_prefix, path_got,
-	    GOT_WORKTREE_PATH_PREFIX);
-	if (err)
-		goto done;
-
-	err = read_meta_file(&base_commit_id_str, path_got,
-	    GOT_WORKTREE_BASE_COMMIT);
-	if (err)
-		goto done;
-
-	err = read_meta_file(&uuidstr, path_got, GOT_WORKTREE_UUID);
-	if (err)
-		goto done;
-	uuid_from_string(uuidstr, &(*worktree)->uuid, &uuid_status);
-	if (uuid_status != uuid_s_ok) {
-		err = got_error_uuid(uuid_status, "uuid_from_string");
-		goto done;
-	}
-
-	err = got_repo_open(&repo, (*worktree)->repo_path, NULL);
-	if (err)
-		goto done;
-
-	err = got_object_resolve_id_str(&(*worktree)->base_commit_id, repo,
-	    base_commit_id_str);
-	if (err)
-		goto done;
-
-	err = read_meta_file(&(*worktree)->head_ref_name, path_got,
-	    GOT_WORKTREE_HEAD_REF);
-	if (err)
-		goto done;
-
-	if (asprintf(&(*worktree)->gotconfig_path, "%s/%s/%s",
-	    (*worktree)->root_path,
-	    GOT_WORKTREE_GOT_DIR, GOT_GOTCONFIG_FILENAME) == -1) {
-		err = got_error_from_errno("asprintf");
-		goto done;
-	}
-
-	err = got_gotconfig_read(&(*worktree)->gotconfig,
-	    (*worktree)->gotconfig_path);
-
-	(*worktree)->root_fd = open((*worktree)->root_path, O_DIRECTORY);
-	if ((*worktree)->root_fd == -1) {
-		err = got_error_from_errno2("open", (*worktree)->root_path);
-		goto done;
-	}
-done:
-	if (repo) {
-		const struct got_error *close_err = got_repo_close(repo);
-		if (err == NULL)
-			err = close_err;
-	}
-	free(path_got);
-	free(path_lock);
-	free(base_commit_id_str);
-	free(uuidstr);
-	free(formatstr);
-	if (err) {
-		if (fd != -1)
-			close(fd);
-		if (*worktree != NULL)
-			got_worktree_close(*worktree);
-		*worktree = NULL;
-	} else
-		(*worktree)->lockfd = fd;
-
-	return err;
-}
-
 const struct got_error *
-got_worktree_open(struct got_worktree **worktree, const char *path)
-{
-	const struct got_error *err = NULL;
-	char *worktree_path;
-
-	worktree_path = strdup(path);
-	if (worktree_path == NULL)
-		return got_error_from_errno("strdup");
-
-	for (;;) {
-		char *parent_path;
-
-		err = open_worktree(worktree, worktree_path);
-		if (err && !(err->code == GOT_ERR_ERRNO && errno == ENOENT)) {
-			free(worktree_path);
-			return err;
-		}
-		if (*worktree) {
-			free(worktree_path);
-			return NULL;
-		}
-		if (worktree_path[0] == '/' && worktree_path[1] == '\0')
-			break;
-		err = got_path_dirname(&parent_path, worktree_path);
-		if (err) {
-			if (err->code != GOT_ERR_BAD_PATH) {
-				free(worktree_path);
-				return err;
-			}
-			break;
-		}
-		free(worktree_path);
-		worktree_path = parent_path;
-	}
-
-	free(worktree_path);
-	return got_error(GOT_ERR_NOT_WORKTREE);
-}
-
-const struct got_error *
-got_worktree_close(struct got_worktree *worktree)
-{
-	const struct got_error *err = NULL;
-
-	if (worktree->lockfd != -1) {
-		if (close(worktree->lockfd) == -1)
-			err = got_error_from_errno2("close",
-			    got_worktree_get_root_path(worktree));
-	}
-	if (close(worktree->root_fd) == -1 && err == NULL)
-		err = got_error_from_errno2("close",
-		    got_worktree_get_root_path(worktree));
-	free(worktree->repo_path);
-	free(worktree->path_prefix);
-	free(worktree->base_commit_id);
-	free(worktree->head_ref_name);
-	free(worktree->root_path);
-	free(worktree->gotconfig_path);
-	got_gotconfig_free(worktree->gotconfig);
-	free(worktree);
-	return err;
-}
-
-const char *
-got_worktree_get_root_path(struct got_worktree *worktree)
-{
-	return worktree->root_path;
-}
-
-const char *
-got_worktree_get_repo_path(struct got_worktree *worktree)
-{
-	return worktree->repo_path;
-}
-const char *
-got_worktree_get_path_prefix(struct got_worktree *worktree)
-{
-	return worktree->path_prefix;
-}
-
-const struct got_error *
 got_worktree_match_path_prefix(int *match, struct got_worktree *worktree,
     const char *path_prefix)
 {
blob - /dev/null
blob + 4a589cf5ece62d780a9e4fac1215b2df6a5ea5cc (mode 644)
--- /dev/null
+++ lib/worktree_open.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/stat.h>
+#include <sys/queue.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <uuid.h>
+
+#include "got_cancel.h"
+#include "got_error.h"
+#include "got_reference.h"
+#include "got_path.h"
+#include "got_worktree.h"
+#include "got_repository.h"
+#include "got_gotconfig.h"
+#include "got_object.h"
+
+#include "got_lib_worktree.h"
+#include "got_lib_gotconfig.h"
+
+static const struct got_error *
+read_meta_file(char **content, const char *path_got, const char *name)
+{
+	const struct got_error *err = NULL;
+	char *path;
+	int fd = -1;
+	ssize_t n;
+	struct stat sb;
+
+	*content = NULL;
+
+	if (asprintf(&path, "%s/%s", path_got, name) == -1) {
+		err = got_error_from_errno("asprintf");
+		path = NULL;
+		goto done;
+	}
+
+	fd = open(path, O_RDONLY | O_NOFOLLOW);
+	if (fd == -1) {
+		if (errno == ENOENT)
+			err = got_error_path(path, GOT_ERR_WORKTREE_META);
+		else
+			err = got_error_from_errno2("open", path);
+		goto done;
+	}
+	if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
+		err = (errno == EWOULDBLOCK ? got_error(GOT_ERR_WORKTREE_BUSY)
+		    : got_error_from_errno2("flock", path));
+		goto done;
+	}
+
+	if (fstat(fd, &sb) != 0) {
+		err = got_error_from_errno2("fstat", path);
+		goto done;
+	}
+	*content = calloc(1, sb.st_size);
+	if (*content == NULL) {
+		err = got_error_from_errno("calloc");
+		goto done;
+	}
+
+	n = read(fd, *content, sb.st_size);
+	if (n != sb.st_size) {
+		err = (n == -1 ? got_error_from_errno2("read", path) :
+		    got_error_path(path, GOT_ERR_WORKTREE_META));
+		goto done;
+	}
+	if ((*content)[sb.st_size - 1] != '\n') {
+		err = got_error_path(path, GOT_ERR_WORKTREE_META);
+		goto done;
+	}
+	(*content)[sb.st_size - 1] = '\0';
+
+done:
+	if (fd != -1 && close(fd) == -1 && err == NULL)
+		err = got_error_from_errno2("close", path_got);
+	free(path);
+	if (err) {
+		free(*content);
+		*content = NULL;
+	}
+	return err;
+}
+
+static const struct got_error *
+open_worktree(struct got_worktree **worktree, const char *path)
+{
+	const struct got_error *err = NULL;
+	char *path_got;
+	char *formatstr = NULL;
+	char *uuidstr = NULL;
+	char *path_lock = NULL;
+	char *base_commit_id_str = NULL;
+	int version, fd = -1;
+	const char *errstr;
+	struct got_repository *repo = NULL;
+	uint32_t uuid_status;
+
+	*worktree = NULL;
+
+	if (asprintf(&path_got, "%s/%s", path, GOT_WORKTREE_GOT_DIR) == -1) {
+		err = got_error_from_errno("asprintf");
+		path_got = NULL;
+		goto done;
+	}
+
+	if (asprintf(&path_lock, "%s/%s", path_got, GOT_WORKTREE_LOCK) == -1) {
+		err = got_error_from_errno("asprintf");
+		path_lock = NULL;
+		goto done;
+	}
+
+	fd = open(path_lock, O_RDWR | O_EXLOCK | O_NONBLOCK);
+	if (fd == -1) {
+		err = (errno == EWOULDBLOCK ? got_error(GOT_ERR_WORKTREE_BUSY)
+		    : got_error_from_errno2("open", path_lock));
+		goto done;
+	}
+
+	err = read_meta_file(&formatstr, path_got, GOT_WORKTREE_FORMAT);
+	if (err)
+		goto done;
+
+	version = strtonum(formatstr, 1, INT_MAX, &errstr);
+	if (errstr) {
+		err = got_error_msg(GOT_ERR_WORKTREE_META,
+		    "could not parse work tree format version number");
+		goto done;
+	}
+	if (version != GOT_WORKTREE_FORMAT_VERSION) {
+		err = got_error(GOT_ERR_WORKTREE_VERS);
+		goto done;
+	}
+
+	*worktree = calloc(1, sizeof(**worktree));
+	if (*worktree == NULL) {
+		err = got_error_from_errno("calloc");
+		goto done;
+	}
+	(*worktree)->lockfd = -1;
+
+	(*worktree)->root_path = realpath(path, NULL);
+	if ((*worktree)->root_path == NULL) {
+		err = got_error_from_errno2("realpath", path);
+		goto done;
+	}
+	err = read_meta_file(&(*worktree)->repo_path, path_got,
+	    GOT_WORKTREE_REPOSITORY);
+	if (err)
+		goto done;
+
+	err = read_meta_file(&(*worktree)->path_prefix, path_got,
+	    GOT_WORKTREE_PATH_PREFIX);
+	if (err)
+		goto done;
+
+	err = read_meta_file(&base_commit_id_str, path_got,
+	    GOT_WORKTREE_BASE_COMMIT);
+	if (err)
+		goto done;
+
+	err = read_meta_file(&uuidstr, path_got, GOT_WORKTREE_UUID);
+	if (err)
+		goto done;
+	uuid_from_string(uuidstr, &(*worktree)->uuid, &uuid_status);
+	if (uuid_status != uuid_s_ok) {
+		err = got_error_uuid(uuid_status, "uuid_from_string");
+		goto done;
+	}
+
+	err = got_repo_open(&repo, (*worktree)->repo_path, NULL);
+	if (err)
+		goto done;
+
+	err = got_object_resolve_id_str(&(*worktree)->base_commit_id, repo,
+	    base_commit_id_str);
+	if (err)
+		goto done;
+
+	err = read_meta_file(&(*worktree)->head_ref_name, path_got,
+	    GOT_WORKTREE_HEAD_REF);
+	if (err)
+		goto done;
+
+	if (asprintf(&(*worktree)->gotconfig_path, "%s/%s/%s",
+	    (*worktree)->root_path,
+	    GOT_WORKTREE_GOT_DIR, GOT_GOTCONFIG_FILENAME) == -1) {
+		err = got_error_from_errno("asprintf");
+		goto done;
+	}
+
+	err = got_gotconfig_read(&(*worktree)->gotconfig,
+	    (*worktree)->gotconfig_path);
+
+	(*worktree)->root_fd = open((*worktree)->root_path, O_DIRECTORY);
+	if ((*worktree)->root_fd == -1) {
+		err = got_error_from_errno2("open", (*worktree)->root_path);
+		goto done;
+	}
+done:
+	if (repo) {
+		const struct got_error *close_err = got_repo_close(repo);
+		if (err == NULL)
+			err = close_err;
+	}
+	free(path_got);
+	free(path_lock);
+	free(base_commit_id_str);
+	free(uuidstr);
+	free(formatstr);
+	if (err) {
+		if (fd != -1)
+			close(fd);
+		if (*worktree != NULL)
+			got_worktree_close(*worktree);
+		*worktree = NULL;
+	} else
+		(*worktree)->lockfd = fd;
+
+	return err;
+}
+
+const struct got_error *
+got_worktree_open(struct got_worktree **worktree, const char *path)
+{
+	const struct got_error *err = NULL;
+	char *worktree_path;
+
+	worktree_path = strdup(path);
+	if (worktree_path == NULL)
+		return got_error_from_errno("strdup");
+
+	for (;;) {
+		char *parent_path;
+
+		err = open_worktree(worktree, worktree_path);
+		if (err && !(err->code == GOT_ERR_ERRNO && errno == ENOENT)) {
+			free(worktree_path);
+			return err;
+		}
+		if (*worktree) {
+			free(worktree_path);
+			return NULL;
+		}
+		if (worktree_path[0] == '/' && worktree_path[1] == '\0')
+			break;
+		err = got_path_dirname(&parent_path, worktree_path);
+		if (err) {
+			if (err->code != GOT_ERR_BAD_PATH) {
+				free(worktree_path);
+				return err;
+			}
+			break;
+		}
+		free(worktree_path);
+		worktree_path = parent_path;
+	}
+
+	free(worktree_path);
+	return got_error(GOT_ERR_NOT_WORKTREE);
+}
+
+const struct got_error *
+got_worktree_close(struct got_worktree *worktree)
+{
+	const struct got_error *err = NULL;
+
+	if (worktree->lockfd != -1) {
+		if (close(worktree->lockfd) == -1)
+			err = got_error_from_errno2("close",
+			    got_worktree_get_root_path(worktree));
+	}
+	if (close(worktree->root_fd) == -1 && err == NULL)
+		err = got_error_from_errno2("close",
+		    got_worktree_get_root_path(worktree));
+	free(worktree->repo_path);
+	free(worktree->path_prefix);
+	free(worktree->base_commit_id);
+	free(worktree->head_ref_name);
+	free(worktree->root_path);
+	free(worktree->gotconfig_path);
+	got_gotconfig_free(worktree->gotconfig);
+	free(worktree);
+	return err;
+}
+
+const char *
+got_worktree_get_root_path(struct got_worktree *worktree)
+{
+	return worktree->root_path;
+}
+
+const char *
+got_worktree_get_repo_path(struct got_worktree *worktree)
+{
+	return worktree->repo_path;
+}
+
+const char *
+got_worktree_get_path_prefix(struct got_worktree *worktree)
+{
+	return worktree->path_prefix;
+}