Commit cb3269c970db6766f8a953fee438b56119fd9847

Patrick Steinhardt 2016-11-11T13:46:59

repository: add function to retrieve paths for repo items

diff --git a/include/git2/repository.h b/include/git2/repository.h
index f605445..74ce4c7 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -371,6 +371,42 @@ GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
 GIT_EXTERN(int) git_repository_is_empty(git_repository *repo);
 
 /**
+ * List of items which belong to the git repository layout
+ */
+typedef enum {
+	GIT_REPOSITORY_ITEM_GITDIR,
+	GIT_REPOSITORY_ITEM_WORKDIR,
+	GIT_REPOSITORY_ITEM_COMMONDIR,
+	GIT_REPOSITORY_ITEM_INDEX,
+	GIT_REPOSITORY_ITEM_OBJECTS,
+	GIT_REPOSITORY_ITEM_REFS,
+	GIT_REPOSITORY_ITEM_PACKED_REFS,
+	GIT_REPOSITORY_ITEM_REMOTES,
+	GIT_REPOSITORY_ITEM_CONFIG,
+	GIT_REPOSITORY_ITEM_INFO,
+	GIT_REPOSITORY_ITEM_HOOKS,
+	GIT_REPOSITORY_ITEM_LOGS,
+	GIT_REPOSITORY_ITEM_MODULES,
+	GIT_REPOSITORY_ITEM_WORKTREES
+} git_repository_item_t;
+
+/**
+ * Get the location of a specific repository file or directory
+ *
+ * This function will retrieve the path of a specific repository
+ * item. It will thereby honor things like the repository's
+ * common directory, gitdir, etc. In case a file path cannot
+ * exist for a given item (e.g. the working directory of a bare
+ * repository), an error is returned.
+ *
+ * @param out Buffer to store the path at
+ * @param repo Repository to get path for
+ * @param item The repository item for which to retrieve the path
+ * @return 0 on success, otherwise a negative value
+ */
+GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item);
+
+/**
  * Get the path of this repository
  *
  * This is the path of the `.git` folder for normal repositories,
diff --git a/src/repository.c b/src/repository.c
index 71b6ec4..14ad7a5 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -36,6 +36,27 @@ GIT__USE_STRMAP
 # include "win32/w32_util.h"
 #endif
 
+static const struct {
+    git_repository_item_t parent;
+    const char *name;
+    bool directory;
+} items[] = {
+	{ GIT_REPOSITORY_ITEM_GITDIR, NULL, true },
+	{ GIT_REPOSITORY_ITEM_WORKDIR, NULL, true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, NULL, true },
+	{ GIT_REPOSITORY_ITEM_GITDIR, "index", false },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "objects", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "refs", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "packed-refs", false },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "remotes", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "config", false },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "info", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "hooks", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "logs", true },
+	{ GIT_REPOSITORY_ITEM_GITDIR, "modules", true },
+	{ GIT_REPOSITORY_ITEM_COMMONDIR, "worktrees", true }
+};
+
 static int check_repositoryformatversion(git_config *config);
 
 #define GIT_COMMONDIR_FILE "commondir"
@@ -2052,6 +2073,46 @@ int git_repository_is_empty(git_repository *repo)
 	return is_empty;
 }
 
+int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item)
+{
+	const char *parent;
+
+	switch (items[item].parent) {
+		case GIT_REPOSITORY_ITEM_GITDIR:
+			parent = git_repository_path(repo);
+			break;
+		case GIT_REPOSITORY_ITEM_WORKDIR:
+			parent = git_repository_workdir(repo);
+			break;
+		case GIT_REPOSITORY_ITEM_COMMONDIR:
+			parent = git_repository_commondir(repo);
+			break;
+		default:
+			giterr_set(GITERR_INVALID, "Invalid item directory");
+			return -1;
+	}
+
+	if (parent == NULL) {
+		giterr_set(GITERR_INVALID, "Path cannot exist in repository");
+		return -1;
+	}
+
+	if (git_buf_sets(out, parent) < 0)
+		return -1;
+
+	if (items[item].name) {
+		if (git_buf_joinpath(out, parent, items[item].name) < 0)
+			return -1;
+	}
+
+	if (items[item].directory) {
+		if (git_path_to_dir(out) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
 const char *git_repository_path(git_repository *repo)
 {
 	assert(repo);