Commit fe93dabeef1f2757e73757bb2ec2dd3c912325a8

Vicent Marti 2010-12-19T21:30:15

Merge branch 'repo-init' of https://github.com/nulltoken/libgit2 into nulltoken-repo-init

diff --git a/src/fileops.c b/src/fileops.c
index 0633f9b..97b2f07 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -310,3 +310,16 @@ int gitfo_dirent(
 	closedir(dir);
 	return GIT_SUCCESS;
 }
+
+int gitfo_mkdir_recurs(const char *path, int mode)
+{
+	//TODO: Implement recursive building of the parent tree structure (This would have to work on both Linux and Windows).
+	int error;
+
+	error = gitfo_mkdir(path, mode);
+
+	if (errno == EEXIST)
+		return GIT_SUCCESS;
+
+	return error;
+}
\ No newline at end of file
diff --git a/src/fileops.h b/src/fileops.h
index 618bddd..6656cdf 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -58,6 +58,7 @@ extern int gitfo_exists(const char *path);
 extern int gitfo_open(const char *path, int flags);
 extern int gitfo_creat(const char *path, int mode);
 extern int gitfo_isdir(const char *path);
+extern int gitfo_mkdir_recurs(const char *path, int mode);
 #define gitfo_close(fd) close(fd)
 
 extern int gitfo_read(git_file fd, void *buf, size_t cnt);
diff --git a/src/git2/repository.h b/src/git2/repository.h
index 24b411b..4c7ba8d 100644
--- a/src/git2/repository.h
+++ b/src/git2/repository.h
@@ -59,7 +59,7 @@ GIT_BEGIN_DECL
  *
  * @param repository pointer to the repo which will be opened
  * @param path the path to the repository
- * @return 0 on sucess; error code otherwise
+ * @return 0 on success; error code otherwise
  */
 GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *path);
 
@@ -88,7 +88,7 @@ GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *pat
  *		Equivalent to $GIT_WORK_TREE.
  *		If NULL, the repository is assumed to be bare.
  *
