• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37474

pending completion
#37474

push

local

web-flow
irinterp: Allow setting all IR flags (#48993)

Currently, `IR_FLAG_NOTHROW` is the only flag that irinterp is allowed to
set on statements, under the assumption that in order for a call to
be irinterp-eligible, it must have been proven `:foldable`, thus `:effect_free`,
and thus `IR_FLAG_EFFECT_FREE` was assumed to have been set. That reasoning
was sound at the time this code was written, but have since introduced
`EFFECT_FREE_IF_INACCESSIBLEMEMONLY`, which breaks the reasoning that
an `:effect_free` inference for the whole function implies the flag on
every statement. As a result, we were failing to DCE otherwise dead
statements if the IR came from irinterp.

3 of 3 new or added lines in 1 file covered. (100.0%)

70258 of 82316 relevant lines covered (85.35%)

32461773.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.8
/stdlib/LibGit2/src/types.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
using Base: something
4
import Base.@kwdef
5
import .Consts: GIT_SUBMODULE_IGNORE, GIT_MERGE_FILE_FAVOR, GIT_MERGE_FILE, GIT_CONFIG
6

7
const OID_RAWSZ = 20
8
const OID_HEXSZ = OID_RAWSZ * 2
9
const OID_MINPREFIXLEN = 4
10

11
abstract type AbstractGitHash end
12

13
"""
14
    GitHash
15

16
A git object identifier, based on the sha-1 hash. It is a $OID_RAWSZ byte string
17
($OID_HEXSZ hex digits) used to identify a `GitObject` in a repository.
18
"""
19
struct GitHash <: AbstractGitHash
20
    val::NTuple{OID_RAWSZ, UInt8}
21
    GitHash(val::NTuple{OID_RAWSZ, UInt8}) = new(val)
18✔
22
end
23
GitHash() = GitHash(ntuple(i->zero(UInt8), OID_RAWSZ))
18✔
24
GitHash(h::GitHash) = h
×
25

26
"""
27
    GitShortHash(hash::GitHash, len::Integer)
28

29
A shortened git object identifier, which can be used to identify a git object when it is
30
unique, consisting of the initial `len` hexadecimal digits of `hash` (the remaining digits
31
are ignored).
32
"""
33
struct GitShortHash <: AbstractGitHash
34
    hash::GitHash   # underlying hash: unused digits are ignored
35
    len::Csize_t    # length in hex digits
36
end
37

38

39
"""
40
    LibGit2.TimeStruct
41

42
Time in a signature.
43
Matches the [`git_time`](https://libgit2.org/libgit2/#HEAD/type/git_time) struct.
44
"""
45
struct TimeStruct
46
    time::Int64     # time in seconds from epoch
47
    offset::Cint    # timezone offset in minutes
48
    @static if LibGit2.VERSION >= v"0.27.0"
49
        sign::Cchar
50
    end
51
end
52

53
"""
54
    LibGit2.SignatureStruct
55

56
An action signature (e.g. for committers, taggers, etc).
57
Matches the [`git_signature`](https://libgit2.org/libgit2/#HEAD/type/git_signature) struct.
58

59
The fields represent:
60
  * `name`: The full name of the committer or author of the commit.
61
  * `email`: The email at which the committer/author can be contacted.
62
  * `when`: a [`TimeStruct`](@ref) indicating when the commit was
63
     authored/committed into the repository.
64
"""
65
struct SignatureStruct
66
    name::Ptr{UInt8}  # full name of the author
67
    email::Ptr{UInt8} # email of the author
68
    when::TimeStruct  # time when the action happened
69
end
70

71
"""
72
    LibGit2.StrArrayStruct
73

74
A LibGit2 representation of an array of strings.
75
Matches the [`git_strarray`](https://libgit2.org/libgit2/#HEAD/type/git_strarray) struct.
76

77
When fetching data from LibGit2, a typical usage would look like:
78
```julia
79
sa_ref = Ref(StrArrayStruct())
80
@check ccall(..., (Ptr{StrArrayStruct},), sa_ref)
81
res = convert(Vector{String}, sa_ref[])
82
free(sa_ref)
83
```
84
In particular, note that `LibGit2.free` should be called afterward on the `Ref` object.
85

86
Conversely, when passing a vector of strings to LibGit2, it is generally simplest to rely
87
on implicit conversion:
88
```julia
89
strs = String[...]
90
@check ccall(..., (Ptr{StrArrayStruct},), strs)
91
```
92
Note that no call to `free` is required as the data is allocated by Julia.
93
"""
94
struct StrArrayStruct
95
   strings::Ptr{Cstring}
×
96
   count::Csize_t
97
end
98
StrArrayStruct() = StrArrayStruct(C_NULL, 0)
×
99

100
function free(sa_ref::Base.Ref{StrArrayStruct})
×
101
    ensure_initialized()
×
102
    ccall((:git_strarray_free, :libgit2), Cvoid, (Ptr{StrArrayStruct},), sa_ref)
×
103
end
104

105
"""
106
    LibGit2.Buffer
107

108
A data buffer for exporting data from libgit2.
109
Matches the [`git_buf`](https://libgit2.org/libgit2/#HEAD/type/git_buf) struct.
110

111
When fetching data from LibGit2, a typical usage would look like:
112
```julia
113
buf_ref = Ref(Buffer())
114
@check ccall(..., (Ptr{Buffer},), buf_ref)
115
# operation on buf_ref
116
free(buf_ref)
117
```
118
In particular, note that `LibGit2.free` should be called afterward on the `Ref` object.
119
"""
120
struct Buffer
121
    ptr::Ptr{Cchar}
122
    asize::Csize_t
123
    size::Csize_t
124
end
125
Buffer() = Buffer(C_NULL, 0, 0)
×
126

127
function free(buf_ref::Base.Ref{Buffer})
×
128
    ensure_initialized()
18✔
129
    ccall((:git_buf_free, :libgit2), Cvoid, (Ptr{Buffer},), buf_ref)
18✔
130
end
131

132
"""
133
    LibGit2.CheckoutOptions
134

135
Matches the [`git_checkout_options`](https://libgit2.org/libgit2/#HEAD/type/git_checkout_options) struct.
136

137
The fields represent:
138
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
139
  * `checkout_strategy`: determine how to handle conflicts and whether to force the
140
     checkout/recreate missing files.
141
  * `disable_filters`: if nonzero, do not apply filters like CLRF (to convert file newlines between UNIX and DOS).
142
  * `dir_mode`: read/write/access mode for any directories involved in the checkout. Default is `0755`.
143
  * `file_mode`: read/write/access mode for any files involved in the checkout.
144
     Default is `0755` or `0644`, depending on the blob.
145
  * `file_open_flags`: bitflags used to open any files during the checkout.
146
  * `notify_flags`: Flags for what sort of conflicts the user should be notified about.
147
  * `notify_cb`: An optional callback function to notify the user if a checkout conflict occurs.
148
     If this function returns a non-zero value, the checkout will be cancelled.
149
  * `notify_payload`: Payload for the notify callback function.
150
  * `progress_cb`: An optional callback function to display checkout progress.
151
  * `progress_payload`: Payload for the progress callback.
152
  * `paths`: If not empty, describes which paths to search during the checkout.
153
     If empty, the checkout will occur over all files in the repository.
154
  * `baseline`: Expected content of the [`workdir`](@ref), captured in a (pointer to a)
155
     [`GitTree`](@ref). Defaults to the state of the tree at HEAD.
156
  * `baseline_index`: Expected content of the [`workdir`](@ref), captured in a (pointer to a)
157
     `GitIndex`. Defaults to the state of the index at HEAD.
158
  * `target_directory`: If not empty, checkout to this directory instead of the `workdir`.
159
  * `ancestor_label`: In case of conflicts, the name of the common ancestor side.
160
  * `our_label`: In case of conflicts, the name of "our" side.
161
  * `their_label`: In case of conflicts, the name of "their" side.
162
  * `perfdata_cb`: An optional callback function to display performance data.
163
  * `perfdata_payload`: Payload for the performance callback.
164
"""
165
@kwdef struct CheckoutOptions
166
    version::Cuint               = Cuint(1)
167

168
    checkout_strategy::Cuint     = Consts.CHECKOUT_SAFE
169

170
    disable_filters::Cint        = Cint(0)
171
    dir_mode::Cuint              = Cuint(0)
172
    file_mode::Cuint             = Cuint(0)
173
    file_open_flags::Cint        = Cint(0)
174

175
    notify_flags::Cuint          = Consts.CHECKOUT_NOTIFY_NONE
176
    notify_cb::Ptr{Cvoid}        = C_NULL
177
    notify_payload::Any          = nothing
178

179
    progress_cb::Ptr{Cvoid}      = C_NULL
180
    progress_payload::Any        = nothing
181

182
    paths::StrArrayStruct        = StrArrayStruct()
183

184
    baseline::Ptr{Cvoid}         = C_NULL
185
    baseline_index::Ptr{Cvoid}   = C_NULL
186

187
    target_directory::Cstring    = Cstring(C_NULL)
188
    ancestor_label::Cstring      = Cstring(C_NULL)
189
    our_label::Cstring           = Cstring(C_NULL)
190
    their_label::Cstring         = Cstring(C_NULL)
191

192
    perfdata_cb::Ptr{Cvoid}      = C_NULL
193
    perfdata_payload::Any        = Nothing
194
end
195
@assert Base.allocatedinline(CheckoutOptions)
196

197
"""
198
    LibGit2.TransferProgress
199

200
Transfer progress information used by the `transfer_progress` remote callback.
201
Matches the [`git_indexer_progress`](https://libgit2.org/libgit2/#HEAD/type/git_indexer_progress) struct.
202
"""
203
@kwdef struct TransferProgress
204
    total_objects::Cuint    = Cuint(0)
205
    indexed_objects::Cuint  = Cuint(0)
206
    received_objects::Cuint = Cuint(0)
207
    local_objects::Cuint    = Cuint(0)
208
    total_deltas::Cuint     = Cuint(0)
209
    indexed_deltas::Cuint   = Cuint(0)
210
    received_bytes::Csize_t = Csize_t(0)
211
end
212
@assert Base.allocatedinline(TransferProgress)
213

214
"""
215
    LibGit2.RemoteCallbacks
216

217
Callback settings.
218
Matches the [`git_remote_callbacks`](https://libgit2.org/libgit2/#HEAD/type/git_remote_callbacks) struct.
219
"""
220
@kwdef struct RemoteCallbacks
10✔
221
    version::Cuint                     = Cuint(1)
5✔
222
    sideband_progress::Ptr{Cvoid}      = C_NULL
223
    completion::Ptr{Cvoid}             = C_NULL
224
    credentials::Ptr{Cvoid}            = C_NULL
225
    certificate_check::Ptr{Cvoid}      = certificate_cb()
226
    transfer_progress::Ptr{Cvoid}      = C_NULL
227
    update_tips::Ptr{Cvoid}            = C_NULL
228
    pack_progress::Ptr{Cvoid}          = C_NULL
229
    push_transfer_progress::Ptr{Cvoid} = C_NULL
230
    push_update_reference::Ptr{Cvoid}  = C_NULL
231
    push_negotiation::Ptr{Cvoid}       = C_NULL
232
    transport::Ptr{Cvoid}              = C_NULL
233
    @static if LibGit2.VERSION >= v"1.2.0"
234
        remote_ready::Ptr{Cvoid}       = C_NULL
235
    end
236
    payload::Any                       = nothing
237
    @static if LibGit2.VERSION >= v"0.99.0"
238
        resolve_url::Ptr{Cvoid}        = C_NULL
239
    end
240
end
241
@assert Base.allocatedinline(RemoteCallbacks)
242

243
"""
244
    LibGit2.Callbacks
245

246
A dictionary which containing the callback name as the key and the value as a tuple of the
247
callback function and payload.
248

249
The `Callback` dictionary to construct `RemoteCallbacks` allows each callback to use a
250
distinct payload. Each callback, when called, will receive `Dict` which will hold the
251
callback's custom payload which can be accessed using the callback name.
252

253
# Examples
254
```julia-repl
255
julia> c = LibGit2.Callbacks(:credentials => (LibGit2.credentials_cb(), LibGit2.CredentialPayload()));
256

257
julia> LibGit2.clone(url, callbacks=c);
258
```
259

260
See [`git_remote_callbacks`](https://libgit2.org/libgit2/#HEAD/type/git_remote_callbacks)
261
for details on supported callbacks.
262
"""
263
const Callbacks = Dict{Symbol, Tuple{Ptr{Cvoid}, Any}}
264

265
function RemoteCallbacks(c::Callbacks)
5✔
266
    callbacks = Dict{Symbol, Ptr{Cvoid}}()
5✔
267
    payloads = Dict{Symbol, Any}()
5✔
268

269
    for (name, (callback, payload)) in c
10✔
270
        callbacks[name] = callback
6✔
271
        payloads[name] = payload
6✔
272
    end
7✔
273

274
    RemoteCallbacks(; payload=payloads, callbacks...)
5✔
275
end
276

277

278
"""
279
    LibGit2.ProxyOptions
280

281
Options for connecting through a proxy.
282

283
Matches the [`git_proxy_options`](https://libgit2.org/libgit2/#HEAD/type/git_proxy_options) struct.
284

285
The fields represent:
286
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
287
  * `proxytype`: an `enum` for the type of proxy to use.
288
     Defined in [`git_proxy_t`](https://libgit2.org/libgit2/#HEAD/type/git_proxy_t).
289
     The corresponding Julia enum is `GIT_PROXY` and has values:
290
     - `PROXY_NONE`: do not attempt the connection through a proxy.
291
     - `PROXY_AUTO`: attempt to figure out the proxy configuration from the git configuration.
292
     - `PROXY_SPECIFIED`: connect using the URL given in the `url` field of this struct.
293
     Default is to auto-detect the proxy type.
294
  * `url`: the URL of the proxy.
295
  * `credential_cb`: a pointer to a callback function which will be called if the remote
296
    requires authentication to connect.
297
  * `certificate_cb`: a pointer to a callback function which will be called if certificate
298
    verification fails. This lets the user decide whether or not to keep connecting. If
299
    the function returns `1`, connecting will be allowed. If it returns `0`, the connection
300
    will not be allowed. A negative value can be used to return errors.
301
  * `payload`: the payload to be provided to the two callback functions.
302

303
# Examples
304
```julia-repl
305
julia> fo = LibGit2.FetchOptions(
306
           proxy_opts = LibGit2.ProxyOptions(url = Cstring("https://my_proxy_url.com")))
307

308
julia> fetch(remote, "master", options=fo)
309
```
310
"""
311
@kwdef struct ProxyOptions
10✔
312
    version::Cuint               = Cuint(1)
5✔
313
    proxytype::Consts.GIT_PROXY  = Consts.PROXY_AUTO
314
    url::Cstring                 = Cstring(C_NULL)
315
    credential_cb::Ptr{Cvoid}    = C_NULL
316
    certificate_cb::Ptr{Cvoid}   = certificate_cb()
317
    payload::Any                 = nothing
318
end
319
@assert Base.allocatedinline(ProxyOptions)
320

321
"""
322
    LibGit2.FetchOptions
323

324
Matches the [`git_fetch_options`](https://libgit2.org/libgit2/#HEAD/type/git_fetch_options) struct.
325

326
The fields represent:
327
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
328
  * `callbacks`: remote callbacks to use during the fetch.
329
  * `prune`: whether to perform a prune after the fetch or not. The default is to
330
     use the setting from the `GitConfig`.
331
  * `update_fetchhead`: whether to update the [`FetchHead`](@ref) after the fetch.
332
     The default is to perform the update, which is the normal git behavior.
333
  * `download_tags`: whether to download tags present at the remote or not. The default
334
     is to request the tags for objects which are being downloaded anyway from the server.
335
  * `proxy_opts`: options for connecting to the remote through a proxy. See [`ProxyOptions`](@ref).
336
     Only present on libgit2 versions newer than or equal to 0.25.0.
337
  * `custom_headers`: any extra headers needed for the fetch. Only present on libgit2 versions
338
     newer than or equal to 0.24.0.
339
"""
340
@kwdef struct FetchOptions
10✔
341
    version::Cuint                     = Cuint(1)
5✔
342
    callbacks::RemoteCallbacks         = RemoteCallbacks()
343
    prune::Cint                        = Consts.FETCH_PRUNE_UNSPECIFIED
344
    update_fetchhead::Cint             = Cint(1)
345
    download_tags::Cint                = Consts.REMOTE_DOWNLOAD_TAGS_AUTO
346
    @static if LibGit2.VERSION >= v"0.25.0"
347
        proxy_opts::ProxyOptions       = ProxyOptions()
348
    end
349
    @static if LibGit2.VERSION >= v"1.4.0"
350
        follow_redirects::Cuint        = Cuint(0)
351
    end
352
    @static if LibGit2.VERSION >= v"0.24.0"
353
        custom_headers::StrArrayStruct = StrArrayStruct()
354
    end
355
end
356
@assert Base.allocatedinline(FetchOptions)
357

358

359
"""
360
    LibGit2.CloneOptions
361

362
Matches the [`git_clone_options`](https://libgit2.org/libgit2/#HEAD/type/git_clone_options) struct.
363

364
The fields represent:
365
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
366
  * `checkout_opts`: The options for performing the checkout of the remote as part of the clone.
367
  * `fetch_opts`: The options for performing the pre-checkout fetch of the remote as part of the clone.
368
  * `bare`: If `0`, clone the full remote repository. If non-zero, perform a bare clone, in which
369
     there is no local copy of the source files in the repository and the [`gitdir`](@ref) and [`workdir`](@ref)
370
     are the same.
371
  * `localclone`: Flag whether to clone a local object database or do a fetch. The default is to let git decide.
372
     It will not use the git-aware transport for a local clone, but will use it for URLs which begin with `file://`.
373
  * `checkout_branch`: The name of the branch to checkout. If an empty string, the default branch of the
374
     remote will be checked out.
375
  * `repository_cb`: An optional callback which will be used to create the *new* repository into which
376
     the clone is made.
377
  * `repository_cb_payload`: The payload for the repository callback.
378
  * `remote_cb`: An optional callback used to create the [`GitRemote`](@ref) before making the clone from it.
379
  * `remote_cb_payload`: The payload for the remote callback.
380
"""
381
@kwdef struct CloneOptions
10✔
382
    version::Cuint                      = Cuint(1)
5✔
383
    checkout_opts::CheckoutOptions      = CheckoutOptions()
384
    fetch_opts::FetchOptions            = FetchOptions()
385
    bare::Cint                          = Cint(0)
386
    localclone::Cint                    = Consts.CLONE_LOCAL_AUTO
387
    checkout_branch::Cstring            = Cstring(C_NULL)
388
    repository_cb::Ptr{Cvoid}           = C_NULL
389
    repository_cb_payload::Any          = nothing
390
    remote_cb::Ptr{Cvoid}               = C_NULL
391
    remote_cb_payload::Any              = nothing
392
end
393
@assert Base.allocatedinline(CloneOptions)
394

395
"""
396
    LibGit2.DiffOptionsStruct
397

398
Matches the [`git_diff_options`](https://libgit2.org/libgit2/#HEAD/type/git_diff_options) struct.
399

400
The fields represent:
401
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
402
  * `flags`: flags controlling which files will appear in the diff. Defaults to `DIFF_NORMAL`.
403
  * `ignore_submodules`: whether to look at files in submodules or not. Defaults to
404
    `SUBMODULE_IGNORE_UNSPECIFIED`, which means the submodule's configuration will control
405
     whether it appears in the diff or not.
406
  * `pathspec`: path to files to include in the diff. Default is to use all files in the repository.
407
  * `notify_cb`: optional callback which will notify the user of changes to the diff as file deltas are
408
     added to it.
409
  * `progress_cb`: optional callback which will display diff progress. Only relevant on libgit2 versions
410
     at least as new as 0.24.0.
411
  * `payload`: the payload to pass to `notify_cb` and `progress_cb`.
412
  * `context_lines`: the number of *unchanged* lines used to define the edges of a hunk.
413
     This is also the number of lines which will be shown before/after a hunk to provide
414
     context. Default is 3.
415
  * `interhunk_lines`: the maximum number of *unchanged* lines *between* two separate
416
     hunks allowed before the hunks will be combined. Default is 0.
417
  * `id_abbrev`: sets the length of the abbreviated [`GitHash`](@ref) to print.
418
     Default is `7`.
419
  * `max_size`: the maximum file size of a blob. Above this size, it will be treated
420
     as a binary blob. The default is 512 MB.
421
  * `old_prefix`: the virtual file directory in which to place old files on one side
422
     of the diff. Default is `"a"`.
423
  * `new_prefix`: the virtual file directory in which to place new files on one side
424
     of the diff. Default is `"b"`.
425
"""
426
@kwdef struct DiffOptionsStruct
×
427
    version::Cuint                           = Consts.DIFF_OPTIONS_VERSION
×
428
    flags::UInt32                            = Consts.DIFF_NORMAL
429

430
    # options controlling which files are in the diff
431
    ignore_submodules::GIT_SUBMODULE_IGNORE  = Consts.SUBMODULE_IGNORE_UNSPECIFIED
432
    pathspec::StrArrayStruct                 = StrArrayStruct()
433
    notify_cb::Ptr{Cvoid}                    = C_NULL
434
    @static if LibGit2.VERSION >= v"0.24.0"
435
        progress_cb::Ptr{Cvoid}              = C_NULL
436
    end
437
    payload::Any                             = nothing
438

439
    # options controlling how the diff text is generated
440
    context_lines::UInt32                    = UInt32(3)
441
    interhunk_lines::UInt32                  = UInt32(0)
442
    id_abbrev::UInt16                        = UInt16(7)
443
    max_size::Int64                          = Int64(512*1024*1024) #512Mb
444
    old_prefix::Cstring                      = Cstring(C_NULL)
445
    new_prefix::Cstring                      = Cstring(C_NULL)
446
end
447
@assert Base.allocatedinline(DiffOptionsStruct)
448

449
"""
450
    LibGit2.DescribeOptions
451

452
Matches the [`git_describe_options`](https://libgit2.org/libgit2/#HEAD/type/git_describe_options) struct.
453

454
The fields represent:
455
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
456
  * `max_candidates_tags`: consider this many most recent tags in `refs/tags` to describe a commit.
457
     Defaults to 10 (so that the 10 most recent tags would be examined to see if they describe a commit).
458
  * `describe_strategy`: whether to consider all entries in `refs/tags` (equivalent to `git-describe --tags`)
459
     or all entries in `refs/` (equivalent to `git-describe --all`). The default is to only show annotated tags.
460
     If `Consts.DESCRIBE_TAGS` is passed, all tags, annotated or not, will be considered.
461
     If `Consts.DESCRIBE_ALL` is passed, any ref in `refs/` will be considered.
462
  * `pattern`: only consider tags which match `pattern`. Supports glob expansion.
463
  * `only_follow_first_parent`: when finding the distance from a matching reference to the described
464
     object, only consider the distance from the first parent.
465
  * `show_commit_oid_as_fallback`: if no matching reference can be found which describes a commit, show the
466
     commit's [`GitHash`](@ref) instead of throwing an error (the default behavior).
467
"""
468
@kwdef struct DescribeOptions
469
    version::Cuint                    = Cuint(1)
470
    max_candidates_tags::Cuint        = Cuint(10)
471
    describe_strategy::Cuint          = Consts.DESCRIBE_DEFAULT
472

473
    pattern::Cstring                  = Cstring(C_NULL)
474
    only_follow_first_parent::Cint    = Cint(0)
475
    show_commit_oid_as_fallback::Cint = Cint(0)
476
end
477
@assert Base.allocatedinline(DescribeOptions)
478

479
"""
480
    LibGit2.DescribeFormatOptions
481

482
Matches the [`git_describe_format_options`](https://libgit2.org/libgit2/#HEAD/type/git_describe_format_options) struct.
483

484
The fields represent:
485
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
486
  * `abbreviated_size`: lower bound on the size of the abbreviated `GitHash` to use, defaulting to `7`.
487
  * `always_use_long_format`: set to `1` to use the long format for strings even if a short format can be used.
488
  * `dirty_suffix`: if set, this will be appended to the end of the description string if the [`workdir`](@ref) is dirty.
489
"""
490
@kwdef struct DescribeFormatOptions
491
    version::Cuint               = Cuint(1)
492
    abbreviated_size::Cuint      = Cuint(7)
493
    always_use_long_format::Cint = Cint(0)
494
    dirty_suffix::Cstring        = Cstring(C_NULL)
495
end
496
@assert Base.allocatedinline(DescribeFormatOptions)
497

498
"""
499
    LibGit2.DiffFile
500

501
Description of one side of a delta.
502
Matches the [`git_diff_file`](https://libgit2.org/libgit2/#HEAD/type/git_diff_file) struct.
503

504
The fields represent:
505
  * `id`: the [`GitHash`](@ref) of the item in the diff. If the item is empty on this
506
     side of the diff (for instance, if the diff is of the removal of a file), this will
507
     be `GitHash(0)`.
508
  * `path`: a `NULL` terminated path to the item relative to the working directory of the repository.
509
  * `size`: the size of the item in bytes.
510
  * `flags`: a combination of the [`git_diff_flag_t`](https://libgit2.org/libgit2/#HEAD/type/git_diff_flag_t)
511
     flags. The `i`th bit of this integer sets the `i`th flag.
512
  * `mode`: the [`stat`](@ref) mode for the item.
513
  * `id_abbrev`: only present in LibGit2 versions newer than or equal to `0.25.0`.
514
     The length of the `id` field when converted using [`string`](@ref). Usually equal to `OID_HEXSZ` ($OID_HEXSZ).
515
"""
516
struct DiffFile
517
    id::GitHash
518
    path::Cstring
519
    size::Int64
520
    flags::UInt32
521
    mode::UInt16
522
    @static if LibGit2.VERSION >= v"0.25.0"
523
        id_abbrev::UInt16
524
    end
525
end
526

527
function Base.show(io::IO, df::DiffFile)
×
528
    println(io, "DiffFile:")
×
529
    println(io, "Oid: $(df.id)")
×
530
    println(io, "Path: $(df.path)")
×
531
    println(io, "Size: $(df.size)")
×
532
end
533

534
"""
535
    LibGit2.DiffDelta
536

537
Description of changes to one entry.
538
Matches the [`git_diff_delta`](https://libgit2.org/libgit2/#HEAD/type/git_diff_delta) struct.
539

540
The fields represent:
541
  * `status`: One of `Consts.DELTA_STATUS`, indicating whether the file has been added/modified/deleted.
542
  * `flags`: Flags for the delta and the objects on each side. Determines whether to treat the file(s)
543
     as binary/text, whether they exist on each side of the diff, and whether the object ids are known
544
     to be correct.
545
  * `similarity`: Used to indicate if a file has been renamed or copied.
546
  * `nfiles`: The number of files in the delta (for instance, if the delta
547
     was run on a submodule commit id, it may contain more than one file).
548
  * `old_file`: A [`DiffFile`](@ref) containing information about the file(s) before the changes.
549
  * `new_file`: A [`DiffFile`](@ref) containing information about the file(s) after the changes.
550
"""
551
struct DiffDelta
552
    status::Cint
553
    flags::UInt32
554
    similarity::UInt16
555
    nfiles::UInt16
556
    old_file::DiffFile
557
    new_file::DiffFile
558
end
559

560
function Base.show(io::IO, dd::DiffDelta)
×
561
    println(io, "DiffDelta:")
×
562
    println(io, "Status: $(Consts.DELTA_STATUS(dd.status))")
×
563
    println(io, "Number of files: $(dd.nfiles)")
×
564
    println(io, "Old file:\n$(dd.old_file)")
×
565
    println(io, "New file:\n$(dd.new_file)")
×
566
end
567

568
"""
569
    LibGit2.MergeOptions
570

571
Matches the [`git_merge_options`](https://libgit2.org/libgit2/#HEAD/type/git_merge_options) struct.
572

573
The fields represent:
574
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
575
  * `flags`: an `enum` for flags describing merge behavior.
576
     Defined in [`git_merge_flag_t`](https://github.com/libgit2/libgit2/blob/HEAD/include/git2/merge.h#L95).
577
     The corresponding Julia enum is `GIT_MERGE` and has values:
578
     - `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common
579
       ancestor and the "ours" or "theirs" side of the merge. Allows merges where
580
       a file has been renamed.
581
     - `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather
582
       than trying to resolve it.
583
     - `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting
584
       from the merge.
585
     - `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases,
586
       use the first one, rather than trying to recursively merge the bases.
587
  * `rename_threshold`: how similar two files must to consider one a rename of the other.
588
    This is an integer that sets the percentage similarity. The default is 50.
589
  * `target_limit`: the maximum number of files to compare with to look for renames.
590
    The default is 200.
591
  * `metric`: optional custom function to use to determine the similarity between two
592
    files for rename detection.
593
  * `recursion_limit`: the upper limit on the number of merges of common ancestors to
594
    perform to try to build a new virtual merge base for the merge. The default is no
595
    limit. This field is only present on libgit2 versions newer than 0.24.0.
596
  * `default_driver`: the merge driver to use if both sides have changed. This field
597
    is only present on libgit2 versions newer than 0.25.0.
598
  * `file_favor`: how to handle conflicting file contents for the `text` driver.
599
    - `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section,
600
       make a note of the conflict in the index which `git checkout` will use to create
601
       a merge file, which the user can then reference to resolve the conflicts. This is
602
       the default.
603
    - `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section,
604
       use the version in the "ours" side of the merge in the index.
605
    - `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section,
606
       use the version in the "theirs" side of the merge in the index.
607
    - `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section,
608
       include each unique line from both sides in the file which is put into the index.
609
  * `file_flags`: guidelines for merging files.
610
"""
611
@kwdef struct MergeOptions
612
    version::Cuint                    = Cuint(1)
613
    flags::Cint                       = Cint(0)
614
    rename_threshold::Cuint           = Cuint(50)
615
    target_limit::Cuint               = Cuint(200)
616
    metric::Ptr{Cvoid}                = C_NULL
617
    @static if LibGit2.VERSION >= v"0.24.0"
618
        recursion_limit::Cuint        = Cuint(0)
619
    end
620
    @static if LibGit2.VERSION >= v"0.25.0"
621
        default_driver::Cstring       = Cstring(C_NULL)
622
    end
623
    file_favor::GIT_MERGE_FILE_FAVOR  = Consts.MERGE_FILE_FAVOR_NORMAL
624
    file_flags::GIT_MERGE_FILE        = Consts.MERGE_FILE_DEFAULT
625
end
626
@assert Base.allocatedinline(MergeOptions)
627

628
"""
629
    LibGit2.BlameOptions
630

631
Matches the [`git_blame_options`](https://libgit2.org/libgit2/#HEAD/type/git_blame_options) struct.
632

633
The fields represent:
634
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
635
  * `flags`: one of `Consts.BLAME_NORMAL` or `Consts.BLAME_FIRST_PARENT` (the other blame flags
636
     are not yet implemented by libgit2).
637
  * `min_match_characters`: the minimum number of *alphanumeric* characters which much change
638
    in a commit in order for the change to be associated with that commit. The default is 20.
639
    Only takes effect if one of the `Consts.BLAME_*_COPIES` flags are used, which libgit2 does
640
    not implement yet.
641
  * `newest_commit`: the [`GitHash`](@ref) of the newest commit from which to look at changes.
642
  * `oldest_commit`: the [`GitHash`](@ref) of the oldest commit from which to look at changes.
643
  * `min_line`: the first line of the file from which to starting blaming. The default is `1`.
644
  * `max_line`: the last line of the file to which to blame. The default is `0`, meaning the
645
    last line of the file.
646
"""
647
@kwdef struct BlameOptions
648
    version::Cuint                    = Cuint(1)
649
    flags::UInt32                     = UInt32(0)
650
    min_match_characters::UInt16      = UInt16(20)
651
    newest_commit::GitHash            = GitHash()
652
    oldest_commit::GitHash            = GitHash()
653
    min_line::Csize_t                 = Csize_t(1)
654
    max_line::Csize_t                 = Csize_t(0)
655
end
656
@assert Base.allocatedinline(BlameOptions)
657

658

659
"""
660
    LibGit2.PushOptions
661

662
Matches the [`git_push_options`](https://libgit2.org/libgit2/#HEAD/type/git_push_options) struct.
663

664
The fields represent:
665
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
666
  * `parallelism`: if a pack file must be created, this variable sets the number of worker
667
     threads which will be spawned by the packbuilder. If `0`, the packbuilder will auto-set
668
     the number of threads to use. The default is `1`.
669
  * `callbacks`: the callbacks (e.g. for authentication with the remote) to use for the push.
670
  * `proxy_opts`: only relevant if the LibGit2 version is greater than or equal to `0.25.0`.
671
     Sets options for using a proxy to communicate with a remote. See [`ProxyOptions`](@ref)
672
     for more information.
673
  * `custom_headers`: only relevant if the LibGit2 version is greater than or equal to `0.24.0`.
674
     Extra headers needed for the push operation.
675
"""
676
@kwdef struct PushOptions
677
    version::Cuint                     = Cuint(1)
678
    parallelism::Cint                  = Cint(1)
679
    callbacks::RemoteCallbacks         = RemoteCallbacks()
680
    @static if LibGit2.VERSION >= v"0.25.0"
681
        proxy_opts::ProxyOptions       = ProxyOptions()
682
    end
683
    @static if LibGit2.VERSION >= v"1.4.0"
684
        follow_redirects::Cuint        = Cuint(0)
685
    end
686
    @static if LibGit2.VERSION >= v"0.24.0"
687
        custom_headers::StrArrayStruct = StrArrayStruct()
688
    end
689
end
690
@assert Base.allocatedinline(PushOptions)
691

692

693
"""
694
    LibGit2.CherrypickOptions
695

696
Matches the [`git_cherrypick_options`](https://libgit2.org/libgit2/#HEAD/type/git_cherrypick_options) struct.
697

698
The fields represent:
699
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
700
  * `mainline`: if cherrypicking a merge commit, specifies the parent number (starting at `1`)
701
    which will allow cherrypick to apply the changes relative to that parent. Only relevant if
702
    cherrypicking a merge commit. Default is `0`.
703
  * `merge_opts`: options for merging the changes in. See [`MergeOptions`](@ref) for more information.
704
  * `checkout_opts`: options for the checkout of the commit being cherrypicked. See [`CheckoutOptions`](@ref)
705
     for more information.
706
"""
707
@kwdef struct CherrypickOptions
708
    version::Cuint = Cuint(1)
709
    mainline::Cuint = Cuint(0)
710
    merge_opts::MergeOptions = MergeOptions()
711
    checkout_opts::CheckoutOptions = CheckoutOptions()
712
end
713
@assert Base.allocatedinline(CherrypickOptions)
714

715

716
"""
717
    LibGit2.IndexTime
718

719
Matches the [`git_index_time`](https://libgit2.org/libgit2/#HEAD/type/git_index_time) struct.
720
"""
721
struct IndexTime
722
    seconds::Int64
723
    nanoseconds::Cuint
724
end
725

726
"""
727
    LibGit2.IndexEntry
728

729
In-memory representation of a file entry in the index.
730
Matches the [`git_index_entry`](https://libgit2.org/libgit2/#HEAD/type/git_index_entry) struct.
731
"""
732
struct IndexEntry
733
    ctime::IndexTime
734
    mtime::IndexTime
735

736
    dev::UInt32
737
    ino::UInt32
738
    mode::UInt32
739
    uid::UInt32
740
    gid::UInt32
741
    file_size::Int64
742

743
    id::GitHash
744

745
    flags::UInt16
746
    flags_extended::UInt16
747

748
    path::Ptr{UInt8}
749
end
750
Base.show(io::IO, ie::IndexEntry) = print(io, "IndexEntry($(string(ie.id)))")
×
751

752
"""
753
    LibGit2.RebaseOptions
754

755
Matches the `git_rebase_options` struct.
756

757
The fields represent:
758
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
759
  * `quiet`: inform other git clients helping with/working on the rebase that the rebase
760
    should be done "quietly". Used for interoperability. The default is `1`.
761
  * `inmemory`: start an in-memory rebase. Callers working on the rebase can go through its
762
    steps and commit any changes, but cannot rewind HEAD or update the repository. The
763
    [`workdir`](@ref) will not be modified. Only present on libgit2 versions newer than or equal to 0.24.0.
764
  * `rewrite_notes_ref`: name of the reference to notes to use to rewrite the commit notes as
765
    the rebase is finished.
766
  * `merge_opts`: merge options controlling how the trees will be merged at each rebase step.
767
     Only present on libgit2 versions newer than or equal to 0.24.0.
768
  * `checkout_opts`: checkout options for writing files when initializing the rebase, stepping
769
    through it, and aborting it. See [`CheckoutOptions`](@ref) for more information.
770
"""
771
@kwdef struct RebaseOptions
772
    version::Cuint                 = Cuint(1)
773
    quiet::Cint                    = Cint(1)
774
    @static if LibGit2.VERSION >= v"0.24.0"
775
        inmemory::Cint             = Cint(0)
776
    end
777
    rewrite_notes_ref::Cstring     = Cstring(C_NULL)
778
    @static if LibGit2.VERSION >= v"0.24.0"
779
        merge_opts::MergeOptions   = MergeOptions()
780
    end
781
    checkout_opts::CheckoutOptions = CheckoutOptions()
782
end
783
@assert Base.allocatedinline(RebaseOptions)
784

785
"""
786
    LibGit2.RebaseOperation
787

788
Describes a single instruction/operation to be performed during the rebase.
789
Matches the [`git_rebase_operation`](https://libgit2.org/libgit2/#HEAD/type/git_rebase_operation_t) struct.
790

791
The fields represent:
792
  * `optype`: the type of rebase operation currently being performed. The options are:
793
      - `REBASE_OPERATION_PICK`: cherry-pick the commit in question.
794
      - `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its
795
        message using the prompt.
796
      - `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user
797
        to edit the commit's contents and its message.
798
      - `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit.
799
        The commit messages of the two commits will be merged.
800
      - `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit.
801
        Only the commit message of the previous commit will be used.
802
      - `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if
803
        the command exits successfully.
804
  * `id`: the [`GitHash`](@ref) of the commit being worked on during this rebase step.
805
  * `exec`: in case `REBASE_OPERATION_EXEC` is used, the command to run during this step
806
    (for instance, running the test suite after each commit).
807
"""
808
struct RebaseOperation
809
    optype::Cint
810
    id::GitHash
811
    exec::Cstring
812
end
813
function Base.show(io::IO, rbo::RebaseOperation)
×
814
    println(io, "RebaseOperation($(string(rbo.id)))")
×
815
    println(io, "Operation type: $(Consts.GIT_REBASE_OPERATION(rbo.optype))")
×
816
end
817

818
"""
819
    LibGit2.StatusOptions
820

821
Options to control how `git_status_foreach_ext()` will issue callbacks.
822
Matches the [`git_status_opt_t`](https://libgit2.org/libgit2/#HEAD/type/git_status_opt_t) struct.
823

824
The fields represent:
825
  * `version`: version of the struct in use, in case this changes later. For now, always `1`.
826
  * `show`: a flag for which files to examine and in which order.
827
    The default is `Consts.STATUS_SHOW_INDEX_AND_WORKDIR`.
828
  * `flags`: flags for controlling any callbacks used in a status call.
829
  * `pathspec`: an array of paths to use for path-matching. The behavior of the path-matching
830
    will vary depending on the values of `show` and `flags`.
831
  * The `baseline` is the tree to be used for comparison to the working directory and
832
    index; defaults to HEAD.
833
"""
834
@kwdef struct StatusOptions
835
    version::Cuint           = Cuint(1)
836
    show::Cint               = Consts.STATUS_SHOW_INDEX_AND_WORKDIR
837
    flags::Cuint             = Consts.STATUS_OPT_INCLUDE_UNTRACKED |
838
                               Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS |
839
                               Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX |
840
                               Consts.STATUS_OPT_SORT_CASE_SENSITIVELY
841
    pathspec::StrArrayStruct = StrArrayStruct()
842
    @static if LibGit2.VERSION >= v"0.27.0"
843
        baseline::Ptr{Cvoid} = C_NULL
844
    end
845
end
846
@assert Base.allocatedinline(StatusOptions)
847

848
"""
849
    LibGit2.StatusEntry
850

851
Providing the differences between the file as it exists in HEAD and the index, and
852
providing the differences between the index and the working directory.
853
Matches the `git_status_entry` struct.
854

855
The fields represent:
856
  * `status`: contains the status flags for the file, indicating if it is current,
857
    or has been changed in some way in the index or work tree.
858
  * `head_to_index`: a pointer to a [`DiffDelta`](@ref) which encapsulates the difference(s)
859
    between the file as it exists in HEAD and in the index.
860
  * `index_to_workdir`: a pointer to a `DiffDelta` which encapsulates the difference(s)
861
    between the file as it exists in the index and in the [`workdir`](@ref).
862
"""
863
struct StatusEntry
864
    status::Cuint
865
    head_to_index::Ptr{DiffDelta}
866
    index_to_workdir::Ptr{DiffDelta}
867
end
868

869
"""
870
    LibGit2.FetchHead
871

872
Contains the information about HEAD during a fetch, including the name and URL
873
of the branch fetched from, the oid of the HEAD, and whether the fetched HEAD
874
has been merged locally.
875

876
The fields represent:
877
  * `name`: The name in the local reference database of the fetch head, for example,
878
     `"refs/heads/master"`.
879
  * `url`: The URL of the fetch head.
880
  * `oid`: The [`GitHash`](@ref) of the tip of the fetch head.
881
  * `ismerge`: Boolean flag indicating whether the changes at the
882
     remote have been merged into the local copy yet or not. If `true`, the local
883
     copy is up to date with the remote fetch head.
884
"""
885
struct FetchHead
886
    name::String
887
    url::String
888
    oid::GitHash
889
    ismerge::Bool
890
end
891

892
function Base.show(io::IO, fh::FetchHead)
×
893
    println(io, "FetchHead:")
×
894
    println(io, "Name: $(fh.name)")
×
895
    println(io, "URL: $(fh.url)")
×
896
    print(io, "OID: ")
×
897
    show(io, fh.oid)
×
898
    println(io)
×
899
    println(io, "Merged: $(fh.ismerge)")
×
900
end
901

902
"""
903
    LibGit2.ConfigEntry
904

905
Matches the [`git_config_entry`](https://libgit2.org/libgit2/#HEAD/type/git_config_entry) struct.
906
"""
907
@kwdef struct ConfigEntry
908
    name::Cstring       = Cstring(C_NULL)
909
    value::Cstring      = Cstring(C_NULL)
910
    level::GIT_CONFIG   = Consts.CONFIG_LEVEL_DEFAULT
911
    free::Ptr{Cvoid}    = C_NULL
912
    payload::Any        = nothing
913
end
914
@assert Base.allocatedinline(ConfigEntry)
915

916
function Base.show(io::IO, ce::ConfigEntry)
×
917
    print(io, "ConfigEntry(\"", unsafe_string(ce.name), "\", \"", unsafe_string(ce.value), "\")")
×
918
end
919

920
"""
921
    LibGit2.split_cfg_entry(ce::LibGit2.ConfigEntry) -> Tuple{String,String,String,String}
922

923
Break the `ConfigEntry` up to the following pieces: section, subsection, name, and value.
924

925
# Examples
926
Given the git configuration file containing:
927
```
928
[credential "https://example.com"]
929
    username = me
930
```
931

932
The `ConfigEntry` would look like the following:
933

934
```julia-repl
935
julia> entry
936
ConfigEntry("credential.https://example.com.username", "me")
937

938
julia> LibGit2.split_cfg_entry(entry)
939
("credential", "https://example.com", "username", "me")
940
```
941

942
Refer to the [git config syntax documentation](https://git-scm.com/docs/git-config#_syntax)
943
for more details.
944
"""
945
function split_cfg_entry(ce::ConfigEntry)
×
946
    key = unsafe_string(ce.name)
×
947

948
    # Determine the positions of the delimiters
949
    subsection_delim = something(findfirst(isequal('.'), key), 0)
×
950
    name_delim = something(findlast(isequal('.'), key), 0)
×
951

952
    section = SubString(key, 1, subsection_delim - 1)
×
953
    subsection = SubString(key, subsection_delim + 1, name_delim - 1)
×
954
    name = SubString(key, name_delim + 1)
×
955
    value = unsafe_string(ce.value)
×
956

957
    return (section, subsection, name, value)
×
958
end
959

960
# Abstract object types
961

962
"""
963
    AbstractGitObject
964

965
`AbstractGitObject`s must obey the following interface:
966
- `obj.owner`, if present, must be a `Union{Nothing,GitRepo,GitTree}`
967
- `obj.ptr`, if present, must be a `Union{Ptr{Cvoid},Ptr{SignatureStruct}}`
968
"""
969
abstract type AbstractGitObject end
970

971
function Base.getproperty(obj::AbstractGitObject, name::Symbol)
×
972
    # These type-assertions enforce the interface requirements above.
973
    # They assist type-inference in cases where the compiler only knows that it
974
    # has an `AbstractGitObject` without being certain about the concrete type.
975
    # See detailed explanation in https://github.com/JuliaLang/julia/pull/36452.
976
    if name === :owner
×
977
        return getfield(obj, :owner)::Union{Nothing,GitRepo,GitTree}
×
978
    elseif name === :ptr
×
979
        return getfield(obj, :ptr)::Union{Ptr{Cvoid},Ptr{SignatureStruct}}
356✔
980
    else
981
        return getfield(obj, name)
×
982
    end
983
end
984

985
Base.isempty(obj::AbstractGitObject) = (obj.ptr == C_NULL)
18✔
986

987
# `GitObject`s must obey the following interface:
988
# - `obj.owner` must be a `GitRepo`
989
# - `obj.ptr` must be a Ptr{Cvoid}
990
abstract type GitObject <: AbstractGitObject end
991

992
function Base.getproperty(obj::GitObject, name::Symbol)
×
993
    if name === :owner
×
994
        return getfield(obj, :owner)::GitRepo
×
995
    elseif name === :ptr
×
996
        return getfield(obj, :ptr)::Ptr{Cvoid}
×
997
    else
998
        return getfield(obj, name)
×
999
    end
1000
end
1001

1002
for (typ, owntyp, sup, cname) in Tuple{Symbol,Any,Symbol,Symbol}[
1003
    (:GitRepo,           nothing,                 :AbstractGitObject, :git_repository),
1004
    (:GitConfig,         :(Union{GitRepo, Nothing}), :AbstractGitObject, :git_config),
1005
    (:GitIndex,          :(Union{GitRepo, Nothing}), :AbstractGitObject, :git_index),
1006
    (:GitRemote,         :GitRepo,                :AbstractGitObject, :git_remote),
1007
    (:GitRevWalker,      :GitRepo,                :AbstractGitObject, :git_revwalk),
1008
    (:GitReference,      :GitRepo,                :AbstractGitObject, :git_reference),
1009
    (:GitDescribeResult, :GitRepo,                :AbstractGitObject, :git_describe_result),
1010
    (:GitDiff,           :GitRepo,                :AbstractGitObject, :git_diff),
1011
    (:GitDiffStats,      :GitRepo,                :AbstractGitObject, :git_diff_stats),
1012
    (:GitAnnotated,      :GitRepo,                :AbstractGitObject, :git_annotated_commit),
1013
    (:GitRebase,         :GitRepo,                :AbstractGitObject, :git_rebase),
1014
    (:GitBlame,          :GitRepo,                :AbstractGitObject, :git_blame),
1015
    (:GitStatus,         :GitRepo,                :AbstractGitObject, :git_status_list),
1016
    (:GitBranchIter,     :GitRepo,                :AbstractGitObject, :git_branch_iterator),
1017
    (:GitConfigIter,     nothing,                 :AbstractGitObject, :git_config_iterator),
1018
    (:GitUnknownObject,  :GitRepo,                :GitObject,         :git_object),
1019
    (:GitCommit,         :GitRepo,                :GitObject,         :git_commit),
1020
    (:GitBlob,           :GitRepo,                :GitObject,         :git_blob),
1021
    (:GitTree,           :GitRepo,                :GitObject,         :git_tree),
1022
    (:GitTag,            :GitRepo,                :GitObject,         :git_tag),
1023
    (:GitTreeEntry,      :GitTree,                :AbstractGitObject, :git_tree_entry),
1024
    ]
1025

1026
    if owntyp === nothing
1027
        @eval mutable struct $typ <: $sup
1028
            ptr::Ptr{Cvoid}
1029
            function $typ(ptr::Ptr{Cvoid}, fin::Bool=true)
18✔
1030
                # fin=false should only be used when the pointer should not be free'd
1031
                # e.g. from within callback functions which are passed a pointer
1032
                @assert ptr != C_NULL
40✔
1033
                obj = new(ptr)
20✔
1034
                if fin
20✔
1035
                    Threads.atomic_add!(REFCOUNT, 1)
20✔
1036
                    finalizer(Base.close, obj)
20✔
1037
                end
1038
                return obj
20✔
1039
            end
1040
        end
1041
        @eval Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::$typ) = x.ptr
