commit 0cd1c46a9e4b3e3a7c7f2947c3eded6fb5439516 from: Stefan Sperling date: Mon Mar 11 17:08:58 2019 UTC create references to base commits of got worktrees 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 +#include +#include #include +#include #include #include #include @@ -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 #include #include +#include #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)