commit - e2e879a0865a8984188e95cb05b86191c8cfd59b
commit + 0cd1c46a9e4b3e3a7c7f2947c3eded6fb5439516
blob - 0e10c1f50cce74da7725fe1a23d46eafa730aa8b
blob + ef9b7d3b627e23b44594955f5c68ea31aff13220
--- got/got.c
+++ got/got.c
} 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
/* 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
#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
*/
#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>
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
#include <util.h>
#include <zlib.h>
#include <time.h>
+#include <libgen.h>
#include "got_error.h"
#include "got_object.h"
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) {
/* 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
}
(*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;
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 *
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;
}
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,
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
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)
{
else
unlink(cfile_path);
+ err = remove_worktree_base_ref(worktree, repo);
+ if (err)
+ goto done;
if (!remove_worktree(worktree_path))
goto done;
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)