×
1042
    else
1043
        @eval mutable struct $typ <: $sup
1044
            owner::$owntyp
1045
            ptr::Ptr{Cvoid}
1046
            function $typ(owner::$owntyp, ptr::Ptr{Cvoid}, fin::Bool=true)
×
1047
                @assert ptr != C_NULL
77✔
1048
                obj = new(owner, ptr)
41✔
1049
                if fin
41✔
1050
                    Threads.atomic_add!(REFCOUNT, 1)
41✔
1051
                    finalizer(Base.close, obj)
41✔
1052
                end
1053
                return obj
41✔
1054
            end
1055
        end
1056
        @eval Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::$typ) = x.ptr
×
1057
        if isa(owntyp, Expr) && owntyp.args[1] === :Union && owntyp.args[3] === :Nothing
1058
            @eval begin
1059
                $typ(ptr::Ptr{Cvoid}, fin::Bool=true) = $typ(nothing, ptr, fin)
10✔
1060
            end
1061
        end
1062
    end
1063
    @eval function Base.close(obj::$typ)
98✔
1064
        if obj.ptr != C_NULL
116✔
1065
            ensure_initialized()
60✔
1066
            ccall(($(string(cname, :_free)), :libgit2), Cvoid, (Ptr{Cvoid},), obj.ptr)
