Commit Diff


commit - 102ff934b46daf842c7e393a017ab35d9449c577
commit + 2c7829a4ca20def05fcbfb4e1557e8f7abbf9c1b
blob - c9d5eab615151bbeb5480b7be1367ad4ea6e6f9b
blob + 7eb019a21f9eef0e5f7ea682d71c44f495124d79
--- got/got.1
+++ got/got.1
@@ -60,6 +60,12 @@ The commands for
 .Nm
 are as follows:
 .Bl -tag -width checkout
+.It Cm init Ar path
+Create a new empty repository at the specified
+.Ar path .
+If
+.Ar path
+already exists, it must be an empty directory.
 .It Cm checkout [ Fl b Ar branch ] [ Fl c Ar commit ] [ Fl p Ar path-prefix ] repository-path [ work-tree-path ]
 Copy files from a repository into a new work tree.
 If the
blob - 01f304f9b44e4b62576f8d26aca2b3756f8d99b0
blob + b45405f0c457c310049eeec2fa105400ab12c10a
--- got/got.c
+++ got/got.c
@@ -74,6 +74,7 @@ struct cmd {
 };
 
 __dead static void	usage(void);
+__dead static void	usage_init(void);
 __dead static void	usage_checkout(void);
 __dead static void	usage_update(void);
 __dead static void	usage_log(void);
@@ -89,6 +90,7 @@ __dead static void	usage_commit(void);
 __dead static void	usage_cherrypick(void);
 __dead static void	usage_backout(void);
 
+static const struct got_error*		cmd_init(int, char *[]);
 static const struct got_error*		cmd_checkout(int, char *[]);
 static const struct got_error*		cmd_update(int, char *[]);
 static const struct got_error*		cmd_log(int, char *[]);
