Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions stdlib/LibGit2/src/diff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function diff_tree(repo::GitRepo, tree::GitTree, pathspecs::AbstractString=""; c
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{DiffOptionsStruct}),
diff_ptr_ptr, repo, tree, isempty(pathspecs) ? C_NULL : pathspecs)
end
return GitDiff(repo, diff_ptr_ptr[])
return GitDiff(diff_ptr_ptr[])
end

"""
Expand All @@ -54,7 +54,7 @@ function diff_tree(repo::GitRepo, oldtree::GitTree, newtree::GitTree)
@check ccall((:git_diff_tree_to_tree, libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{DiffOptionsStruct}),
diff_ptr_ptr, repo, oldtree, newtree, C_NULL)
return GitDiff(repo, diff_ptr_ptr[])
return GitDiff(diff_ptr_ptr[])
end

"""
Expand All @@ -70,7 +70,7 @@ function GitDiffStats(diff::GitDiff)
@check ccall((:git_diff_get_stats, libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}),
diff_stat_ptr_ptr, diff)
return GitDiffStats(diff.owner, diff_stat_ptr_ptr[])
return GitDiffStats(diff_stat_ptr_ptr[])
end

"""
Expand Down Expand Up @@ -142,3 +142,34 @@ function Base.show(io::IO, diff::GitDiff)
println(io, "Number of deltas: $(count(diff))")
show(io, GitDiffStats(diff))
end

"""
GitDiff(content::AbstractString)

Parse a diff from a buffer. The `content` should be in unified diff format.
Returns a [`GitDiff`](@ref) object.