60✔
1067
            obj.ptr = C_NULL
60✔
1068
            if Threads.atomic_sub!(REFCOUNT, 1) == 1
60✔
1069
                # will the last finalizer please turn out the lights?
1070
                ccall((:git_libgit2_shutdown, :libgit2), Cint, ())
56✔
1071
            end
1072
        end
1073
    end
1074
end
1075

1076
## Calling `GitObject(repo, ...)` will automatically resolve to the appropriate type.
1077
function GitObject(repo::GitRepo, ptr::Ptr{Cvoid})
×
1078
    T = objtype(Consts.OBJECT(ptr))
×
1079
    T(repo, ptr)
×
1080
end
1081

1082
"""
1083
    LibGit2.GitSignature
1084

1085
This is a Julia wrapper around a pointer to a
1086
[`git_signature`](https://libgit2.org/libgit2/#HEAD/type/git_signature) object.
1087
"""
1088
mutable struct GitSignature <: AbstractGitObject
1089
    ptr::Ptr{SignatureStruct}
1090
    function GitSignature(ptr::Ptr{SignatureStruct})
×
1091
        @assert ptr != C_NULL
×
1092
        obj = new(ptr)
×
1093
        finalizer(Base.close, obj)
×
1094
        return obj
×
1095
    end
1096
end
1097
function Base.close(obj::GitSignature)
×
1098
    if obj.ptr != C_NULL