@@ -105,6 +107,8 @@ static const struct got_error*		cmd_cherrypick(int, ch
 static const struct got_error*		cmd_backout(int, char *[]);
 
 static struct cmd got_commands[] = {
+	{ "init",	cmd_init,	usage_init,
+	    "create a new empty repository" },
 	{ "checkout",	cmd_checkout,	usage_checkout,
 	    "check out a new work tree from a repository" },
 	{ "update",	cmd_update,	usage_update,
@@ -270,6 +274,62 @@ apply_unveil(const char *repo_path, int repo_read_only
 		return got_error_from_errno("unveil");
 
 	return NULL;
+}
+
+__dead static void
+usage_init(void)
+{
+	fprintf(stderr, "usage: %s init path\n", getprogname());
+	exit(1);
+}
+
+static const struct got_error *
+cmd_init(int argc, char *argv[])
+{
+	const struct got_error *error = NULL;
+	char *repo_path = NULL;
+	int ch;
+
+	while ((ch = getopt(argc, argv, "")) != -1) {
+		switch (ch) {
+		default:
+			usage_init();
+			/* NOTREACHED */
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+#ifndef PROFILE
+	if (pledge("stdio rpath wpath cpath unveil", NULL) == -1)
+		err(1, "pledge");
+#endif
+	if (argc != 1)
+		usage_checkout();
+
+	repo_path = strdup(argv[0]);
+	if (repo_path == NULL)
+		return got_error_from_errno("strdup");
+
+	got_path_strip_trailing_slashes(repo_path);
+
+	error = got_path_mkdir(repo_path);
+	if (error &&
+	    !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
+		goto done;
+
+	error = apply_unveil(repo_path, 0, NULL, 0);
+	if (error)
+		goto done;
+
+	error = got_repo_init(repo_path);
+	if (error != NULL)
+		goto done;
+
+done:
+	free(repo_path);
+	return error;
 }
 
 __dead static void
blob - 999fdf6ddce081722e9bfc9da58ae4feafdc6c18
blob + 0930f71c8146155d8882f672a15be677ed8eadc5
--- include/got_path.h
+++ include/got_path.h
@@ -101,3 +101,6 @@ void got_path_strip_trailing_slashes(char *);
 
 /* Look up the absolute path of a program in $PATH */
 const struct got_error *got_path_find_prog(char **, const char *);
+
+/* Create a new file at a specified path, with optional content. */
+const struct got_error *got_path_create_file(const char *, const char *);
blob - c026e9bdbd4e6a2294da9829b38751ccbf812f54
blob + 0b61b49e3ed0680170a01636840b554aac156059
--- include/got_repository.h
+++ include/got_repository.h
@@ -54,3 +54,6 @@ int got_repo_is_bare(struct got_repository *);
 /* Attempt to map an arbitrary path to a path within the repository. */
 const struct got_error *got_repo_map_path(char **, struct got_repository *,
     const char *, int);
+
+/* Create a new repository in an empty directory at a specified path. */
+const struct got_error *got_repo_init(const char *);
blob - a0cd8335240385644aab73fbf3221f9c2da0960c
blob + fb283d460760a9d031f838d8fb9053fc63930ebd
--- lib/path.c
+++ lib/path.c
@@ -20,6 +20,7 @@
 #include <sys/stat.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <libgen.h>
 #include <stdlib.h>
@@ -433,4 +434,31 @@ got_path_find_prog(char **filename, const char *prog)
 	}
 	free(path);
 	return NULL;
+}
+
+const struct got_error *
+got_path_create_file(const char *path, const char *content)
+{
+	const struct got_error *err = NULL;
+	int fd = -1;
+
+	fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
+	    GOT_DEFAULT_FILE_MODE);
+	if (fd == -1) {
+		err = got_error_from_errno2("open", path);
+		goto done;
+	}
+
+	if (content) {
+		int len = dprintf(fd, "%s\n", content);
+		if (len != strlen(content) + 1) {
+			err = got_error_from_errno("dprintf");
+			goto done;
+		}
+	}
+
+done:
+	if (fd != -1 && close(fd) == -1 && err == NULL)
+		err = got_error_from_errno("close");
+	return err;
 }
blob - 0277a4437dc809ba033a7083ce7d917e0a36fd81
blob + 3e583b28ea58e79e805fdf160253840f7f33b6da
--- lib/repository.c
+++ lib/repository.c
@@ -815,7 +815,63 @@ got_repo_get_cached_pack(struct got_repository *repo, 
 			break;
 		if (strcmp(pack->path_packfile, path_packfile) == 0)
 			return pack;
+	}
+
+	return NULL;
+}
+
+const struct got_error *
+got_repo_init(const char *repo_path)
+{
+	const struct got_error *err = NULL;
+	const char *dirnames[] = {
+		GOT_OBJECTS_DIR,
+		GOT_OBJECTS_PACK_DIR,
+		GOT_REFS_DIR,
+	};
+	const char *description_str = "Unnamed repository; "
+	    "edit this file 'description' to name the repository.";
+	const char *headref_str = "ref: refs/heads/master";
+	const char *gitconfig_str = "[core]\n"
+	    "\trepositoryformatversion = 0\n"
+	    "\tfilemode = true\n"
+	    "\tbare = true\n";
+	char *path;
+	int i;
+
+	if (!got_path_dir_is_empty(repo_path))
+		return got_error(GOT_ERR_DIR_NOT_EMPTY);
+
+	for (i = 0; i < nitems(dirnames); i++) {
+		if (asprintf(&path, "%s/%s", repo_path, dirnames[i]) == -1) {
+			return got_error_from_errno("asprintf");
+		}
+		err = got_path_mkdir(path);
+		free(path);
+		if (err)
+			return err;
 	}
 
+	if (asprintf(&path, "%s/%s", repo_path, "description") == -1)
+		return got_error_from_errno("asprintf");
+	err = got_path_create_file(path, description_str);
+	free(path);
+	if (err)
+		return err;
+
+	if (asprintf(&path, "%s/%s", repo_path, GOT_HEAD_FILE) == -1)
+		return got_error_from_errno("asprintf");
+	err = got_path_create_file(path, headref_str);
+	free(path);
+	if (err)
+		return err;
+
+	if (asprintf(&path, "%s/%s", repo_path, "config") == -1)
+		return got_error_from_errno("asprintf");
+	err = got_path_create_file(path, gitconfig_str);
+	free(path);
+	if (err)
+		return err;
+
 	return NULL;
 }
blob - 9c8e0f6aa7469f287229ba2c42433b98972f6c62
blob + b0e00b1694127e0786c2ba99bebc3d6873e007a3
--- lib/worktree.c
+++ lib/worktree.c
@@ -63,32 +63,11 @@ create_meta_file(const char *path_got, const char *nam
 {
 	const struct got_error *err = NULL;
 	char *path;
-	int fd = -1;
 
-	if (asprintf(&path, "%s/%s", path_got, name) == -1) {
-		err = got_error_from_errno("asprintf");
-		path = NULL;
-		goto done;
-	}
-
-	fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
-	    GOT_DEFAULT_FILE_MODE);
-	if (fd == -1) {
-		err = got_error_from_errno2("open", path);
-		goto done;
-	}
+	if (asprintf(&path, "%s/%s", path_got, name) == -1)
+		return got_error_from_errno("asprintf");
 
-	if (content) {
-		int len = dprintf(fd, "%s\n", content);
-		if (len != strlen(content) + 1) {
-			err = got_error_from_errno("dprintf");
-			goto done;
-		}
-	}
-
-done:
-	if (fd != -1 && close(fd) == -1 && err == NULL)
-		err = got_error_from_errno("close");
+	err = got_path_create_file(path, content);
 	free(path);
 	return err;
 }