NAME
Git::Native::Remote::Result - Per-ref outcomes from a Remote fetch or push
VERSION
version 0.005
SYNOPSIS
my $r = $remote->fetch(
refspecs => ['+refs/heads/*:refs/remotes/origin/*'],
);
if ( !$r->ok ) {
for my $u ( @{ $r->updated } ) {
warn sprintf "updated %s: %s -> %s\n", $u->{ref},
$u->{from} // '(new)', $u->{to} // '(deleted)';
}
for my $u ( @{ $r->rejected } ) {
warn "rejected $u->{ref}: $u->{reason}\n";
}
}
DESCRIPTION
Structured return value from Git::Native::Remote's fetch and push. It exists because libgit2 returns 0 from git_remote_fetch / git_remote_push even when individual refs were skipped or refused; the per-ref outcome is only visible through the callbacks Remote installs (update_tips on fetch, push_update_reference on push). See "updated", "rejected", and "ok".
updated
for my $u ( @{ $result->updated } ) {
say $u->{ref}, defined $u->{to} ? " -> $u->{to}" : ' (deleted)';
}
Arrayref of hashrefs, one per ref that actually moved on the receiving side. Every entry carries the same four keys, ref / from / to / reason, whether it came from a fetch or a push, so code that reads a Result does not have to know which operation produced it. A value the operation cannot supply is present as an explicit undef rather than as a missing key.
# fetch
{ ref => 'refs/karr/x', from => undef, to => 'b8f9ae41...', reason => '' }
# push
{ ref => 'refs/karr/x', from => undef, to => 'b8f9ae41...', reason => '' }
ref is the name of the ref on the side that changed, and that side differs by operation: a fetch names the local destination the refspec mapped to, a push names the ref as the remote calls it. That asymmetry is the semantics of the underlying libgit2 callbacks (update_tips fires after the local ref is written, push_update_reference relays the server's own report) and is deliberately left alone.
to is the OID the ref now points at, or undef when the ref was deleted rather than moved — a stale mirror ref dropped by a prune => 1 fetch, or a delete refspec on a push. On a push it is read off the local source side of the refspec, not from the server, and is therefore also undef for a source that is not a resolvable local reference.
from is the OID the ref pointed at before. It is undef when the ref did not exist on the receiving side yet, and always undef on a push: the previous remote-side OID would take a ref listing before the push, and "push" in Git::Native::Remote does not spend an extra network round trip on it. Call "list_refs" in Git::Native::Remote first if you need that snapshot.
reason is the empty string on every updated entry — a non-empty reason means the ref was refused, and refused refs are in "rejected" instead. Refs that were already up to date are not reported at all.
rejected
die "push refused: $_->{ref}: $_->{reason}" for @{ $result->rejected };
Arrayref of { ref => $name, reason => $message }, one per ref the server refused, with reason the status string it sent (non-fast-forward, pre-receive hook declined, ...). Refused refs never carry an empty reason — an empty status is libgit2's "accepted, nothing to say" and lands in "updated".
Only a push can populate this. A fetch leaves it empty even when libgit2 skipped a non-fast-forward ref, because that path does not reach the update_tips callback; on a fetch, a skipped ref shows up as a missing entry in "updated".
ok
$result->ok # 1 when nothing at all happened
True iff both updated and rejected are empty, i.e. the operation moved no ref and no ref was refused. A fully successful push of N refs reports all N in updated and is therefore not ok — the check for "did anything go wrong" is @{ $result->rejected }, and ok answers the different question "was this a no-op?".
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-git-native/issues.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.