×
1099
        ensure_initialized()
×
1100
        ccall((:git_signature_free, :libgit2), Cvoid, (Ptr{SignatureStruct},), obj.ptr)
×
1101
        obj.ptr = C_NULL
×
1102
    end
1103
end
1104

1105
# Structure has the same layout as SignatureStruct
1106
mutable struct Signature
1107
    name::String
1108
    email::String
1109
    time::Int64
1110
    time_offset::Cint
1111
end
1112

1113
"""
1114
    LibGit2.BlameHunk
1115

1116
Matches the [`git_blame_hunk`](https://libgit2.org/libgit2/#HEAD/type/git_blame_hunk) struct.
1117
The fields represent:
1118
    * `lines_in_hunk`: the number of lines in this hunk of the blame.
1119
    * `final_commit_id`: the [`GitHash`](@ref) of the commit where this section was last changed.
1120
    * `final_start_line_number`: the *one based* line number in the file where the
1121
       hunk starts, in the *final* version of the file.
1122
    * `final_signature`: the signature of the person who last modified this hunk. You will
1123
       need to pass this to `Signature` to access its fields.
1124
    * `orig_commit_id`: the [`GitHash`](@ref) of the commit where this hunk was first found.
1125
    * `orig_path`: the path to the file where the hunk originated. This may be different
1126
       than the current/final path, for instance if the file has been moved.
1127
    * `orig_start_line_number`: the *one based* line number in the file where the
1128
       hunk starts, in the *original* version of the file at `orig_path`.
1129
    * `orig_signature`: the signature of the person who introduced this hunk. You will
1130
       need to pass this to `Signature` to access its fields.
1131
    * `boundary`: `'1'` if the original commit is a "boundary" commit (for instance, if it's
1132
       equal to an oldest commit set in `options`).
1133
"""
1134
@kwdef struct BlameHunk
1135
    lines_in_hunk::Csize_t                = Csize_t(0)
