Summary
libra push origin --delete <tag> prints Everything up-to-date, exits 0, and deletes nothing. The tag stays published while the user is told the operation succeeded.
The cause is that a short deletion target is unconditionally qualified as refs/heads/<name>, so a tag name resolves to a branch that does not exist, and a deletion request naming a ref the remote does not advertise is treated as "nothing to do" rather than an error.
Reproduction
With a tag that exists on the remote:
$ libra ls-remote origin | grep tag
12de8a8b15f61e5c32bc315049632e16355903f1 refs/tags/v0.1.0
$ libra push origin --delete v0.1.0
Everything up-to-date
$ libra ls-remote origin | grep tag
12de8a8b15f61e5c32bc315049632e16355903f1 refs/tags/v0.1.0 # still there
The fully-qualified refspec form works:
$ libra push origin :refs/tags/v0.1.0
To git@github.com:libra-tools/skills.git
- [deleted] v0.1.0
Expected: libra push origin --delete v0.1.0 deletes the tag, as git push --delete v0.1.0 does.
The exit code is 0 even for a ref that does not exist at all
$ libra push origin --delete zz-nonexistent-probe
Everything up-to-date
$ echo $?
0
Git refuses this: error: unable to delete 'zz-nonexistent-probe': remote ref does not exist, exit 1. Scripts and agents branching on the exit code are told a deletion happened when none did — this is the part that makes the bug more than a missing feature.
Root cause
apply_delete_flag (src/command/push.rs:729) rewrites --delete v0.1.0 into the deletion refspec :v0.1.0, which is the documented design. The destination is then qualified (src/command/push.rs:1433-1438):
if input.starts_with("refs/heads/") {
// ...
} else {
ensure_valid_ref(format!("refs/heads/{input}"), input)
}
Any short name becomes refs/heads/<input>, so the request becomes "delete refs/heads/v0.1.0". The remote has no such ref, the plan comes out empty, and push reports Everything up-to-date.
Git instead resolves the name against the refs the remote advertises and deletes the single match (erroring when the name is ambiguous).
Docs contradiction
docs/commands/push.md:428 states the equivalence directly:
| Delete remote branch | libra push -d origin branch or libra push origin :branch | git push -d origin branch / git push origin :branch |
and docs/commands/push.md:61 documents -d, --delete as "Delete the named remote refs", with no branches-only restriction. So either the behaviour or the documentation is wrong.
Suggested fix
Two independent changes; the second is worth making even if the first is deferred.
- Resolve a short deletion target against the remote's advertised refs — try
refs/heads/<name> then refs/tags/<name>, and refuse an ambiguous name the way Git does, instead of hardcoding the refs/heads/ prefix.
- Fail a deletion that names a ref the remote does not advertise. Today that path is indistinguishable from an up-to-date push. Making it an error surfaces the missing resolution in (1) rather than hiding it, and stops
--delete from reporting success for a ref it never touched.
A regression test that pushes a tag, deletes it with --delete <short name>, and asserts it is gone from ls-remote would cover both.
Possibly the same family as #464
#464 is also a short-name-versus-fully-qualified-ref mismatch: status looks a remote-tracking ref up by its short name while clone/fetch/push store it fully qualified. Different commands and different fixes, but it may be worth auditing every place a ref name is qualified with a hardcoded refs/heads/ prefix.
Environment
libra 0.22.10
Linux 7.1.9-arch1-2 x86_64
remote: github.com over SSH
Summary
libra push origin --delete <tag>printsEverything up-to-date, exits 0, and deletes nothing. The tag stays published while the user is told the operation succeeded.The cause is that a short deletion target is unconditionally qualified as
refs/heads/<name>, so a tag name resolves to a branch that does not exist, and a deletion request naming a ref the remote does not advertise is treated as "nothing to do" rather than an error.Reproduction
With a tag that exists on the remote:
The fully-qualified refspec form works:
Expected:
libra push origin --delete v0.1.0deletes the tag, asgit push --delete v0.1.0does.The exit code is 0 even for a ref that does not exist at all
Git refuses this:
error: unable to delete 'zz-nonexistent-probe': remote ref does not exist, exit 1. Scripts and agents branching on the exit code are told a deletion happened when none did — this is the part that makes the bug more than a missing feature.Root cause
apply_delete_flag(src/command/push.rs:729) rewrites--delete v0.1.0into the deletion refspec:v0.1.0, which is the documented design. The destination is then qualified (src/command/push.rs:1433-1438):Any short name becomes
refs/heads/<input>, so the request becomes "deleterefs/heads/v0.1.0". The remote has no such ref, the plan comes out empty, and push reportsEverything up-to-date.Git instead resolves the name against the refs the remote advertises and deletes the single match (erroring when the name is ambiguous).
Docs contradiction
docs/commands/push.md:428states the equivalence directly:| Delete remote branch |
libra push -d origin branchorlibra push origin :branch|git push -d origin branch/git push origin :branch|and
docs/commands/push.md:61documents-d, --deleteas "Delete the named remote refs", with no branches-only restriction. So either the behaviour or the documentation is wrong.Suggested fix
Two independent changes; the second is worth making even if the first is deferred.
refs/heads/<name>thenrefs/tags/<name>, and refuse an ambiguous name the way Git does, instead of hardcoding therefs/heads/prefix.--deletefrom reporting success for a ref it never touched.A regression test that pushes a tag, deletes it with
--delete <short name>, and asserts it is gone fromls-remotewould cover both.Possibly the same family as #464
#464 is also a short-name-versus-fully-qualified-ref mismatch:
statuslooks a remote-tracking ref up by its short name whileclone/fetch/pushstore it fully qualified. Different commands and different fixes, but it may be worth auditing every place a ref name is qualified with a hardcodedrefs/heads/prefix.Environment