Commit c09a553dff65da50eb12a2ec1b10799f7dad0c73

Stefan Sperling 2018-03-12T21:19:31

add a 'got checkout' command

diff --git a/got/Makefile b/got/Makefile
index 1336114..8e74120 100644
--- a/got/Makefile
+++ b/got/Makefile
@@ -1,8 +1,8 @@
 .PATH:${.CURDIR}/../lib
 
 PROG=		got
-SRCS=		got.c delta.c error.c object.c path.c pack.c refs.c \
-		repository.c sha1.c zbuf.c
+SRCS=		got.c delta.c error.c fileindex.c object.c path.c pack.c \
+		refs.c repository.c sha1.c worktree.c zbuf.c
 
 CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib
 LDADD = -lutil -lz
diff --git a/got/got.c b/got/got.c
index ac347fd..8d6ac0c 100644
--- a/got/got.c
+++ b/got/got.c
@@ -24,11 +24,13 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <libgen.h>
 
 #include "got_error.h"
 #include "got_object.h"
 #include "got_refs.h"
 #include "got_repository.h"
+#include "got_worktree.h"
 
 #ifndef nitems
 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
@@ -42,12 +44,16 @@ struct cmd {
 };
 
 __dead void	usage(void);
+__dead void	usage_checkout(void);
 __dead void	usage_log(void);
 
+const struct got_error*		cmd_checkout(int, char *[]);
 const struct got_error*		cmd_log(int, char *[]);
 const struct got_error*		cmd_status(int, char *[]);
 
 struct cmd got_commands[] = {
+	{ "checkout",	cmd_checkout,	usage_checkout,
+	    "check out a work tree from a repository" },
 	{ "log",	cmd_log,	usage_log,
 	    "show repository history" },
 #ifdef notyet
@@ -66,9 +72,6 @@ main(int argc, char *argv[])
 
 	setlocale(LC_ALL, "");
 
-	if (pledge("stdio rpath wpath cpath", NULL) == -1)
-		err(1, "pledge");
-
 	while ((ch = getopt(argc, argv, "h")) != -1) {
 		switch (ch) {
 		case 'h':
@@ -124,6 +127,80 @@ usage(void)
 	exit(1);
 }
 
+__dead void
+usage_checkout(void)
+{
+	fprintf(stderr, "usage: %s checkout REPO_PATH [WORKTREE_PATH]\n",
+	    getprogname());
+	exit(1);
+}
+
+const struct got_error *
+cmd_checkout(int argc, char *argv[])
+{
+	const struct got_error *error = NULL;
+	struct got_repository *repo = NULL;
+	struct got_reference *head_ref = NULL;
+	struct got_worktree *worktree = NULL;
+	char *repo_path = NULL;
+	char *worktree_path = NULL;
+
+	if (pledge("stdio rpath wpath cpath flock", NULL) == -1)
+		err(1, "pledge");
+
+	if (argc == 2) {
+		char *cwd, *base, *dotgit;
+		repo_path = argv[1];
+		cwd = getcwd(NULL, 0);
+		if (cwd == NULL)
+			err(1, "getcwd");
+		base = basename(repo_path);
+		if (base == NULL)
+			err(1, "basename");
+		dotgit = strstr(base, ".git");
+		if (dotgit)
+			*dotgit = '\0';
+		if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
+			free(cwd);
+			return got_error(GOT_ERR_NO_MEM);
+		}
+		free(cwd);
+	} else if (argc == 3) {
+		repo_path = argv[1];
+		worktree_path = strdup(argv[2]);
+		if (worktree_path == NULL)
+			return got_error(GOT_ERR_NO_MEM);
+	} else
+		usage_checkout();
+
+	printf("%s %s %s %s\n", getprogname(), argv[0], repo_path, worktree_path);
+
+	error = got_repo_open(&repo, repo_path);
+	if (error != NULL)
+		goto done;
+	error = got_ref_open(&head_ref, repo, GOT_REF_HEAD);
+	if (error != NULL)
+		goto done;
+
+	error = got_worktree_init(worktree_path, head_ref, "/", repo);
+	if (error != NULL)
+		goto done;
+
+	error = got_worktree_open(&worktree, worktree_path);
+	if (error != NULL)
+		goto done;
+
+	error = got_worktree_checkout_files(worktree, head_ref, repo);
+	if (error != NULL)
+		goto done;
+
+	printf("checked out %s\n", worktree_path);
+
+done:
+	free(worktree_path);
+	return error;
+}
+
 static const struct got_error *
 print_commit_object(struct got_object *, struct got_object_id *,
     struct got_repository *);