1136

1137
    final_commit_id::GitHash              = GitHash()
1138
    final_start_line_number::Csize_t      = Csize_t(0)
1139
    final_signature::Ptr{SignatureStruct} = Ptr{SignatureStruct}(C_NULL)
1140

1141
    orig_commit_id::GitHash               = GitHash()
1142
    orig_path::Cstring                    = Cstring(C_NULL)
1143
    orig_start_line_number::Csize_t       = Csize_t(0)
1144
    orig_signature::Ptr{SignatureStruct}  = Ptr{SignatureStruct}(C_NULL)
1145

1146
    boundary::Char                        = '\0'
1147
end
1148
@assert Base.allocatedinline(BlameHunk)
1149

1150
"""
1151
    with(f::Function, obj)
1152

1153
Resource management helper function. Applies `f` to `obj`, making sure to call
1154
`close` on `obj` after `f` successfully returns or throws an error. Ensures that
1155
allocated git resources are finalized as soon as they are no longer needed.
1156
"""
1157
function with(f::Function, obj)
36✔
1158
    try
36✔
1159
        f(obj)
36✔
1160
    finally
1161
        close(obj)
36✔
1162
    end
1163
end
1164

1165
with(f::Function, ::Type{T}, args...) where {T} = with(f, T(args...))
×
1166

