commit 86c3caaf12dbc42db6a36284eddfbbb5d243dc8f from: Stefan Sperling date: Fri Mar 09 23:22:23 2018 UTC add worktree skeleton code; implement got_worktree_init() commit - 8fd174549d6af728ab316c727a527717d9fec756 commit + 86c3caaf12dbc42db6a36284eddfbbb5d243dc8f blob - 63ff8ec7e3a9660476e5885e90590b8b63bc52bc blob + 128e60e57bcaaebc470eebccc4ddd8948f7044d4 --- include/got_error.h +++ include/got_error.h @@ -39,6 +39,7 @@ #define GOT_ERR_BAD_DELTA 21 #define GOT_ERR_COMPRESSION 22 #define GOT_ERR_BAD_OBJ_ID_STR 23 +#define GOT_ERR_WORKTREE_EXISTS 26 static const struct got_error { int code; @@ -68,6 +69,7 @@ static const struct got_error { { GOT_ERR_BAD_DELTA, "bad delta" }, { GOT_ERR_COMPRESSION, "compression failed" }, { GOT_ERR_BAD_OBJ_ID_STR,"bad object id string" }, + { GOT_ERR_WORKTREE_EXISTS,"worktree already exists" }, }; const struct got_error * got_error(int code); blob - e3f715f2cb0eed090fe9c7e42d360a3a6a75b4c2 blob + 9c30c84496b4423c3a5d3cb3282db636b246c4d3 --- include/got_refs.h +++ include/got_refs.h @@ -32,3 +32,4 @@ void got_ref_close(struct got_reference *); struct got_reference *got_ref_dup(struct got_reference *); const struct got_error *got_ref_resolve(struct got_object_id **, struct got_repository *, struct got_reference *); +char *got_ref_to_str(struct got_reference *); blob - 3ca9626ba044cf3a00cb587c9d36e9c37ee0f39e blob + 96f1b71b451b24b056f5bb16b7cf978bfc6dc5e1 --- include/got_repository.h +++ include/got_repository.h @@ -20,6 +20,7 @@ struct got_repository; const struct got_error *got_repo_open(struct got_repository**, const char *); void got_repo_close(struct got_repository*); +char *got_repo_get_path(struct got_repository *); char *got_repo_get_path_git_dir(struct got_repository *); char *got_repo_get_path_objects(struct got_repository *); char *got_repo_get_path_objects_pack(struct got_repository *); blob - /dev/null blob + efcffc833f19d3b45d8b089c5ce124a3e89c9fd8 (mode 644) --- /dev/null +++ include/got_worktree.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * 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. + */ + +struct got_worktree; + +const struct got_error *got_worktree_init(const char *, struct got_reference *, + struct got_repository *); +const struct got_error *got_worktree_open(struct got_worktree **, const char *); +void got_worktree_close(struct got_worktree *); +char *got_worktree_get_repo_path(struct got_worktree *); +struct got_reference *got_worktree_get_head(struct got_worktree *); +const struct got_error *got_worktree_set_head(struct got_worktree *, + struct got_reference *, struct got_repository *); +const struct got_error *got_worktree_checkout_files(struct got_worktree *, + struct got_repository *); blob - 3bca95b3c18a88dff7ba4f0da9738a1e898be44b blob + 977e6fa24ee0d199aed7f2654832d0b74fc28e31 --- lib/got_path_priv.h +++ lib/got_path_priv.h @@ -16,6 +16,9 @@ /* Utilities for dealing with filesystem paths. */ +#define GOT_DEFAULT_FILE_MODE (S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH) +#define GOT_DEFAULT_DIR_MODE (S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH) + /* Determine whether a path is an absolute path. */ int got_path_is_absolute(const char *); blob - /dev/null blob + 97fb6c888a7d319c81a0c1bc3f9c8158bed69ada (mode 644) --- /dev/null +++ lib/got_worktree_priv.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * 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. + */ + +struct got_worktree { + char *path_worktree_root; + char *path_repo; +}; + +#define GOT_WORKTREE_GOT_DIR ".got" +#define GOT_WORKTREE_FILE_INDEX "fileindex" +#define GOT_WORKTREE_REPOSITORY "repository" blob - 718cf6189afb20d006aa4347bc2035355ab67e9c blob + fe5dfd7dd99058d3effb1cd88e52dc14a2767ebb --- lib/refs.c +++ lib/refs.c @@ -275,4 +275,22 @@ got_ref_resolve(struct got_object_id **id, struct got_ return got_error(GOT_ERR_NO_MEM); memcpy((*id)->sha1, ref->ref.ref.sha1, SHA1_DIGEST_LENGTH); return NULL; +} + +char * +got_ref_to_str(struct got_reference *ref) +{ + char *str; + if (ref->flags & GOT_REF_IS_SYMBOLIC) { + if (asprintf(&str, "ref: %s", ref->ref.symref.ref) == -1) + return NULL; + } else { + str = calloc(1, SHA1_DIGEST_STRING_LENGTH); + if (str == NULL) + return NULL; + str = got_sha1_digest_to_str(ref->ref.ref.sha1, str, + SHA1_DIGEST_STRING_LENGTH); + } + + return str; } blob - 9037504a410bed587331d101a6cb0a65968e356a blob + ac7b356c73e492aea73ce81178afb0ddd399972d --- lib/repository.c +++ lib/repository.c @@ -51,6 +51,12 @@ #define GOT_OBJECTS_PACK_DIR "objects/pack" char * +got_repo_get_path(struct got_repository *repo) +{ + return strdup(repo->path); +} + +char * got_repo_get_path_git_dir(struct got_repository *repo) { char *path_git; blob - /dev/null blob + 0a8cb93ba6e69957b761f1acb9c87b4aeb315c61 (mode 644) --- /dev/null +++ lib/worktree.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_repository.h" +#include "got_refs.h" +#include "got_worktree.h" + +#include "got_worktree_priv.h" +#include "got_path_priv.h" + +const struct got_error * +got_worktree_init(const char *path, struct got_reference *head_ref, + struct got_repository *repo) +{ + const struct got_error *err = NULL; + char *abspath = NULL; + char *normpath = NULL; + char *gotpath = NULL; + char *indexpath = NULL; + char *headpath = NULL; + char *repopath = NULL; + char *refstr = NULL; + char *path_repos = NULL; + char buf[4]; + ssize_t n; + int fd; + + if (got_path_is_absolute(path)) { + abspath = strdup(path); + if (abspath == NULL) + return got_error(GOT_ERR_NO_MEM); + } else { + abspath = got_path_get_absolute(path); + if (abspath == NULL) + return got_error(GOT_ERR_BAD_PATH); + } + + /* Create top-level directory (may already exist). */ + normpath = got_path_normalize(abspath); + if (normpath == NULL) { + err = got_error(GOT_ERR_BAD_PATH); + goto done; + } + if (mkdir(normpath, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST) { + err = got_error_from_errno(); + goto done; + } + + /* Create .got directory (may already exist). */ + if (asprintf(&gotpath, "%s/%s", normpath, GOT_WORKTREE_GOT_DIR) == -1) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + if (mkdir(gotpath, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST) { + err = got_error_from_errno(); + goto done; + } + + /* Create an empty file index. */ + if (asprintf(&indexpath, "%s/%s", gotpath, GOT_WORKTREE_FILE_INDEX) + == -1) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + fd = open(indexpath, O_RDWR | O_CREAT | O_EXCL | O_EXLOCK | O_NOFOLLOW, + GOT_DEFAULT_FILE_MODE); + if (fd == -1) { + err = got_error_from_errno(); + goto done; + } + n = read(fd, buf, sizeof(buf)); + if (n != 0) { + err = (n == -1 ? got_error_from_errno() : + got_error(GOT_ERR_WORKTREE_EXISTS)); + close(fd); + goto done; + } + if (close(fd) == -1) { + err = got_error_from_errno(); + goto done; + } + + /* Write the HEAD reference. */ + refstr = got_ref_to_str(head_ref); + if (refstr == NULL) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + if (asprintf(&headpath, "%s/%s", gotpath, GOT_REF_HEAD) == -1) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + fd = open(headpath, O_RDWR | O_CREAT | O_EXCL | O_EXLOCK | O_NOFOLLOW, + GOT_DEFAULT_FILE_MODE); + if (fd == -1) { + err = got_error_from_errno(); + goto done; + } + n = read(fd, buf, sizeof(buf)); + if (n != 0) { + err = (n == -1 ? got_error_from_errno() : + got_error(GOT_ERR_WORKTREE_EXISTS)); + close(fd); + goto done; + } + n = write(fd, refstr, strlen(refstr)); + if (n != strlen(refstr)) { + err = got_error_from_errno(); + close(fd); + goto done; + } + n = write(fd, "\n", 1); + if (n != 1) { + err = got_error_from_errno(); + close(fd); + goto done; + } + if (close(fd) == -1) { + err = got_error_from_errno(); + goto done; + } + + /* Store path to repository. */ + if (asprintf(&repopath, "%s/%s", gotpath, GOT_WORKTREE_REPOSITORY) == -1) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + fd = open(repopath, O_RDWR | O_CREAT | O_EXCL | O_EXLOCK | O_NOFOLLOW, + GOT_DEFAULT_FILE_MODE); + if (fd == -1) { + err = got_error_from_errno(); + goto done; + } + n = read(fd, buf, sizeof(buf)); + if (n != 0) { + err = (n == -1 ? got_error_from_errno() : + got_error(GOT_ERR_WORKTREE_EXISTS)); + close(fd); + goto done; + } + path_repos = got_repo_get_path(repo); + if (path_repos == NULL) { + err = got_error(GOT_ERR_NO_MEM); + goto done; + } + n = write(fd, path_repos, strlen(path_repos)); + if (n != strlen(path_repos)) { + err = got_error_from_errno(); + close(fd); + goto done; + } + n = write(fd, "\n", 1); + if (n != 1) { + err = got_error_from_errno(); + close(fd); + goto done; + } + if (close(fd) == -1) { + err = got_error_from_errno(); + goto done; + } + +done: + free(abspath); + free(normpath); + free(gotpath); + free(indexpath); + free(headpath); + free(repopath); + free(refstr); + free(path_repos); + return err; +} + +const struct got_error * +got_worktree_open(struct got_worktree **worktree, const char *path) +{ + return NULL; +} + +void +got_worktree_close(struct got_worktree *worktree) +{ +} + +char * +got_worktree_get_repo_path(struct got_worktree *worktree) +{ + return strdup(worktree->path_repo); +} + +struct got_reference * +got_worktree_get_head(struct got_worktree *worktree) +{ + return NULL; +} + +const struct got_error * +got_worktree_set_head(struct got_worktree *worktree, struct got_reference *head, + struct got_repository *repo) +{ + return NULL; +} + +const struct got_error * +got_worktree_checkout_files(struct got_worktree *worktree, + struct got_repository *repo) +{ + return NULL; +} blob - /dev/null blob + e10b20fba2f9e06be8895d91675f8fc91f3ff522 (mode 644) --- /dev/null +++ regress/worktree/Makefile @@ -0,0 +1,14 @@ +.PATH:${.CURDIR}/../../lib + +PROG = worktree_test +SRCS = worktree.c repository.c object.c path.c error.c refs.c sha1.c pack.c \ + delta.c zb.c worktree_test.c + +CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib +LDADD = -lutil -lz +DEBUG = -O0 -g +CFLAGS += -Werror + +NOMAN = yes + +.include blob - /dev/null blob + 252f9c9aa2f1eac333b88865699f7a17ad70aae3 (mode 644) --- /dev/null +++ regress/worktree/worktree_test.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2018 Stefan Sperling + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "got_error.h" +#include "got_object.h" +#include "got_refs.h" +#include "got_repository.h" +#include "got_worktree.h" + +#include "got_worktree_priv.h" + +#define GOT_REPO_PATH "../../../" + +static int verbose; + +void +test_printf(char *fmt, ...) +{ + va_list ap; + + if (!verbose) + return; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +static int +check_meta_file_exists(const char *worktree_path, const char *name) +{ + FILE *f; + char *path; + + if (asprintf(&path, "%s/%s/%s", worktree_path, GOT_WORKTREE_GOT_DIR, + name) == -1) + return 0; + f = fopen(path, "r"); + if (f == NULL) + return 0; + fclose(f); + return 1; +} + +static int +worktree_init(const char *repo_path) +{ + const struct got_error *err; + struct got_repository *repo = NULL; + struct got_reference *head_ref = NULL; + char worktree_path[PATH_MAX]; + int ok = 0; + + err = got_repo_open(&repo, repo_path); + if (err != NULL || repo == NULL) + goto done; + err = got_ref_open(&head_ref, repo, GOT_REF_HEAD); + if (err != NULL || head_ref == NULL) + goto done; + + strlcpy(worktree_path, "worktree-XXXXXX", sizeof(worktree_path)); + if (mkdtemp(worktree_path) == NULL) + goto done; + + err = got_worktree_init(worktree_path, head_ref, repo); + if (err != NULL) + goto done; + + /* Ensure required files were created. */ + if (!check_meta_file_exists(worktree_path, GOT_REF_HEAD)) + goto done; + if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_FILE_INDEX)) + goto done; + if (!check_meta_file_exists(worktree_path, GOT_WORKTREE_REPOSITORY)) + goto done; + ok = 1; +done: + if (head_ref) + got_ref_close(head_ref); + if (repo) + got_repo_close(repo); + return ok; +} + +#define RUN_TEST(expr, name) \ + { test_ok = (expr); \ + printf("test %s %s\n", (name), test_ok ? "ok" : "failed"); \ + failure = (failure || !test_ok); } + + +void +usage(void) +{ + fprintf(stderr, "usage: worktree_test [-v] [REPO_PATH]\n"); +} + +int +main(int argc, char *argv[]) +{ + int test_ok = 0, failure = 0; + const char *repo_path; + int ch; + int vflag = 0; + + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + verbose = 1; + break; + default: + usage(); + return 1; + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + repo_path = GOT_REPO_PATH; + else if (argc == 1) + repo_path = argv[0]; + else { + usage(); + return 1; + } + + RUN_TEST(worktree_init(repo_path), "init"); + + return failure ? 1 : 0; +}