* `git push --porcelain` has no effect when deleting a ref which does not exist
@ 2026-08-05 7:19 Xavier Morel
2026-08-05 16:30 ` Junio C Hamano
0 siblings, 1 reply; 3+ messages in thread
From: Xavier Morel @ 2026-08-05 7:19 UTC (permalink / raw)
To: git
Using `push --delete --porcelain` with refs which are extant correctly
outputs the relevant information in the documented format:
- :refs/heads/<branch1> [deleted]
- :refs/heads/<branch2> [deleted]
However doing the same with refs which don't exist on the remote (e.g.
because of a concurrent deletion) has the error written out in
human-targeted text:
error: unable to delete '<branch1>': remote ref does not exist
error: unable to delete '<branch2>': remote ref does not exist
I would have expected something along the lines of:
! :refs/heads/<branch> [remote failure]
which would be machine-readable as documented for the `--porcelain`
flag. Was that intended or is it just something that fell through the
cracks of code convolution?
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: `git push --porcelain` has no effect when deleting a ref which does not exist
2026-08-05 7:19 `git push --porcelain` has no effect when deleting a ref which does not exist Xavier Morel
@ 2026-08-05 16:30 ` Junio C Hamano
2026-08-07 6:51 ` Xavier Morel
0 siblings, 1 reply; 3+ messages in thread
From: Junio C Hamano @ 2026-08-05 16:30 UTC (permalink / raw)
To: Xavier Morel; +Cc: git
Xavier Morel <xmo@odoo.com> writes:
> Using `push --delete --porcelain` with refs which are extant correctly
> outputs the relevant information in the documented format:
>
> - :refs/heads/<branch1> [deleted]
> - :refs/heads/<branch2> [deleted]
>
> However doing the same with refs which don't exist on the remote (e.g.
> because of a concurrent deletion) has the error written out in
> human-targeted text:
>
> error: unable to delete '<branch1>': remote ref does not exist
> error: unable to delete '<branch2>': remote ref does not exist
>
> I would have expected something along the lines of:
>
> ! :refs/heads/<branch> [remote failure]
>
> which would be machine-readable as documented for the `--porcelain`
> flag. Was that intended or is it just something that fell through the
> cracks of code convolution?
If I have to guess, I would say it is because nobody thought of
covering this usage pattern, which allows you to randomly throw a
deletion request to probe what does and what does not exist on the
other side.
Patches welcome.
Thanks.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: `git push --porcelain` has no effect when deleting a ref which does not exist
2026-08-05 16:30 ` Junio C Hamano
@ 2026-08-07 6:51 ` Xavier Morel
0 siblings, 0 replies; 3+ messages in thread
From: Xavier Morel @ 2026-08-07 6:51 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
On 05/08/2026 18:30, Junio C Hamano wrote:
> Xavier Morel <xmo@odoo.com> writes:
>
>> Using `push --delete --porcelain` with refs which are extant correctly
>> outputs the relevant information in the documented format:
>>
>> - :refs/heads/<branch1> [deleted]
>> - :refs/heads/<branch2> [deleted]
>>
>> However doing the same with refs which don't exist on the remote (e.g.
>> because of a concurrent deletion) has the error written out in
>> human-targeted text:
>>
>> error: unable to delete '<branch1>': remote ref does not exist
>> error: unable to delete '<branch2>': remote ref does not exist
>>
>> I would have expected something along the lines of:
>>
>> ! :refs/heads/<branch> [remote failure]
>>
>> which would be machine-readable as documented for the `--porcelain`
>> flag. Was that intended or is it just something that fell through the
>> cracks of code convolution?
>
> If I have to guess, I would say it is because nobody thought of
> covering this usage pattern, which allows you to randomly throw a
> deletion request to probe what does and what does not exist on the
> other side.
>
> Patches welcome.
Looking at the current code, the abort when requesting the deletion of a
ref which is not on the remote is pretty early in the process, during
ref matching, which then causes `push` to bail.
Reading some of the followup the following call
`set_ref_status_for_push` can already set statuses on remote refs before
the push, in which case such refs with statuses set will be ignored for
the actual network operation (and the entire thing would be skipped if
atomic), and then we get to the reporting and teardown.
So it looks like
- `match_explicit` could create a dummy dest ref and set its status to
some sort of failure value (either an existing one or a new one) instead
of aborting
- then `set_ref_status_for_push` should skip over refs which already
have a status set (so it doesn't overwrite a previous error)
- push_refs_with_push all refs with a rejection status already set
- and then the formatting needs to get adapted for the new mode / case
And then trying to delete a non-existent ref would appear in the report
normally, and valid pushes would be performed instead of ignored, unless
`atomic` was set in which case they'd all be aborted, similar to other
abortions from "pre-push" checks by set_ref_status_for_push.
Does that seem to make sense or did I miss something critical? Do you
foresee significant issues?
Would you rather a new status code for this case or extending an
existing case? e.g. I could see REF_STATUS_REJECT_NODELETE on an
otherwise zeroed deletion ref for missing on remote, and a non-zeroed
deletion ref would be the existing "remote rejecting the deletion" case
so less code (and notably not push_refs_with_push) would have to be
adapted, but it would make the new case a bit more implicit.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-07 6:51 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 7:19 `git push --porcelain` has no effect when deleting a ref which does not exist Xavier Morel
2026-08-05 16:30 ` Junio C Hamano
2026-08-07 6:51 ` Xavier Morel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox