"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Tracey Emery <tracey@traceyemery.net>
Subject:
Re: make gotadmin run in a work tree
To:
gameoftrees@openbsd.org
Date:
Mon, 15 Nov 2021 07:49:04 -0700

Download raw body.

Thread
On Sun, Nov 14, 2021 at 11:37:31AM +0100, Stefan Sperling wrote:
> On Sun, Nov 14, 2021 at 11:23:59AM +0100, Stefan Sperling wrote:
> > gotweb benefits a little as well by shedding some weight, as in
> > linking less unused worktree code into the binary.
> 
> Actually, gotweb doesn't need any worktree.c code at all. I have
> removed worktree.c from its list of source files on the main branch.

Glad you caught this :)

> 
> Here is a rebased patch which ignores the gotweb side of things.
> 

ok!

> -----------------------------------------------
> commit 1813a7c09b4ab5f28fa7c139625db5f4362ce61c (gotadmin-worktree)
> from: Stefan Sperling <stsp@stsp.name>
> date: Sun Nov 14 10:35:31 2021 UTC
>  
>  let gotadmin find the repository automatically if invoked in a work tree
>  
>  Move a small amount of code from worktree.c to a new file worktree_open.c,
>  which contains everything required to open and close a work tree and inspect
>  some of its basic parameters. This can be used by gotadmin.
>  
> diff d34cb66cb9f25f0f06b250b99d9aa3de97c3cb7d 9e8331df1736d9c4835ad8d2419d1fe9c3642215
> blob - 4b9ae8539216e6a771ac12438cc2e7bb79a42ced
> blob + 7e44f914b454fcd320fd03610f840109df3f7539
> --- got/Makefile
> +++ got/Makefile
> @@ -7,7 +7,7 @@ SRCS=		got.c blame.c commit_graph.c delta.c diff.c \
>  		diffreg.c error.c fileindex.c object.c object_cache.c \
>  		object_idset.c object_parse.c opentemp.c path.c pack.c \
>  		privsep.c reference.c repository.c sha1.c worktree.c \
> -		inflate.c buf.c rcsutil.c diff3.c lockfile.c \
> +		worktree_open.c inflate.c buf.c rcsutil.c diff3.c lockfile.c \
>  		deflate.c object_create.c delta_cache.c fetch.c \
>  		gotconfig.c diff_main.c diff_atomize_text.c \
>  		diff_myers.c diff_output.c diff_output_plain.c \
> blob - e40715b4c34c91855cc2dd46670ca0ba712541a6
> blob + 47d9d105cc92076d9e33ac2abdf1adf89ab25dc9
> --- gotadmin/Makefile
> +++ gotadmin/Makefile
> @@ -8,7 +8,7 @@ SRCS=		gotadmin.c \
>  		inflate.c lockfile.c object.c object_cache.c object_create.c \
>  		object_idset.c object_parse.c opentemp.c pack.c pack_create.c \
>  		path.c privsep.c reference.c repository.c repository_admin.c \
> -		sha1.c bloom.c murmurhash2.c
> +		worktree_open.c sha1.c bloom.c murmurhash2.c
>  MAN =		${PROG}.1
>  
>  CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib
> 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 - b3c2e764d6748d711145b67ef6017d4d695e00b7
> blob + 4a20c98ded30054a59e95d5cd5fc9f422d019da1
> --- gotadmin/gotadmin.c
> +++ gotadmin/gotadmin.c
> @@ -42,6 +42,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]))
> @@ -232,10 +233,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;
> @@ -265,13 +299,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;
>  
> @@ -333,7 +366,7 @@ cmd_info(int argc, char *argv[])
>  done:
>  	if (repo)
>  		got_repo_close(repo);
> -	free(cwd);
> +	free(repo_path);
>  	return error;
>  }
>  
> @@ -521,7 +554,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;
> @@ -571,13 +604,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;
>  
> @@ -651,7 +683,7 @@ done:
>  	got_ref_list_free(&include_refs);
>  	free(id_str);
>  	free(pack_hash);
> -	free(cwd);
> +	free(repo_path);
>  	return error;
>  }
>  
> @@ -1002,7 +1034,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;
> @@ -1050,13 +1082,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;
>  
> @@ -1121,6 +1152,6 @@ cmd_cleanup(int argc, char *argv[])
>  done:
>  	if (repo)
>  		got_repo_close(repo);
> -	free(cwd);
> +	free(repo_path);
>  	return error;
>  }
> blob - 8b445649ab78fb4dde26c6c0d79295a825c10599
> blob + 31c6214885278c6568978931a3f60491140afca9
> --- lib/worktree.c
> +++ lib/worktree.c
> @@ -118,70 +118,6 @@ done:
>  }
>  
>  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 *
>  write_head_ref(const char *path_got, struct got_reference *head_ref)
>  {
>  	const struct got_error *err = NULL;
> @@ -320,226 +256,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;
> +}
> blob - f7072d7a4b02817f653b795674c32ae7feabcc0b
> blob + ba79d5e787ada9939dea4f62aae062cea501f845
> --- tog/Makefile
> +++ tog/Makefile
> @@ -7,7 +7,7 @@ SRCS=		tog.c blame.c commit_graph.c delta.c diff.c \
>  		diffreg.c error.c fileindex.c object.c object_cache.c \
>  		object_idset.c object_parse.c opentemp.c path.c pack.c \
>  		privsep.c reference.c repository.c sha1.c worktree.c \
> -		utf8.c inflate.c buf.c rcsutil.c diff3.c \
> +		worktree_open.c utf8.c inflate.c buf.c rcsutil.c diff3.c \
>  		lockfile.c deflate.c object_create.c delta_cache.c \
>  		gotconfig.c diff_main.c diff_atomize_text.c \
>  		diff_myers.c diff_output.c diff_output_plain.c \

-- 

Tracey Emery