Push destination and saved-mapping parity
Pinned reference: TortoiseGit 7338078f8ddd924b8cddee35f512f2286072136d.
This batch extends the partial Push family review; it does not
establish complete Push dialog or transport acceptance.
Source contract and native implementation
CAppUtils::DoPush, src/TortoiseProc/AppUtils.cpp:2831–2844, constructs the
outgoing refspec from the entered source and, when nonempty, : plus the entered
destination. It does not rewrite a short destination to refs/heads/ or
refs/tags/, and does not query remote refs to choose a namespace.
Sources/TurtleGitCore/Push.swift now forwards that literal refspec. Git resolves
an existing unique short remote tag or branch; a colliding branch and tag fails
without selecting one. Explicit refs/heads/… and refs/tags/… destinations
resolve that ambiguity. Empty source produces :destination, including deletion
of an existing short or qualified tag. A raw hash can update an existing unique
short destination, but creating a new destination from a raw hash requires an
explicit namespace when Git cannot infer one. The previous automatic namespace
rewrite was a port divergence. The older hash-to-new-branch fixture now supplies
refs/heads/hash-target explicitly.
Validation still checks source identity/uniqueness and destination ref syntax.
The refs/heads/ prefix used to validate a bare destination's syntax is never
sent as an inferred destination. No remote namespace preflight or fallback was
added. Named remote, URL/path, and each all-remotes transport receive the same
literal spelling, so different servers retain their own namespace/error behavior.
Unicode spellings remain byte-preserved through the existing
core.precomposeunicode=false command path. Push remains a Git CLI operation;
this batch adds no alternate libgit2 push implementation.
Existing flags, SSH preparation, authentication, hooks, streamed output, cancellation, result ownership and post-actions retain their paths. A later all-remotes failure reports earlier completed destinations without rollback. A cancellation already signalled before submission prevents config or transport writes. Saved short-branch defaults still precede transport and remain saved if transport fails, matching the source ordering.
Enabled options versus mapping ownership
CPushDlg::EnDisablePushRemoteArchiveBranch, PushDlg.cpp:336–354, enables
IDC_PROC_PUSH_SET_UPSTREAM for named-remote mode with a nonempty source or all
branches. The two saved-mapping controls (IDC_PROC_PUSH_SET_PUSHBRANCH and
IDC_PROC_PUSH_SET_PUSHREMOTE) require named single-remote mode, a nonempty
source, no all-branches mode and no upstream setting. The source does not require
that text to denote a local branch before enabling those controls. Native
PushWindow.swift now uses that same text/mode policy, including tags and hashes.
Submission is separate: PushDlg.cpp:466–489 writes mappings only when
g_Git.IsLocalBranch(m_BranchSourceName) succeeds. Git.cpp:1615–1619 compares
that supplied text literally against local branch names. Both the libgit2
git_branch_name path (Git.cpp:1746–1764) and CLI branch-list path
(Git.cpp:1780–1816) return short local names. Therefore only an exact short
local branch owns branch.<name>.pushbranch/pushremote writes. A tag, hash,
HEAD, revision expression or fully qualified refs/heads/… source can have the
save controls checked and still performs no mapping write. Core accepts that
submission as a no-op for configuration, rather than rejecting it. Suggested
branch defaults remain independently available; resolving HEAD for a suggestion
does not authorize saving its current branch's mapping. Upstream flags are passed
to Git without imposing a local-branch-only UI restriction.
PushDlg.cpp:456–464 saves URL history, destination history and the selected
remote inside the ordinary submission branch, excluding all-branches and remote
removal. Native remembered-remote persistence now follows that same gate; removal
and all-branches leave the previous ordinary selection intact. The existing
preflight/owned confirmation path still decides whether submission is accepted.
Focused evidence and remaining work
New PushDestinationTests.swift contains real disposable local/bare-repository
cases for branch/hash updates to existing short tags, short/qualified tag deletion,
branch/tag collision and explicit escape, unqualified new hash failure and
qualified success, URL transport, per-server all-remotes partial failure, literal
saved destinations after rejection, pre-cancellation, non-short-branch save no-op,
tag upstream forwarding, and exact Unicode refspec/transport flags against a
private rejecting hook. Existing Push tests retain transport, flags, source
identity, validation and cancellation coverage. The saved-default fixture uses
short main to exercise actual mapping ownership; the qualified-source suggestion
assertion remains. Native namespace/removal/source-picker fixtures are maintained
with the parent batch's grouped receiver checks.
The dated grouped checkpoint records 25 relevant Core methods, the Debug app build and six native case/engine pairs passing on their first run. All six owned receivers were reaped and independently absent. Fixtures use private repositories/preferences and owned helper cleanup. Hosted CI, physical keyboard and accessibility interaction, signed sandbox grants, real network credentials, and all exceptional remote/protocol states remain separate acceptance work.