1167
"""
1168
    with_warn(f::Function, ::Type{T}, args...)
1169

1170
Resource management helper function. Apply `f` to `args`, first constructing
1171
an instance of type `T` from `args`. Makes sure to call `close` on the resulting
1172
object after `f` successfully returns or throws an error. Ensures that
1173
allocated git resources are finalized as soon as they are no longer needed. If an
1174
error is thrown by `f`, a warning is shown containing the error.
1175
"""
1176
function with_warn(f::Function, ::Type{T}, args...) where T
×
1177
    obj = T(args...)
×
1178
    try
×
1179
        with(f, obj)
×
1180
    catch err
1181
        @warn "$(string(T)) thrown exception:" exception=err
×
1182
    end
1183
end
1184

1185
"""
1186
    LibGit2.Consts.OBJECT(::Type{T}) where T<:GitObject
1187

1188
The `OBJECT` enum value corresponding to type `T`.
1189
"""
1190
Consts.OBJECT(::Type{GitCommit})        = Consts.OBJ_COMMIT
×
1191
Consts.OBJECT(::Type{GitTree})          = Consts.OBJ_TREE
×
1192
Consts.OBJECT(::Type{GitBlob})          = Consts.OBJ_BLOB
×
1193
Consts.OBJECT(::Type{GitTag})           = Consts.OBJ_TAG
×
1194
Consts.OBJECT(::Type{GitUnknownObject}) = Consts.OBJ_ANY
×
1195
Consts.OBJECT(::Type{GitObject})        = Consts.OBJ_ANY
×
1196