- * @return 0 on sucess; error code otherwise
+ * @return 0 on success; error code otherwise
  */
 GIT_EXTERN(int) git_repository_open2(git_repository **repository,
 		const char *git_dir,
@@ -162,6 +162,22 @@ GIT_EXTERN(int) git_repository_newobject(git_object **object, git_repository *re
  */
 GIT_EXTERN(void) git_repository_free(git_repository *repo);
 
+/**
+ * Creates a new Git repository.
+ *
+ * Limits:
+ *  - Reinit of an existing directory is not implemented yet. Will blindly return GIT_SUCCESS.
+ *  - The parent directory structure of the repository has to already exist. Recursive building of the parent tree structure is not implemented yet.
+ *  - Config file creation handling is not implemented yet.
+ *
+ * @param repo_out pointer to the repo which will be created or reinitialized
+ * @param path the path to the repository
+ * @param is_bare if true, a Git repository without a working directory is created at the pointed path. 
+ *		If false, provided path will be considered as the working directory into which the .git directory will be created.
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_repository_init(git_repository** repo_out, const char* path, unsigned is_bare);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/src/repository.c b/src/repository.c
index 098d098..94f67f4 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -35,9 +35,15 @@
 
 #define GIT_FOLDER "/.git/"
 #define GIT_OBJECTS_FOLDER "objects/"
+#define GIT_REFS_FOLDER "refs/"
 #define GIT_INDEX_FILE "index"
 #define GIT_HEAD_FILE "HEAD"
 
+#define GIT_SYMREF "ref: "
+#define GIT_REFS_HEADS "refs/heads/"
+#define GIT_REFS_TAGS "refs/tags/"
+#define GIT_BRANCH_MASTER "master"
+
 static const int default_table_size = 32;
 static const double max_load_factor = 0.65;
 
@@ -58,6 +64,13 @@ static struct {
 	{ "REF_DELTA", 0, 0					}   /* 7 = GIT_OBJ_REF_DELTA */
 };
 
+typedef struct git_repository_init_results {
+	char *path_repository;
+
+	unsigned is_bare:1;
+	unsigned has_been_reinit:1;
+} git_repository_init_results;
+
 /***********************************************************
  *
  * MISCELANEOUS HELPER FUNCTIONS
@@ -722,3 +735,146 @@ int git_object_typeisloose(git_otype type)
 
 	return git_objects_table[type].loose;
 }
+
+int git_repository_init__reinit(git_repository_init_results* results)
+{
+	/* To be implemented */
+
+	results->has_been_reinit = 1;
+	return GIT_SUCCESS;
+}
+
+int git_repository_init__create_head(const char* head_path)
+{
+	git_file fd;
+	int error = GIT_SUCCESS;
+	char head_symlink[50];
+	int len;
+
+	len = sprintf(head_symlink, "%s %s%s\n", GIT_SYMREF, GIT_REFS_HEADS, GIT_BRANCH_MASTER);
+	
+	if ((fd = gitfo_creat(head_path, S_IREAD | S_IWRITE)) < 0)
+		return GIT_ERROR;
+
+	error = gitfo_write(fd, (void*)head_symlink, strlen(head_symlink));
+	if (error < GIT_SUCCESS)
+		goto cleanup;
+
+cleanup:
+	gitfo_close(fd);
+	return error;
+}
+
+int git_repository_init__create_structure_or_reinit(git_repository_init_results* results)
+{
+	char temp_path[GIT_PATH_MAX];
+	int path_len;
+	const int mode = 0755; /* or 0777 ? */
+
+	char* git_dir = results->path_repository;
+
+	//TODO : Ensure the parent tree structure already exists by recursively building it through gitfo_mkdir_recurs().
+	if (gitfo_mkdir_recurs(git_dir, mode))
+		return GIT_ERROR;
+
+	path_len = strlen(git_dir);
+	strcpy(temp_path, git_dir);
+
+	/* Does HEAD file already exist ? */
+	strcpy(temp_path + path_len, GIT_HEAD_FILE);
+
+	if (!gitfo_exists(temp_path) && gitfo_isdir(temp_path) < GIT_SUCCESS)
+	{
+		/* Something very wrong has happened to this repository :-) */
+		return GIT_ERROR;
+	}
+
+	if (!gitfo_exists(temp_path))
+	{
+		return git_repository_init__reinit(results);
+	}
+
+	if (git_repository_init__create_head(temp_path) < GIT_SUCCESS)
+		return GIT_ERROR;
+
+	/* Creates the '/objects/' directory */
+	strcpy(temp_path + path_len, GIT_OBJECTS_FOLDER);
+	if (gitfo_mkdir(temp_path, mode))
+		return GIT_ERROR;
+
+	/* Creates the '/refs/' directory */
+	strcpy(temp_path + path_len, GIT_REFS_FOLDER);
+	if (gitfo_mkdir(temp_path, mode))
+		return GIT_ERROR;
+
+	/* Creates the '/refs/heads/' directory */
+	strcpy(temp_path + path_len, GIT_REFS_HEADS);
+	if (gitfo_mkdir(temp_path, mode))
+		return GIT_ERROR;
+
+	/* Creates the '/refs/tags/' directory */
+	strcpy(temp_path + path_len, GIT_REFS_TAGS);
+	if (gitfo_mkdir(temp_path, mode))
+		return GIT_ERROR;
+
+
+	/* To be implemented */
+
+	return GIT_SUCCESS;
+}
+
+int git_repository_init__assign_git_directory(git_repository_init_results* results, const char* path)
+{
+	const int MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH = 66; // TODO: How many ?
+	char temp_path[GIT_PATH_MAX];
+	int path_len;
+
+	path_len = strlen(path);
+	strcpy(temp_path, path);
+
+	/* Ensure path has a trailing slash */
+	if (temp_path[path_len - 1] != '/') {
+		temp_path[path_len] = '/';
+		temp_path[path_len + 1] = 0;
+
+		path_len = path_len + 1;
+	}
+
+	assert(path_len < GIT_PATH_MAX - MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH);
+
+	if (!results->is_bare)
+	{
+		strcpy(temp_path + path_len - 1, GIT_FOLDER);
+		path_len = path_len + strlen(GIT_FOLDER) - 1; /* Skip the leading slash from the constant */
+	}
+
+	results->path_repository = git__strdup(temp_path);
+
+	return GIT_SUCCESS;
+}
+
+int git_repository_init(git_repository** repo_out, const char* path, unsigned is_bare)
+{
+	git_repository_init_results results;
+	int error = GIT_SUCCESS;
+	
+	assert(repo_out && path);
+
+	results.is_bare = is_bare;
+
+	error = git_repository_init__assign_git_directory(&results, path);
+	if (error < GIT_SUCCESS)
+		goto cleanup;
+
+	error = git_repository_init__create_structure_or_reinit(&results);
+	if (error < GIT_SUCCESS)
+		goto cleanup;
+
+	error = git_repository_open(repo_out, results.path_repository);
+	if (error < GIT_SUCCESS)
+		goto cleanup;
+
+cleanup:
+	free(results.path_repository);
+	return error;
+}
\ No newline at end of file