Commit Diff


commit - e2e879a0865a8984188e95cb05b86191c8cfd59b
commit + 0cd1c46a9e4b3e3a7c7f2947c3eded6fb5439516
blob - 0e10c1f50cce74da7725fe1a23d46eafa730aa8b
blob + ef9b7d3b627e23b44594955f5c68ea31aff13220
--- got/got.c
+++ got/got.c
@@ -364,7 +364,7 @@ cmd_checkout(int argc, char *argv[])
 	} else
 		usage_checkout();
 
-	error = apply_unveil(repo_path, 1, worktree_path);
+	error = apply_unveil(repo_path, 0, worktree_path);
 	if (error)
 		goto done;
 
blob - 1d766a09291a9b72e2f9f336bad668cc74a37885
blob + f28495fa63bafdcb674db7376c3f74985866eaaa
--- lib/got_lib_path.h
+++ lib/got_lib_path.h
@@ -86,3 +86,6 @@ const struct got_error *got_pathlist_insert(struct got
 
 /* Free resources allocated for a path list. */
 void got_pathlist_free(struct got_pathlist_head *);
+
+/* Attempt to create a directory at a given path. */
+const struct got_error *got_path_mkdir(const char *);
blob - a8227e282bac5fa313746e559af0e6b4555deefe
blob + 44e57dfaa01aeaa6c43be04e12a7c9931a6c98c1
--- lib/got_lib_worktree.h
+++ lib/got_lib_worktree.h
@@ -47,3 +47,5 @@ struct got_worktree {
 
 #define GOT_WORKTREE_FORMAT_VERSION	1
 #define GOT_WORKTREE_INVALID_COMMIT_ID	GOT_SHA1_STRING_ZERO
+
+#define GOT_WORKTREE_BASE_REF_PREFIX "got/worktree-base"
blob - 8deccf40930398276c6f6880a025fb54ea2c1ca4
blob + e4c1921409c815ed610bff7618c0c7964b4c8d49
--- lib/path.c
+++ lib/path.c
@@ -16,8 +16,11 @@
  */
 
 #include <sys/queue.h>
+#include <sys/stat.h>
 
+#include <errno.h>
 #include <limits.h>
+#include <libgen.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -263,5 +266,63 @@ got_pathlist_free(struct got_pathlist_head *pathlist)
 	while ((pe = TAILQ_FIRST(pathlist)) != NULL) {
 		TAILQ_REMOVE(pathlist, pe, entry);
 		free(pe);
+	}
+}
+
+static const struct got_error *
+make_parent_dirs(const char *abspath)
+{
+	const struct got_error *err = NULL;
+
+	char *parent = dirname(abspath);
+	if (parent == NULL)
+		return NULL;
+
+	if (mkdir(parent, GOT_DEFAULT_DIR_MODE) == -1) {
+		if (errno == ENOENT) {
+			err = make_parent_dirs(parent);
+			if (err)
+				return err;
+			if (mkdir(parent, GOT_DEFAULT_DIR_MODE) == -1)
+				return got_error_from_errno();
+		} else
+			err = got_error_from_errno();
 	}
+
+	return err;
 }
+
+const struct got_error *
+got_path_mkdir(const char *abspath)
+{
+	const struct got_error *err = NULL;
+
+	if (mkdir(abspath, GOT_DEFAULT_DIR_MODE) == -1) {
+		struct stat sb;
+
+		if (errno == EEXIST) {
+			if (lstat(abspath, &sb) == -1) {
+				err = got_error_from_errno();
+				goto done;
+			}
+
+			if (!S_ISDIR(sb.st_mode)) {
+				/* TODO directory is obstructed; do something */
+				err = got_error(GOT_ERR_FILE_OBSTRUCTED);
+				goto done;
+			}
+
+			return NULL;
+		} else if (errno == ENOENT) {
+			err = make_parent_dirs(abspath);
+			if (err)
+				goto done;
+			if (mkdir(abspath, GOT_DEFAULT_DIR_MODE) == -1)
+				err = got_error_from_errno();
+		} else
+			err = got_error_from_errno();
+	}
+
+done:
+	return err;
+}
blob - f66bd478a3c05946cd2b7a0efb4a82334c51fad0
blob + 0dd31b244f9c1bd18003cfa2eee85256fa951db5
--- lib/reference.c
+++ lib/reference.c
@@ -28,6 +28,7 @@
 #include <util.h>
 #include <zlib.h>
 #include <time.h>
+#include <libgen.h>
 
 #include "got_error.h"
 #include "got_object.h"
@@ -741,8 +742,24 @@ got_ref_write(struct got_reference *ref, struct got_re
 
 	err = got_opentemp_named(&tmppath, &f, path);
 	if (f == NULL) {
-		err = got_error_from_errno();
-		goto done;
+		char *parent;
+		if (errno != ENOENT) {
+			err = got_error_from_errno();
+			goto done;
+		}
+		parent = dirname(path);
+		if (parent == NULL) {
+			err = got_error_from_errno();
+			goto done;
+		}
+		err = got_path_mkdir(parent);
+		if (err)
+			goto done;
+		err = got_opentemp_named(&tmppath, &f, path);
+		if (f == NULL) {
+			err = got_error_from_errno();
+			goto done;
+		}
 	}
 
 	if (ref->flags & GOT_REF_IS_SYMBOLIC) {
@@ -771,9 +788,12 @@ got_ref_write(struct got_reference *ref, struct got_re
 
 	/* XXX: check if old content matches our expectations? */
 
-	if (stat(path, &sb) != 0 && errno != ENOENT) {
-		err = got_error_from_errno();
-		goto done;
+	if (stat(path, &sb) != 0) {
+		if (errno != ENOENT) {
+			err = got_error_from_errno();
+			goto done;
+		}
+		sb.st_mode = GOT_DEFAULT_FILE_MODE;
 	}
 
 	if (rename(tmppath, path) != 0) {
blob - c31c617a30892baa3483b16afaa9cab24260757a
blob + b42d708e36334e931b05947d1446757aa3433d93
--- lib/worktree.c
+++ lib/worktree.c
@@ -369,7 +369,7 @@ open_worktree(struct got_worktree **worktree, const ch
 	}
 	(*worktree)->lockfd = -1;
 
-	(*worktree)->root_path = strdup(path);
+	(*worktree)->root_path = realpath(path, NULL);
 	if ((*worktree)->root_path == NULL) {
 		err = got_error_from_errno();
 		goto done;
@@ -575,29 +575,6 @@ lock_worktree(struct got_worktree *worktree, int opera
 		return (errno == EWOULDBLOCK ? got_error(GOT_ERR_WORKTREE_BUSY)
 		    : got_error_from_errno());
 	return NULL;
-}
-
-static const struct got_error *
-make_parent_dirs(const char *abspath)
-{
-	const struct got_error *err = NULL;
-
-	char *parent = dirname(abspath);
-	if (parent == NULL)
-		return NULL;
-
-	if (mkdir(parent, GOT_DEFAULT_DIR_MODE) == -1) {
-		if (errno == ENOENT) {
-			err = make_parent_dirs(parent);
-			if (err)
-				return err;
-			if (mkdir(parent, GOT_DEFAULT_DIR_MODE) == -1)
-				return got_error_from_errno();
-		} else
-			err = got_error_from_errno();
-	}
-
-	return err;
 }
 
 static const struct got_error *
@@ -609,34 +586,7 @@ add_dir_on_disk(struct got_worktree *worktree, const c
 	if (asprintf(&abspath, "%s/%s", worktree->root_path, path) == -1)
 		return got_error_from_errno();
 
-	/* XXX queue work rather than editing disk directly? */
-	if (mkdir(abspath, GOT_DEFAULT_DIR_MODE) == -1) {
-		struct stat sb;
-
-		if (errno == EEXIST) {
-			if (lstat(abspath, &sb) == -1) {
-				err = got_error_from_errno();
-				goto done;
-			}
-
-			if (!S_ISDIR(sb.st_mode)) {
-				/* TODO directory is obstructed; do something */
-				err = got_error(GOT_ERR_FILE_OBSTRUCTED);
-				goto done;
-			}
-
-			return NULL;
-		} else if (errno == ENOENT) {
-			err = make_parent_dirs(abspath);
-			if (err)
-				goto done;
-			if (mkdir(abspath, GOT_DEFAULT_DIR_MODE) == -1)
-				err = got_error_from_errno();
-		} else
-			err = got_error_from_errno();
-	}
-
-done:
+	err = got_path_mkdir(abspath);
 	free(abspath);
 	return err;
 }
@@ -1218,9 +1168,57 @@ diff_new(void *arg, struct got_tree_entry *te, const c
 		    a->cancel_cb, a->cancel_arg);
 
 	free(path);
+	return err;
+}
+
+/*
+ * Prevent Git's garbage collector from deleting our base commit by
+ * setting a reference to our base commit's ID.
+ */
+static const struct got_error *
+ref_base_commit(struct got_worktree *worktree, struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct got_reference *ref = NULL;
+	const char *root_path;
+	char *refname = NULL, *uuidstr = NULL, *s;
+	uint32_t uuid_status;
+
+	uuid_to_string(&worktree->uuid, &uuidstr, &uuid_status);
+	if (uuid_status != uuid_s_ok)
+		return got_error_uuid(uuid_status);
+
+	root_path = got_worktree_get_root_path(worktree);
+	while (root_path[0] == '/')
+		root_path++;
+	if (asprintf(&refname, "%s-%s-%s", GOT_WORKTREE_BASE_REF_PREFIX,
+	    root_path, uuidstr) == -1) {
+		err = got_error_from_errno();
+		goto done;
+	}
+
+	/* Replace slashes from worktree's on-disk path with dashes. */
+	s = refname + sizeof(GOT_WORKTREE_BASE_REF_PREFIX) - 1;
+	while (*s) {
+		if (*s == '/')
+			*s = '-';
+		s++;
+	}
+
+	err = got_ref_alloc(&ref, refname, worktree->base_commit_id);
+	if (err)
+		goto done;
+
+	err = got_ref_write(ref, repo);
+done:
+	free(uuidstr);
+	free(refname);
+	if (ref)
+		got_ref_close(ref);
 	return err;
 }
 
+
 const struct got_error *
 got_worktree_checkout_files(struct got_worktree *worktree,
     struct got_repository *repo, got_worktree_checkout_cb progress_cb,
@@ -1276,6 +1274,10 @@ got_worktree_checkout_files(struct got_worktree *workt
 	if (err)
 		goto done;
 
+	err = ref_base_commit(worktree, repo);
+	if (err)
+		goto done;
+
 	err = got_object_open_as_commit(&commit, repo,
 	   worktree->base_commit_id);
 	if (err)
blob - b33bfa37d5d2f770d3d199f2ed1a186aa12dd65e
blob + 7c5d377f96e97fcd2712608352c474e3eda79765
--- regress/worktree/worktree_test.c
+++ regress/worktree/worktree_test.c
@@ -83,6 +83,48 @@ remove_meta_file(const char *worktree_path, const char
 	return 1;
 }
 
+static const struct got_error *
+remove_worktree_base_ref(struct got_worktree *worktree,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	const char *root_path;
+	struct got_reference *base_ref;
+	char *refname = NULL, *uuidstr = NULL, *s;
+	uint32_t uuid_status;
+
+	uuid_to_string(&worktree->uuid, &uuidstr, &uuid_status);
+	if (uuid_status != uuid_s_ok)
+		return got_error_uuid(uuid_status);
+	root_path = got_worktree_get_root_path(worktree);
+	while (*root_path == '/')
+		root_path++;
+	if (asprintf(&refname, "refs/%s-%s-%s", GOT_WORKTREE_BASE_REF_PREFIX,
+	    root_path, uuidstr) == -1)
+		return got_error_from_errno();
+
+	/* Replace slashes from worktree's on-disk path with dashes. */
+	s = refname + sizeof(GOT_WORKTREE_BASE_REF_PREFIX) - 1;
+	while (*s) {
+		if (*s == '/')
+			*s = '-';
+		s++;
+	}
+
+	err = got_ref_open(&base_ref, repo, refname);
+	if (err)
+		goto done;
+
+	err = got_ref_delete(base_ref, repo);
+done:
+	if (base_ref)
+		got_ref_close(base_ref);
+	free(uuidstr);
+	free(refname);
+	return err;
+
+}
+
 static int
 remove_worktree(const char *worktree_path)
 {
@@ -370,6 +412,9 @@ worktree_checkout(const char *repo_path)
 	else
 		unlink(cfile_path);
 
+	err = remove_worktree_base_ref(worktree, repo);
+	if (err)
+		goto done;
 	if (!remove_worktree(worktree_path))
 		goto done;
 
@@ -444,7 +489,7 @@ main(int argc, char *argv[])
 	if (unveil("/tmp", "rwc") != 0)
 		err(1, "unveil");
 
-	if (unveil(repo_path, "r") != 0)
+	if (unveil(repo_path, "rwc") != 0)
 		err(1, "unveil");
 
 	if (got_privsep_unveil_exec_helpers() != NULL)