1197
function Consts.OBJECT(ptr::Ptr{Cvoid})
×
1198
    ensure_initialized()
×
1199
    ccall((:git_object_type, :libgit2), Consts.OBJECT, (Ptr{Cvoid},), ptr)
×
1200
end
1201

1202
"""
1203
    objtype(obj_type::Consts.OBJECT)
1204

1205
Return the type corresponding to the enum value.
1206
"""
1207
function objtype(obj_type::Consts.OBJECT)
×
1208
    if obj_type == Consts.OBJ_COMMIT
×
1209
        GitCommit
×
1210
    elseif obj_type == Consts.OBJ_TREE
×
1211
        GitTree
×
1212
    elseif obj_type == Consts.OBJ_BLOB
×
1213
        GitBlob
×
1214
    elseif obj_type == Consts.OBJ_TAG
×
1215
        GitTag
×
1216
    elseif obj_type == Consts.OBJ_ANY #this name comes from the header
×
1217
        GitUnknownObject
×
1218
    else
1219
        throw(GitError(Error.Object, Error.ENOTFOUND, "Object type $obj_type is not supported"))
×
1220
    end
1221
end
1222

1223
abstract type AbstractCredential end
1224

1225
"""
1226
    isfilled(cred::AbstractCredential) -> Bool
1227

1228
Verifies that a credential is ready for use in authentication.
1229
"""
1230
isfilled(::AbstractCredential)
1231

1232
"Credential that support only `user` and `password` parameters"
1233
mutable struct UserPasswordCredential <: AbstractCredential
1234
    user::String
1235
    pass::Base.SecretBuffer
1236
    function UserPasswordCredential(user::AbstractString="", pass::Union{AbstractString, Base.SecretBuffer}="")
2✔
1237
        new(user, pass)
2✔
1238
    end
1239
end
1240

1241
function Base.setproperty!(cred::UserPasswordCredential, name::Symbol, value)
×
1242
    if name === :pass
1✔
1243
        field = getfield(cred, name)
1✔
1244
        Base.shred!(field)
1✔
1245
    end
1246
    setfield!(cred, name, convert(fieldtype(typeof(cred), name), value))
4✔
1247
end
1248

1249
function Base.shred!(cred::UserPasswordCredential)
1✔
1250
    cred.user = ""
3✔
1251
    Base.shred!(cred.pass)
3✔
1252
    return cred
×
1253
end
1254

1255
function Base.:(==)(a::UserPasswordCredential, b::UserPasswordCredential)
×
1256
    a.user == b.user && a.pass == b.pass
×
1257
end
1258

1259
function isfilled(cred::UserPasswordCredential)
×
1260
    !isempty(cred.user) && !isempty(cred.pass)
3✔
1261
end
1262

1263
"SSH credential type"
1264
mutable struct SSHCredential <: AbstractCredential
1265
    user::String
1266
    pass::Base.SecretBuffer
1267
    # Paths to private keys
1268
    prvkey::String
1269
    pubkey::String
1270
    function SSHCredential(user="", pass="",
×
1271
                           prvkey="", pubkey="")
1272
        new(user, pass, prvkey, pubkey)
×
1273
    end
1274
end
1275

