@@ -105,6 +105,80 @@
{:ok, changes} = Diff.diff_trees(repo, root1_sha, root2_sha)
assert [%{path: "src/main.ex", status: :modified}] = changes
end
test "file replaced by directory with neighboring ascii entries" do
repo = RepoHelper.memory_repo()
blob_foo = Blob.from_content("file content\n")
{:ok, blob_foo_sha} = Object.write(repo, blob_foo)
blob_bar = Blob.from_content("bar content\n")
{:ok, blob_bar_sha} = Object.write(repo, blob_bar)
blob_foobar = Blob.from_content("foo_bar content\n")
{:ok, blob_foobar_sha} = Object.write(repo, blob_foobar)
blob_footxt = Blob.from_content("foo.txt content\n")
{:ok, blob_footxt_sha} = Object.write(repo, blob_footxt)
# Old tree has "foo" (file), "foo.txt", "foo_bar"
tree1 =
Tree.new([
%{mode: "100644", name: "foo", sha: blob_foo_sha},
%{mode: "100644", name: "foo.txt", sha: blob_footxt_sha},
%{mode: "100644", name: "foo_bar", sha: blob_foobar_sha}
])
{:ok, tree1_sha} = Object.write(repo, tree1)
# Subtree for directory "foo" containing "bar.txt"
sub_foo = Tree.new([%{mode: "100644", name: "bar.txt", sha: blob_bar_sha}])
{:ok, sub_foo_sha} = Object.write(repo, sub_foo)
# New tree has "foo" (directory), "foo.txt", "foo_bar"
tree2 =
Tree.new([
%{mode: "40000", name: "foo", sha: sub_foo_sha},
%{mode: "100644", name: "foo.txt", sha: blob_footxt_sha},
%{mode: "100644", name: "foo_bar", sha: blob_foobar_sha}
])
{:ok, tree2_sha} = Object.write(repo, tree2)
{:ok, changes} = Diff.diff_trees(repo, tree1_sha, tree2_sha)
paths_and_statuses = Enum.map(changes, fn c -> {c.path, c.status} end)
assert {"foo", :deleted} in paths_and_statuses
assert {"foo/bar.txt", :added} in paths_and_statuses
# Unchanged neighbor files must NOT be in diff
refute {"foo.txt", :modified} in paths_and_statuses
refute {"foo_bar", :modified} in paths_and_statuses
end
test "directory replaced by file" do
repo = RepoHelper.memory_repo()
blob_bar = Blob.from_content("bar content\n")
{:ok, blob_bar_sha} = Object.write(repo, blob_bar)
blob_foo = Blob.from_content("new file content\n")
{:ok, blob_foo_sha} = Object.write(repo, blob_foo)
sub_foo = Tree.new([%{mode: "100644", name: "bar.txt", sha: blob_bar_sha}])
{:ok, sub_foo_sha} = Object.write(repo, sub_foo)
tree1 = Tree.new([%{mode: "40000", name: "foo", sha: sub_foo_sha}])
{:ok, tree1_sha} = Object.write(repo, tree1)
tree2 = Tree.new([%{mode: "100644", name: "foo", sha: blob_foo_sha}])
{:ok, tree2_sha} = Object.write(repo, tree2)
{:ok, changes} = Diff.diff_trees(repo, tree1_sha, tree2_sha)
paths_and_statuses = Enum.map(changes, fn c -> {c.path, c.status} end)
assert {"foo/bar.txt", :deleted} in paths_and_statuses
assert {"foo", :added} in paths_and_statuses
end
end
describe "diff_blobs" do
@@ -313,6 +387,81 @@
{:ok, diffs} = Diff.diff_commits(repo, c1_sha, c2_sha)
assert [%{path: "file.txt", status: :modified, hunks: hunks}] = diffs
assert hunks != []
end
test "diff between commits with identical tree returns empty list" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
blob1 = Blob.from_content("hello\n")
{:ok, b1_sha} = Object.write(repo, blob1)
tree1 = Tree.new([%{mode: "100644", name: "file.txt", sha: b1_sha}])
{:ok, t1_sha} = Object.write(repo, tree1)
c1 = %Commit{
tree: t1_sha,
parents: [],
author: "A <a@a.com> 1000000000 +0000",
committer: "A <a@a.com> 1000000000 +0000",
message: "first\n"
}
{:ok, c1_sha} = Object.write(repo, c1)
c2 = %Commit{
tree: t1_sha,
parents: [c1_sha],
author: "A <a@a.com> 1000000001 +0000",
committer: "A <a@a.com> 1000000001 +0000",
message: "empty commit\n"
}
{:ok, c2_sha} = Object.write(repo, c2)
assert {:ok, []} = Diff.diff_commits(repo, c1_sha, c2_sha)
end
test "diff_commits handles task crash or timeout gracefully without raising" do
repo = RepoHelper.memory_repo()
ExGitObjectstore.init(repo)
blob1 = Blob.from_content("hello\n")
{:ok, b1_sha} = Object.write(repo, blob1)
tree1 = Tree.new([%{mode: "100644", name: "file.txt", sha: b1_sha}])
{:ok, t1_sha} = Object.write(repo, tree1)
blob2 = Blob.from_content("world\n")
{:ok, b2_sha} = Object.write(repo, blob2)
tree2 = Tree.new([%{mode: "100644", name: "file.txt", sha: b2_sha}])
{:ok, t2_sha} = Object.write(repo, tree2)
c1 = %Commit{
tree: t1_sha,
parents: [],
author: "A <a@a.com> 1000000000 +0000",
committer: "A <a@a.com> 1000000000 +0000",
message: "first\n"
}
{:ok, c1_sha} = Object.write(repo, c1)
c2 = %Commit{
tree: t2_sha,
parents: [c1_sha],
author: "A <a@a.com> 1000000001 +0000",
committer: "A <a@a.com> 1000000001 +0000",
message: "second\n"
}
{:ok, c2_sha} = Object.write(repo, c2)
# Simulated task exit is isolated and returns safe stub
{:ok, diffs} =
Diff.diff_commits(repo, c1_sha, c2_sha,
file_diff_hook: fn _change -> exit(:simulated_crash) end
)
assert [%{path: "file.txt", status: :modified, hunks: [], binary: true}] = diffs
end
end
end