Commit Diff


commit - f7d20e8910435defc9367c68c5af4418123d088e
commit + 776d4d299ae7521a3f409a3b5dbbcc1a400f2b5c
blob - ec5c5b134da4aac3b2260284f17374a481a813ca
blob + 6687d7a01fd4ec3044033b802990e13135f72b4b
--- include/got_object.h
+++ include/got_object.h
@@ -182,6 +182,13 @@ const struct got_error *got_object_blob_read_block(siz
 const struct got_error *
 got_object_open_as_commit(struct got_commit_object **,
     struct got_repository *, struct got_object_id *);
+const struct got_error *
+got_object_open_as_tree(struct got_tree_object **,
+    struct got_repository *, struct got_object_id *);
 
+const struct got_error *
+got_object_open_by_path(struct got_object **, struct got_repository *,
+    struct got_object_id *, const char *);
+
 const struct got_error *got_object_commit_add_parent(struct got_commit_object *,
     const char *);
blob - 1765ec7c9ff3743ac32416d1a8baa920602a89c6
blob + 7e037bbba1c2f2cf32211f9bc9ad9dd6a060568e
--- lib/object.c
+++ lib/object.c
@@ -1121,7 +1121,28 @@ got_object_tree_open(struct got_tree_object **tree,
 			return err;
 		err = read_tree_object_privsep(tree, obj, fd);
 		close(fd);
+	}
+	return err;
+}
+
+const struct got_error *
+got_object_open_as_tree(struct got_tree_object **tree,
+    struct got_repository *repo, struct got_object_id *id)
+{
+	const struct got_error *err;
+	struct got_object *obj;
+
+	err = got_object_open(&obj, repo, id);
+	if (err)
+		return err;
+	if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT) {
+		err = got_error(GOT_ERR_OBJ_TYPE);
+		goto done;
 	}
+
+	err = got_object_tree_open(tree, repo, obj);
+done:
+	got_object_close(obj);
 	return err;
 }
 
@@ -1334,5 +1355,104 @@ got_object_blob_read_block(size_t *outlenp, struct got
 	if (n == 0 && ferror(blob->f))
 		return got_ferror(blob->f, GOT_ERR_IO);
 	*outlenp = n;
+	return NULL;
+}
+
+static struct got_tree_entry *
+find_entry_by_name(struct got_tree_object *tree, const char *name)
+{
+	struct got_tree_entry *te;
+
+	SIMPLEQ_FOREACH(te, &tree->entries, entry) {
+		if (strcmp(te->name, name) == 0)
+			return te;
+	}
 	return NULL;
+}
+
+const struct got_error *
+got_object_open_by_path(struct got_object **obj, struct got_repository *repo,
+    struct got_object_id *commit_id, const char *path)
+{
+	const struct got_error *err = NULL;
+	struct got_commit_object *commit = NULL;
+	struct got_tree_object *tree = NULL;
+	struct got_tree_entry *entry = NULL;
+	char *seg, *s = NULL;
+
+	*obj = NULL;
+
+	/* We are expecting an absolute in-repository path. */
+	if (path[0] != '/')
+		return got_error(GOT_ERR_NOT_ABSPATH);
+
+	err = got_object_open_as_commit(&commit, repo, commit_id);
+	if (err)
+		goto done;
+
+	/* Handle opening of root of commit's tree. */
+	if (path[1] == '\0') {
+		err = got_object_open(obj, repo, commit->tree_id);
+		if (err)
+			goto done;
+		return NULL;
+	}
+
+	err = got_object_open_as_tree(&tree, repo, commit->tree_id);
+	if (err)
+		goto done;
+
+	s = strdup(path);
+	if (s == NULL) {
+		err = got_error_from_errno();
+		goto done;
+	}
+	err = got_canonpath(path, s, strlen(s) + 1);
+	if (err)
+		goto done;
+
+	s++; /* skip leading '/' */
+	seg = s;
+	while (*s) {
+		struct got_tree_object *next_tree;
+
+		if (*s != '/') {
+			s++;
+			continue;
+		}
+
+		/* end of path segment */
+		*s = '\0';
+
+		entry = find_entry_by_name(tree, seg);
+		if (entry == NULL) {
+			err = got_error(GOT_ERR_NO_OBJ);
+			goto done;
+		}
+
+		seg = s + 1;
+		s++;
+
+		if (*s) {
+			err = got_object_open_as_tree(&next_tree, repo,
+			    entry->id);
+			entry = NULL;
+			if (err)
+				goto done;
+			got_object_tree_close(tree);
+			tree = next_tree;
+		}
+	}
+
+	if (entry)
+		err = got_object_open(obj, repo, entry->id);
+	else
+		err = got_error(GOT_ERR_NO_OBJ);
+done:
+	free(s);
+	if (commit)
+		got_object_commit_close(commit);
+	if (tree)
+		got_object_tree_close(tree);
+	return err;
 }