1276
function Base.setproperty!(cred::SSHCredential, name::Symbol, value)
×
1277
    if name === :pass
×
1278
        field = getfield(cred, name)
×
1279
        Base.shred!(field)
×
1280
    end
1281
    setfield!(cred, name, convert(fieldtype(typeof(cred), name), value))
×
1282
end
1283

1284

1285
function Base.shred!(cred::SSHCredential)
×
1286
    cred.user = ""
×
1287
    Base.shred!(cred.pass)
×
1288
    cred.prvkey = ""
×
1289
    cred.pubkey = ""
×
1290
    return cred
×
1291
end
1292

1293
function Base.:(==)(a::SSHCredential, b::SSHCredential)
×
1294
    a.user == b.user && a.pass == b.pass && a.prvkey == b.prvkey && a.pubkey == b.pubkey
×
1295
end
1296

1297
function isfilled(cred::SSHCredential)
×
1298
    !isempty(cred.user) && isfile(cred.prvkey) && isfile(cred.pubkey) &&
×
1299
    (!isempty(cred.pass) || !is_passphrase_required(cred.prvkey))
1300
end
1301

1302
"Caches credential information for re-use"
1303
struct CachedCredentials
1304
    cred::Dict{String,AbstractCredential}
1305
    CachedCredentials() = new(Dict{String,AbstractCredential}())
×
1306
end
1307

1308
Base.haskey(cache::CachedCredentials, cred_id) = Base.haskey(cache.cred, cred_id)
×
1309
Base.getindex(cache::CachedCredentials, cred_id) = Base.getindex(cache.cred, cred_id)
×
1310
Base.get!(cache::CachedCredentials, cred_id, default) = Base.get!(cache.cred, cred_id, default)
×
1311

1312
function Base.shred!(p::CachedCredentials)
×
1313
    foreach(Base.shred!, values(p.cred))
×
1314
    return p
×
1315
end
1316

1317
function approve(cache::CachedCredentials, cred::AbstractCredential, url::AbstractString)
×
1318
    cred_id = credential_identifier(url)
×
1319
    if haskey(cache.cred, cred_id)
×
1320
        # Shred the cached credential we'll be overwriting if it isn't identical
1321
        cred !== cache.cred[cred_id] && Base.shred!(cache.cred[cred_id])
×
1322
    end
1323
    cache.cred[cred_id] = cred
×
1324
    nothing
×
1325
end
1326

1327
function reject(cache::CachedCredentials, cred::AbstractCredential, url::AbstractString)
×
1328
    cred_id = credential_identifier(url)
×
1329
    if haskey(cache.cred, cred_id)
×
1330
        # Shred the cached credential if it isn't the `cred` passed in
1331
        cred !== cache.cred[cred_id] && Base.shred!(cache.cred[cred_id])
×
1332
        delete!(cache.cred, cred_id)
×
1333
    end
1334
    nothing
×
1335
end
1336

1337
"""
1338
    LibGit2.CredentialPayload
1339

1340
Retains the state between multiple calls to the credential callback for the same URL.
1341
A `CredentialPayload` instance is expected to be `reset!` whenever it will be used with a
1342
different URL.
1343
"""
1344
mutable struct CredentialPayload
1345
    explicit::Union{AbstractCredential, Nothing}
1346
    cache::Union{CachedCredentials, Nothing}
1347
    allow_ssh_agent::Bool    # Allow the use of the SSH agent to get credentials
1348
    allow_git_helpers::Bool  # Allow the use of git credential helpers
1349
    allow_prompt::Bool       # Allow prompting the user for credentials
1350

1351
    config::GitConfig
1352

1353
    # Ephemeral state fields
1354
    credential::Union{AbstractCredential, Nothing}
1355
    first_pass::Bool
1356
    use_ssh_agent::Bool
1357
    use_env::Bool
1358
    use_git_helpers::Bool
1359
    remaining_prompts::Int
1360

1361
    url::String
1362
    scheme::String
1363
    username::String
1364
    host::String
1365

1366
    function CredentialPayload(
21✔
1367
            credential::Union{AbstractCredential, Nothing}=nothing,
1368
            cache::Union{CachedCredentials, Nothing}=nothing,
1369
            config::GitConfig=GitConfig();
1370
            allow_ssh_agent::Bool=true,
1371
            allow_git_helpers::Bool=true,
1372
            allow_prompt::Bool=true)
1373

1374
        payload = new(credential, cache, allow_ssh_agent, allow_git_helpers, allow_prompt, config)
5✔
1375
        return reset!(payload)
5✔
1376
    end
1377
end
1378

1379
function CredentialPayload(credential::AbstractCredential; kwargs...)
4✔
1380
    CredentialPayload(credential, nothing; kwargs...)
2✔
1381
end
1382

1383
function CredentialPayload(cache::CachedCredentials; kwargs...)
×
1384
    CredentialPayload(nothing, cache; kwargs...)
×
1385
end
1386

1387
CredentialPayload(p::CredentialPayload) = p
5✔
1388

1389
function Base.shred!(p::CredentialPayload)
1✔
1390
    # Note: Avoid shredding the `explicit` or `cache` fields as these are just references
1391
    # and it is not our responsibility to shred them.
1392
    credential = p.credential
1✔
1393
    credential !== nothing && Base.shred!(credential)
1✔
1394
    p.credential = nothing
1✔
1395
end
1396

1397
"""
1398
    reset!(payload, [config]) -> CredentialPayload
1399

1400
Reset the `payload` state back to the initial values so that it can be used again within
1401
the credential callback. If a `config` is provided the configuration will also be updated.
1402
"""
1403
function reset!(p::CredentialPayload, config::GitConfig=p.config)
20✔
1404
    p.config = config
20✔
1405
    p.credential = nothing
10✔
1406
    p.first_pass = true
10✔
1407
    p.use_ssh_agent = p.allow_ssh_agent
10✔
1408
    p.use_env = true
10✔
1409
    p.use_git_helpers = p.allow_git_helpers
10✔
1410
    p.remaining_prompts = p.allow_prompt ? 3 : 0
10✔
1411
    p.url = ""
10✔
1412
    p.scheme = ""
10✔
1413
    p.username = ""
10✔
1414
    p.host = ""
10✔
1415

1416
    return p
10✔
1417
end
1418

1419
"""
1420
    approve(payload::CredentialPayload; shred::Bool=true) -> Nothing
1421

1422
Store the `payload` credential for re-use in a future authentication. Should only be called
1423
when authentication was successful.
1424

1425
The `shred` keyword controls whether sensitive information in the payload credential field
1426
should be destroyed. Should only be set to `false` during testing.
1427
"""
1428
function approve(p::CredentialPayload; shred::Bool=true)
4✔
1429
    cred = p.credential
2✔
1430
    cred === nothing && return  # No credential was used
2✔
1431

1432
    # Each `approve` call needs to avoid shredding the passed in credential as we need
1433
    # the credential information intact for subsequent approve calls.
1434
    cache = p.cache
×
1435
    if cache !== nothing
×
1436
        approve(cache, cred, p.url)
×
1437
        shred = false  # Avoid wiping `cred` as this would also wipe the cached copy
×
1438
    end
1439
    if p.allow_git_helpers
×
1440
        approve(p.config, cred, p.url)
×
1441
    end
1442

1443
    if shred
×
1444
        Base.shred!(cred)
×
1445
        p.credential = nothing
×
1446
    end
1447
    nothing
×
1448
end
1449

1450
"""
1451
    reject(payload::CredentialPayload; shred::Bool=true) -> Nothing
1452

1453
Discard the `payload` credential from begin re-used in future authentication. Should only be
1454
called when authentication was unsuccessful.
1455

1456
The `shred` keyword controls whether sensitive information in the payload credential field
1457
should be destroyed. Should only be set to `false` during testing.
1458
"""
1459
function reject(p::CredentialPayload; shred::Bool=true)
4✔
1460
    cred = p.credential
2✔
1461
    cred === nothing && return  # No credential was used
2✔
1462

1463
    # Note: each `reject` call needs to avoid shredding the passed in credential as we need
1464
    # the credential information intact for subsequent reject calls.
1465
    cache = p.cache
2✔
1466
    if cache !== nothing
2✔
1467
        reject(cache, cred, p.url)
×
1468
    end
1469
    if p.allow_git_helpers
2✔
1470
        reject(p.config, cred, p.url)
×
1471
    end
1472

1473
    if shred
2✔
1474
        Base.shred!(cred)
2✔
1475
        p.credential = nothing
2✔
1476
    end
1477
    nothing
2✔
1478
end
1479

1480
# Useful for functions which can handle various kinds of credentials
1481
const Creds = Union{CredentialPayload, AbstractCredential, CachedCredentials, Nothing}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc