You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/12/01 10:06:39 UTC
svn commit: r1717391 -
/subversion/branches/ra-git/subversion/libsvn_fs_git/git-revroot.c
Author: rhuijben
Date: Tue Dec 1 09:06:39 2015
New Revision: 1717391
URL: http://svn.apache.org/viewvc?rev=1717391&view=rev
Log:
On the ra-git branch: Extend the git-revroot to basically support all
operations to perform a svn checkout/update/switch of/within 'trunk'.
As a side-effect this also adds support for 'svn info', 'svn ls', etc.
* subversion/libsvn_fs_git/git-revroot.c
(includes): Add svn_dirent_uri.h.
(svn_fs_git_root_t): Add branch map.
(svn_fs_git_fs_id_t): New define.
(cleanup_git_object,
get_entry_object): New function.
(fs_git_id_unparse,
fs_git_id_compare): New function.
(id_vtable): New var.
(make_id): Implement.
(relpath_reverse_split): New function.
(find_branch,
find_tree_entry): New function.
(fs_git_paths_changed): Hardcode basic support for r0 and r1.
(fs_git_check_path): Implement.
(fs_git_node_id,
fs_git_node_relation): Implement.
(fs_git_copied_from,
fs_git_closest_copy): Return not copied instead of not implemented.
(fs_git_node_proplist,
fs_git_props_changed): We don't have properties yet.
(fs_git_dir_entries): Implement.
(fs_git_dir_optimal_order): Implement.
(fs_git_file_length,
fs_git_file_checksum,
fs_git_file_contents): Implement.
(fs_git_try_process_file_contents): Return not succeeded.
(fs_git_contents_changed): Compare files by matching their oid.
(fs_git_get_file_delta_stream): Provide deltas.
(svn_fs_git__revision_root): Initialize map.
Modified:
subversion/branches/ra-git/subversion/libsvn_fs_git/git-revroot.c
Modified: subversion/branches/ra-git/subversion/libsvn_fs_git/git-revroot.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_git/git-revroot.c?rev=1717391&r1=1717390&r2=1717391&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_git/git-revroot.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_git/git-revroot.c Tue Dec 1 09:06:39 2015
@@ -28,6 +28,7 @@
#include "svn_fs.h"
#include "svn_hash.h"
+#include "svn_dirent_uri.h"
#include "svn_version.h"
#include "svn_pools.h"
@@ -43,8 +44,17 @@ typedef struct svn_fs_git_root_t
git_commit *commit;
const char *rev_path;
svn_boolean_t exact;
+ apr_hash_t *branch_map;
} svn_fs_git_root_t;
+typedef struct svn_fs_git_fs_id_t
+{
+ git_oid commit;
+ const char *path;
+ const char *branch;
+ svn_fs_root_t *root;
+} svn_fs_git_fs_id_t;
+
static apr_status_t
git_root_cleanup(void *baton)
{
@@ -59,12 +69,195 @@ git_root_cleanup(void *baton)
return APR_SUCCESS;
}
+/* Helper for get_entry_object */
+static apr_status_t
+cleanup_git_object(void *baton)
+{
+ git_object_free(baton);
+ return APR_SUCCESS;
+}
+
+/* Gets the raw git object behind an entry. Takes care of the 'will free'
+ promise via the pool */
+static svn_error_t *
+get_entry_object(git_object **obj,
+ const git_tree *tree,
+ const git_tree_entry *entry,
+ apr_pool_t *result_pool)
+{
+ git_object *obj_out;
+
+ GIT2_ERR(git_tree_entry_to_object(&obj_out, git_tree_owner(tree), entry));
+
+ apr_pool_cleanup_register(result_pool, obj_out, cleanup_git_object,
+ apr_pool_cleanup_null);
+
+ *obj = obj_out;
+ return SVN_NO_ERROR;
+}
+
+/* We don't have real ids (yet) */
+static svn_string_t *fs_git_id_unparse(const svn_fs_id_t *id,
+ apr_pool_t *pool)
+{
+ return svn_string_create("", pool);
+}
+
+/* Fake an id via the node relation check to make the repos layer
+ happy */
+static svn_fs_node_relation_t fs_git_id_compare(const svn_fs_id_t *a,
+ const svn_fs_id_t *b)
+{
+ const svn_fs_git_fs_id_t *id_a, *id_b;
+ if (a->vtable != b->vtable)
+ return svn_fs_node_unrelated;
+
+ id_a = a->fsap_data;
+ id_b = b->fsap_data;
+
+ if (id_a->root && id_b->root
+ && id_a->root->fs == id_b->root->fs)
+ {
+ svn_fs_node_relation_t rel;
+ svn_error_t *err;
+
+ err = id_a->root->vtable->node_relation(&rel,
+ id_a->root, id_a->path,
+ id_b->root, id_b->path,
+ id_a->root->pool);
+
+ if (!err)
+ return rel;
+
+ svn_error_clear(err);
+ }
+
+ return svn_fs_node_unrelated;
+}
+
+static id_vtable_t id_vtable =
+{
+ fs_git_id_unparse,
+ fs_git_id_compare
+};
+
static svn_fs_id_t *
make_id(svn_fs_root_t *root,
const char *path,
apr_pool_t *result_pool)
{
- return NULL;
+ svn_fs_git_fs_id_t *id = apr_pcalloc(result_pool, sizeof(*id));
+ svn_fs_id_t *fsid = apr_pcalloc(result_pool, sizeof(*fsid));
+
+ fsid->fsap_data = id;
+ fsid->vtable = &id_vtable;
+
+ id->path = apr_pstrdup(result_pool, path);
+ id->root = root;
+ memset(&id->commit, 0, sizeof(id->commit));
+
+ return fsid;
+}
+
+/* svn_relpath_split, but then for the first component instead of the last */
+static svn_error_t *
+relpath_reverse_split(const char **root, const char **remaining,
+ const char *relpath,
+ apr_pool_t *result_pool)
+{
+ const char *ch = strchr(relpath, '/');
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ if (!ch)
+ {
+ if (root)
+ *root = apr_pstrdup(result_pool, relpath);
+ if (remaining)
+ *remaining = "";
+ }
+ else
+ {
+ if (root)
+ *root = apr_pstrmemdup(result_pool, relpath, ch - relpath);
+ if (remaining)
+ *remaining = apr_pstrdup(result_pool, ch + 1);
+ }
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+find_branch(const git_commit **commit, const char **relpath,
+ svn_fs_root_t *root, const char *path, apr_pool_t *pool)
+{
+ svn_fs_git_root_t *fgr = root->fsap_data;
+
+ if (*path == '/')
+ path++;
+
+ if (fgr->rev_path)
+ {
+ apr_size_t len;
+ len = strlen(fgr->rev_path);
+
+ if (!strncmp(path, fgr->rev_path, len)
+ && (!path[len] || path[len] == '/'))
+ {
+ *commit = fgr->commit;
+ *relpath = path[len] ? &path[len + 1] : "";
+ return SVN_NO_ERROR;
+ }
+ }
+
+ *commit = NULL;
+ *relpath = NULL;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+find_tree_entry(const git_tree_entry **entry, git_tree *tree,
+ const char *relpath,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ const char *basename, *tail;
+ const git_tree_entry *e;
+
+ SVN_ERR(relpath_reverse_split(&basename, &tail, relpath, scratch_pool));
+
+ e = git_tree_entry_byname(tree, basename);
+ if (e && !*tail)
+ {
+ *entry = e;
+ return SVN_NO_ERROR;
+ }
+ else if (!e)
+ {
+ *entry = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ switch (git_tree_entry_type(e))
+ {
+ case GIT_OBJ_TREE:
+ {
+ git_object *obj;
+ git_tree *sub_tree;
+
+ SVN_ERR(get_entry_object(&obj, tree, e, result_pool));
+
+ sub_tree = (git_tree*)obj;
+
+ SVN_ERR(find_tree_entry(entry, sub_tree, tail,
+ result_pool, scratch_pool));
+ break;
+ }
+ case GIT_OBJ_BLOB:
+ *entry = NULL;
+ break;
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+
+ return SVN_NO_ERROR;
}
/* Determining what has changed in a root */
@@ -73,7 +266,29 @@ fs_git_paths_changed(apr_hash_t **change
svn_fs_root_t *root,
apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ apr_hash_t *changed_paths = apr_hash_make(pool);
+ *changed_paths_p = changed_paths;
+ if (root->rev == 0)
+ return SVN_NO_ERROR;
+ else if (root->rev == 1)
+ {
+ svn_fs_path_change2_t *ch;
+
+ ch = svn_fs__path_change_create_internal(make_id(root, "/trunk", pool),
+ svn_fs_path_change_add,
+ pool);
+ ch->node_kind = svn_node_dir;
+ svn_hash_sets(changed_paths, "/trunk", ch);
+
+ ch = svn_fs__path_change_create_internal(make_id(root, "/branches",
+ pool),
+ svn_fs_path_change_add,
+ pool);
+ ch->node_kind = svn_node_dir;
+ svn_hash_sets(changed_paths, "/branches", ch);
+ }
+
+ return SVN_NO_ERROR;
}
/* Generic node operations */
@@ -81,6 +296,11 @@ static svn_error_t *
fs_git_check_path(svn_node_kind_t *kind_p, svn_fs_root_t *root,
const char *path, apr_pool_t *pool)
{
+ const git_commit *commit;
+ const char *relpath;
+ git_tree *tree;
+ git_tree_entry *entry;
+
if (*path == '/')
path++;
if (!*path)
@@ -89,7 +309,47 @@ fs_git_check_path(svn_node_kind_t *kind_
return SVN_NO_ERROR;
}
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ SVN_ERR(find_branch(&commit, &relpath, root, path, pool));
+ if (!commit)
+ {
+ if (!strcmp(path, "branches") || !strcmp(path, "tags"))
+ {
+ *kind_p = svn_node_dir;
+ return SVN_NO_ERROR;
+ }
+
+ *kind_p = svn_node_none;
+ return SVN_NO_ERROR;
+ }
+
+ if (!relpath[0])
+ {
+ *kind_p = svn_node_dir;
+ return SVN_NO_ERROR;
+ }
+
+ GIT2_ERR(git_commit_tree(&tree, commit));
+ SVN_ERR(find_tree_entry(&entry, tree, relpath, pool, pool));
+
+ if (!entry)
+ *kind_p = svn_node_none;
+ else
+ {
+ switch (git_tree_entry_type(entry))
+ {
+ case GIT_OBJ_TREE:
+ *kind_p = svn_node_dir;
+ break;
+ case GIT_OBJ_BLOB:
+ *kind_p = svn_node_file;
+ break;
+ default:
+ *kind_p = svn_node_none;
+ break;
+ }
+ }
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -105,7 +365,8 @@ static svn_error_t *
fs_git_node_id(const svn_fs_id_t **id_p, svn_fs_root_t *root,
const char *path, apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ *id_p = make_id(root, path, pool);
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -114,7 +375,79 @@ fs_git_node_relation(svn_fs_node_relatio
svn_fs_root_t *root_b, const char *path_b,
apr_pool_t *scratch_pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ const git_commit *commit_a, *commit_b;
+ const char *relpath_a, *relpath_b;
+ git_tree *tree_a, *tree_b;
+ const git_tree_entry *entry_a, *entry_b;
+
+ if (*path_a == '/')
+ path_a++;
+ if (*path_b == '/')
+ path_b++;
+
+ if (!*path_a || !*path_b)
+ {
+ if (!*path_a && !*path_b)
+ {
+ if (root_a->rev == root_b->rev)
+ *relation = svn_fs_node_unchanged;
+ else
+ *relation = svn_fs_node_common_ancestor;
+ }
+ else
+ *relation = svn_fs_node_unrelated;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(find_branch(&commit_a, &relpath_a, root_a, path_a, scratch_pool));
+ SVN_ERR(find_branch(&commit_b, &relpath_b, root_b, path_b, scratch_pool));
+
+ if (!(commit_a && commit_b))
+ {
+ *relation = svn_fs_node_unrelated;
+ return SVN_NO_ERROR;
+ }
+ else if ((*relpath_a == '\0') || (*relpath_b == '\0'))
+ {
+ if ((*relpath_a == '\0') && (*relpath_b == '\0'))
+ *relation = svn_fs_node_common_ancestor;
+ else
+ *relation = svn_fs_node_unrelated;
+
+ return SVN_NO_ERROR;
+ }
+
+ if (strcmp(relpath_a, relpath_b))
+ {
+ *relation = svn_fs_node_unrelated;
+ return SVN_NO_ERROR;
+ }
+
+ GIT2_ERR(git_commit_tree(&tree_a, commit_a));
+ GIT2_ERR(git_commit_tree(&tree_b, commit_b));
+
+ SVN_ERR(find_tree_entry(&entry_a, tree_a, relpath_a,
+ scratch_pool, scratch_pool));
+ SVN_ERR(find_tree_entry(&entry_b, tree_b, relpath_b,
+ scratch_pool, scratch_pool));
+
+ if (!entry_a || !entry_b)
+ {
+ *relation = svn_fs_node_unrelated;
+ return SVN_NO_ERROR;
+ }
+
+ if (git_tree_entry_type(entry_a) != git_tree_entry_type(entry_b))
+ *relation = svn_fs_node_unrelated;
+ else if (!git_oid_cmp(git_tree_entry_id(entry_a),
+ git_tree_entry_id(entry_b)))
+ {
+ *relation = svn_fs_node_unchanged;
+ }
+ else
+ *relation = svn_fs_node_common_ancestor;
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -179,7 +512,10 @@ fs_git_copied_from(svn_revnum_t *rev_p,
svn_fs_root_t *root, const char *path,
apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ *rev_p = SVN_INVALID_REVNUM;
+ *path_p = NULL;
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -187,7 +523,10 @@ fs_git_closest_copy(svn_fs_root_t **root
svn_fs_root_t *root, const char *path,
apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ *root_p = NULL;
+ *path_p = NULL;
+
+ return SVN_NO_ERROR;
}
/* Property operations */
@@ -202,7 +541,9 @@ static svn_error_t *
fs_git_node_proplist(apr_hash_t **table_p, svn_fs_root_t *root,
const char *path, apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ *table_p = apr_hash_make(pool);
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -228,7 +569,8 @@ fs_git_props_changed(int *changed_p, svn
const char *path2, svn_boolean_t strict,
apr_pool_t *scratch_pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ *changed_p = FALSE;
+ return SVN_NO_ERROR;
}
/* Directories */
@@ -237,11 +579,18 @@ fs_git_dir_entries(apr_hash_t **entries_
const char *path, apr_pool_t *pool)
{
svn_fs_dirent_t *de;
+ const git_commit *commit;
+ git_tree *tree;
+ const char *relpath;
+ apr_size_t idx;
+
+ if (*path == '/')
+ path++;
*entries_p = apr_hash_make(pool);
if (!root->rev)
return SVN_NO_ERROR;
- else if (*path == '/' && path[1] == '\0')
+ else if (*path == '\0')
{
de = apr_pcalloc(pool, sizeof(*de));
de->kind = svn_node_dir;
@@ -263,7 +612,48 @@ fs_git_dir_entries(apr_hash_t **entries_
return SVN_NO_ERROR;
}
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+
+ SVN_ERR(find_branch(&commit, &relpath, root, path, pool));
+ if (!commit)
+ {
+ /* TODO: List 'tags' and 'branches' */
+ return SVN_NO_ERROR;
+ }
+
+ GIT2_ERR(git_commit_tree(&tree, commit));
+
+ if (*relpath)
+ {
+ const git_tree_entry *entry;
+ git_object *obj;
+
+ SVN_ERR(find_tree_entry(&entry, tree, relpath, pool, pool));
+
+ if (!entry || git_tree_entry_type(entry) != GIT_OBJ_TREE)
+ return SVN_FS__ERR_NOT_DIRECTORY(root->fs, path);
+
+ SVN_ERR(get_entry_object(&obj, tree, entry, pool));
+
+ tree = (git_tree*)obj;
+ }
+
+ for (idx = 0; idx < git_tree_entrycount(tree); idx++)
+ {
+ const git_tree_entry *e = git_tree_entry_byindex(tree, idx);
+
+ de = apr_pcalloc(pool, sizeof(*de));
+ de->id = make_id(root, path, pool);
+ de->name = git_tree_entry_name(e);
+
+ if (git_tree_entry_type(e) == GIT_OBJ_TREE)
+ de->kind = svn_node_dir;
+ else
+ de->kind = svn_node_file;
+
+ svn_hash_sets(*entries_p, de->name, de);
+ }
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -273,7 +663,16 @@ fs_git_dir_optimal_order(apr_array_heade
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ /* 1:1 copy of entries with no difference in ordering */
+ apr_hash_index_t *hi;
+ apr_array_header_t *result
+ = apr_array_make(result_pool, apr_hash_count(entries),
+ sizeof(svn_fs_dirent_t *));
+ for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
+ APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi);
+
+ *ordered_p = result;
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -288,7 +687,31 @@ static svn_error_t *
fs_git_file_length(svn_filesize_t *length_p, svn_fs_root_t *root,
const char *path, apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ const git_commit *commit;
+ git_tree *tree;
+ git_tree_entry *entry;
+ git_object *obj;
+ git_blob *blob;
+ const char *relpath;
+
+ SVN_ERR(find_branch(&commit, &relpath, root, path, pool));
+
+ if (!commit)
+ return SVN_FS__ERR_NOT_FILE(root->fs, path);
+
+ GIT2_ERR(git_commit_tree(&tree, commit));
+
+ SVN_ERR(find_tree_entry(&entry, tree, relpath, pool, pool));
+
+ if (!entry || git_tree_entry_type(entry) != GIT_OBJ_BLOB)
+ return SVN_FS__ERR_NOT_FILE(root->fs, path);
+
+ SVN_ERR(get_entry_object(&obj, tree, entry, pool));
+
+ blob = (git_blob*)obj;
+
+ *length_p = git_blob_rawsize(blob);
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -296,7 +719,31 @@ fs_git_file_checksum(svn_checksum_t **ch
svn_checksum_kind_t kind, svn_fs_root_t *root,
const char *path, apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ const git_commit *commit;
+ git_tree *tree;
+ git_tree_entry *entry;
+ git_object *obj;
+ git_blob *blob;
+ const char *relpath;
+
+ SVN_ERR(find_branch(&commit, &relpath, root, path, pool));
+
+ if (!commit)
+ return SVN_FS__ERR_NOT_FILE(root->fs, path);
+
+ GIT2_ERR(git_commit_tree(&tree, commit));
+
+ SVN_ERR(find_tree_entry(&entry, tree, relpath, pool, pool));
+
+ if (!entry || git_tree_entry_type(entry) != GIT_OBJ_BLOB)
+ return SVN_FS__ERR_NOT_FILE(root->fs, path);
+
+ SVN_ERR(get_entry_object(&obj, tree, entry, pool));
+
+ blob = (git_blob*)obj;
+
+ *checksum = NULL; /* ### TODO: Get via DB cache */
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -304,7 +751,44 @@ fs_git_file_contents(svn_stream_t **cont
svn_fs_root_t *root, const char *path,
apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ const git_commit *commit;
+ git_tree *tree;
+ git_tree_entry *entry;
+ git_object *obj;
+ git_blob *blob;
+ const char *relpath;
+ svn_filesize_t sz;
+
+ SVN_ERR(find_branch(&commit, &relpath, root, path, pool));
+
+ if (!commit)
+ return SVN_FS__ERR_NOT_FILE(root->fs, path);
+
+ GIT2_ERR(git_commit_tree(&tree, commit));
+
+ SVN_ERR(find_tree_entry(&entry, tree, relpath, pool, pool));
+
+ if (!entry || git_tree_entry_type(entry) != GIT_OBJ_BLOB)
+ return SVN_FS__ERR_NOT_FILE(root->fs, path);
+
+ SVN_ERR(get_entry_object(&obj, tree, entry, pool));
+
+ blob = (git_blob*)obj;
+
+ sz = git_blob_rawsize(blob);
+
+ /* For now use the github 10 MB limit */
+ if (sz < (10 * 1024 * 1024))
+ {
+ svn_string_t *s = svn_string_ncreate(
+ git_blob_rawcontent(blob), (apr_size_t)sz, pool);
+
+ *contents = svn_stream_from_string(s, pool);
+ }
+ else
+ *contents = svn_stream_empty(pool);
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -315,7 +799,9 @@ fs_git_try_process_file_contents(svn_boo
void* baton,
apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ *success = FALSE;
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -345,12 +831,47 @@ fs_git_apply_text(svn_stream_t **content
}
static svn_error_t *
-fs_git_contents_changed(int *changed_p, svn_fs_root_t *root1,
- const char *path1, svn_fs_root_t *root2,
- const char *path2, svn_boolean_t strict,
+fs_git_contents_changed(int *changed_p,
+ svn_fs_root_t *root_a, const char *path_a,
+ svn_fs_root_t *root_b, const char *path_b,
+ svn_boolean_t strict,
apr_pool_t *scratch_pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ const git_commit *commit_a, *commit_b;
+ const char *relpath_a, *relpath_b;
+ git_tree *tree_a, *tree_b;
+ const git_tree_entry *entry_a, *entry_b;
+
+ SVN_ERR(find_branch(&commit_a, &relpath_a, root_a, path_a, scratch_pool));
+ SVN_ERR(find_branch(&commit_b, &relpath_b, root_b, path_b, scratch_pool));
+
+ if (!commit_a)
+ return SVN_FS__ERR_NOT_FILE(root_a->fs, path_a);
+ else if (!commit_b)
+ return SVN_FS__ERR_NOT_FILE(root_b->fs, path_b);
+
+ GIT2_ERR(git_commit_tree(&tree_a, commit_a));
+ GIT2_ERR(git_commit_tree(&tree_b, commit_b));
+
+ SVN_ERR(find_tree_entry(&entry_a, tree_a, relpath_a,
+ scratch_pool, scratch_pool));
+ SVN_ERR(find_tree_entry(&entry_b, tree_b, relpath_b,
+ scratch_pool, scratch_pool));
+
+ if (!entry_a)
+ return SVN_FS__ERR_NOT_FILE(root_a->fs, path_a);
+ else if (!entry_b)
+ return SVN_FS__ERR_NOT_FILE(root_b->fs, path_b);
+
+ if (!git_oid_cmp(git_tree_entry_id(entry_a),
+ git_tree_entry_id(entry_b)))
+ {
+ *changed_p = FALSE;
+ }
+ else
+ *changed_p = TRUE;
+
+ return SVN_NO_ERROR;
}
static svn_error_t *
@@ -361,7 +882,23 @@ fs_git_get_file_delta_stream(svn_txdelta
const char *target_path,
apr_pool_t *pool)
{
- return svn_error_create(APR_ENOTIMPL, NULL, NULL);
+ svn_stream_t *source, *target;
+ svn_txdelta_stream_t *delta_stream;
+
+ /* Get read functions for the source file contents. */
+ if (source_root && source_path)
+ SVN_ERR(fs_git_file_contents(&source, source_root, source_path, pool));
+ else
+ source = svn_stream_empty(pool);
+
+ /* Get read functions for the target file contents. */
+ SVN_ERR(fs_git_file_contents(&target, target_root, target_path, pool));
+
+ /* Create a delta stream that turns the ancestor into the target. */
+ svn_txdelta2(&delta_stream, source, target, TRUE, pool);
+
+ *stream_p = delta_stream;
+ return SVN_NO_ERROR;
}
/* Merging. */
@@ -450,6 +987,8 @@ svn_fs_git__revision_root(svn_fs_root_t
root->vtable = &root_vtable;
root->fsap_data = fgr;
+ fgr->branch_map = apr_hash_make(pool);
+
if (rev > 0)
{
git_oid *oid;