This is equivalent to [`git_diff_from_buffer`](https://libgit2.org/libgit2/#HEAD/group/diff/git_diff_from_buffer).

# Examples
```julia
diff_str = \"\"\"
diff --git a/file.txt b/file.txt
index 1234567..abcdefg 100644
--- a/file.txt
+++ b/file.txt
@@ -1 +1 @@
-old content
+new content
\"\"\"
diff = LibGit2.GitDiff(diff_str)
```
"""
function GitDiff(content::AbstractString)
ensure_initialized()
diff_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL)
@check ccall((:git_diff_from_buffer, libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Cstring, Csize_t),
diff_ptr_ptr, content, sizeof(content))
return GitDiff(diff_ptr_ptr[])
end
26 changes: 26 additions & 0 deletions stdlib/LibGit2/src/index.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,32 @@ function write_tree!(idx::GitIndex)
return oid_ptr[]
end

"""
write_tree_to!(repo::GitRepo, idx::GitIndex)::GitHash

Write the index `idx` as a [`GitTree`](@ref) to the given repository `repo`.
This is similar to [`write_tree!`](@ref) but allows writing the index to a
different repository than the one it may be associated with.

Trees will be recursively created for each subtree in `idx`. The returned
[`GitHash`](@ref) can be used to create a [`GitCommit`](@ref).

This is equivalent to [`git_index_write_tree_to`](https://libgit2.org/libgit2/#HEAD/group/index/git_index_write_tree_to).

# Examples
```julia
idx = LibGit2.GitIndex(source_repo)
tree_oid = LibGit2.write_tree_to!(target_repo, idx)
```
"""
function write_tree_to!(repo::GitRepo, idx::GitIndex)
ensure_initialized()
oid_ptr = Ref(GitHash())
@check ccall((:git_index_write_tree_to, libgit2), Cint,
(Ptr{GitHash}, Ptr{Cvoid}, Ptr{Cvoid}), oid_ptr, idx, repo)
return oid_ptr[]
end

function repository(idx::GitIndex)
if idx.owner === nothing
throw(GitError(Error.Index, Error.ENOTFOUND, "Index does not have an owning repository."))
Expand Down
32 changes: 32 additions & 0 deletions stdlib/LibGit2/src/tree.jl
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,35 @@ end
function Base.haskey(tree::GitTree, target::AbstractString)
return _getindex(tree, target) !== nothing
end

"""
apply_to_tree(repo::GitRepo, preimage::GitTree, diff::GitDiff, options::ApplyOptions=ApplyOptions())

Apply a [`GitDiff`](@ref) to a [`GitTree`](@ref), returning the resulting index.
The `preimage` is the tree to which the diff will be applied. The `diff` should be
generated from the `preimage` to some other tree.

The returned [`GitIndex`](@ref) contains the result of applying the diff and can be
written as a tree using [`write_tree_to!`](@ref).

This is equivalent to [`git_apply_to_tree`](https://libgit2.org/libgit2/#HEAD/group/apply/git_apply_to_tree).

# Examples
```julia
repo = LibGit2.GitRepo(repo_path)
tree1 = LibGit2.GitTree(repo, "HEAD^{tree}")
tree2 = LibGit2.GitTree(repo, "HEAD~1^{tree}")
diff = LibGit2.diff_tree(repo, tree1, tree2)
result_index = LibGit2.apply_to_tree(repo, tree1, diff)
tree_oid = LibGit2.write_tree_to!(repo, result_index)
```
"""
function apply_to_tree(repo::GitRepo, preimage::GitTree, diff::GitDiff, options::ApplyOptions=ApplyOptions())
ensure_initialized()
out_index_ptr = Ref{Ptr{Cvoid}}(C_NULL)
opts_ptr = Ref(options)
@check ccall((:git_apply_to_tree, libgit2), Cint,
(Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{ApplyOptions}),
out_index_ptr, repo, preimage, diff, opts_ptr)
return GitIndex(nothing, out_index_ptr[])
end
26 changes: 24 additions & 2 deletions stdlib/LibGit2/src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,28 @@ The fields represent:
end
@assert Base.allocatedinline(StatusOptions)

"""
LibGit2.ApplyOptions

Options for applying a diff.
Matches the [`git_apply_options`](https://libgit2.org/libgit2/#HEAD/type/git_apply_options) struct.

The fields represent:
* `version`: version of the struct in use, in case this changes later. For now, always `1`.
* `delta_cb`: optional callback that will be made before each delta is applied.
* `hunk_cb`: optional callback that will be made before each hunk is applied.
* `payload`: the payload for the callback functions.
* `flags`: flags controlling how the apply is performed (e.g., check mode).
"""
@kwdef struct ApplyOptions
version::Cuint = Cuint(1)
delta_cb::Ptr{Cvoid} = C_NULL
hunk_cb::Ptr{Cvoid} = C_NULL
payload::Any = nothing
flags::Cuint = Cuint(0)
end
@assert Base.allocatedinline(ApplyOptions)

"""
LibGit2.StatusEntry

Expand Down Expand Up @@ -1042,8 +1064,8 @@ for (typ, owntyp, sup, cname) in Tuple{Symbol,Any,Symbol,Symbol}[
(:GitRevWalker, :GitRepo, :AbstractGitObject, :git_revwalk),
(:GitReference, :GitRepo, :AbstractGitObject, :git_reference),
(:GitDescribeResult, :GitRepo, :AbstractGitObject, :git_describe_result),
(:GitDiff, :GitRepo, :AbstractGitObject, :git_diff),
(:GitDiffStats, :GitRepo, :AbstractGitObject, :git_diff_stats),
(:GitDiff, nothing, :AbstractGitObject, :git_diff),
(:GitDiffStats, nothing, :AbstractGitObject, :git_diff_stats),
(:GitAnnotated, :GitRepo, :AbstractGitObject, :git_annotated_commit),
(:GitRebase, :GitRepo, :AbstractGitObject, :git_rebase),
(:GitBlame, :GitRepo, :AbstractGitObject, :git_blame),
Expand Down
14 changes: 14 additions & 0 deletions stdlib/LibGit2/test/libgit2-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,20 @@ mktempdir() do dir
@test !LibGit2.isdirty(repo, cached=true)
@test !LibGit2.isdiff(repo, "HEAD", cached=true)
end

LibGit2.with(LibGit2.GitRepo(cache_repo)) do repo
diff_str = "diff --git a/test.txt b/test.txt\nindex 0000000..1111111 100644\n"
@test_throws LibGit2.GitError LibGit2.GitDiff(diff_str)

tree1 = LibGit2.GitTree(repo, "HEAD~1^{tree}")
tree2 = LibGit2.GitTree(repo, "HEAD^{tree}")
diff = LibGit2.diff_tree(repo, tree1, tree2)
idx = LibGit2.apply_to_tree(repo, tree1, diff)
@test idx isa LibGit2.GitIndex
oid = LibGit2.write_tree_to!(repo, idx)
@test oid isa LibGit2.GitHash
close(idx)
end
end
end

Expand Down