[PATCH v3 0/2] remote: url-based pushRemote with renamed remotes
"Harald Nordgren via GitGitGadget" <[email protected]>
| Newsgroups | org.kernel.vger.git |
|---|---|
| Message-ID | <[email protected]> |
Fix git status not showing the push branch after remotes are renamed, when
branch.<name>.pushRemote is a URL matching exactly one configured remote.
Changes in v4:
* Match against the actual push URL, including pushurl and pushInsteadOf.
* Clarify how rearranging remotes exposes the git status tracking problem.
* Simplify and correct the documentation for URL-valued pushRemote.
Changes in v3:
* Revamp commit messages to clarify motivation.
Changes in v2:
* Clarify that URL push destinations already work and that this change only
restores their tracking information.
* Document URL values for branch.<name>.pushRemote and their @{push}
behavior.
Harald Nordgren (2):
remote: pass repository to push tracking helper
remote: find tracking branches for URL push destinations
Documentation/config/branch.adoc | 1 +
Documentation/revisions.adoc | 3 +
remote.c | 43 +++++++++--
remote.h | 2 +
t/t5505-remote.sh | 124 +++++++++++++++++++++++++++++++
transport.c | 5 +-
6 files changed, 172 insertions(+), 6 deletions(-)
base-commit: 48bbf81c29ca9a4479ec7850fe206518682cdb2f
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2358%2FHaraldNordgren%2Fremote-resolve-url-push-tracking-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2358/HaraldNordgren/remote-resolve-url-push-tracking-v3
Pull-Request: https://github.com/git/git/pull/2358
Range-diff vs v2:
1: b1ac49de87 = 1: b1ac49de87 remote: pass repository to push tracking helper
2: 6e924a7fec ! 2: a343af9d50 remote: find tracking branches for URL push destinations
@@ Metadata
## Commit message ##
remote: find tracking branches for URL push destinations
- Git already accepts a repository URL as branch.<name>.pushRemote and
- can push to it. When a configured remote has the same URL, however,
- "git status" cannot show that remote's push branch.
+ Git accepts a repository URL as branch.<name>.pushRemote and can push
+ to it. This branch setting takes precedence over remote.pushDefault.
- This can happen in fork workflows when the original remote is renamed
- to "upstream", the fork is added as "origin", and an existing
- pushRemote value still contains the fork URL. The URL still points to
- the right repository, so pushing works. However, @{push} is unavailable
- because Git does not connect the URL to "origin". As a result,
+ A branch can be configured with a URL-valued pushRemote before any push
+ occurs. If the remotes are later rearranged with "git remote rename" and
+ "git remote add", the newly added remote may use that URL. The URL value
+ is unaffected by the rename and continues to take precedence over
+ remote.pushDefault. The URL and the remote then point to the same
+ repository, but Git does not connect them for tracking. Pushing works,
+ but @{push} cannot identify the remote's tracking branch. As a result,
"git status" cannot show the push branch, and an up-to-date push can
- leave its local tracking information stale.
+ leave its tracking information stale.
- When exactly one configured remote has the URL as one of its
- remote.<name>.url values, use its fetch refspec to find and refresh the
- push branch. Keep the URL as the push destination so the configured
- remote's push settings do not change existing behavior. Keep the
- current behavior when no remote matches or multiple remotes match.
+ When exactly one configured remote uses the push destination URL, use
+ that remote for push tracking. Continue to push to the URL so the
+ configured remote's push settings do not change existing behavior. Keep
+ the current behavior when no remote matches or multiple remotes match.
Signed-off-by: Harald Nordgren <[email protected]>
@@ Documentation/config/branch.adoc: This option defaults to `never`.
repository), you would want to set `remote.pushDefault` to
specify the remote to push to for all branches, and use this
option to override it for a specific branch.
-+ The value may be the name of a configured remote or a repository
-+ URL. A URL is used directly as the push destination.
++ The value may be the name of a configured remote or a repository URL.
`branch.<name>.merge`::
Defines, together with `branch.<name>.remote`, the upstream branch
@@ Documentation/revisions.adoc: some output processing may assume ref names in UTF
`git push` were run while `branchname` was checked out (or the current
`HEAD` if no branchname is specified). Like for '@\{upstream\}', we report
the remote-tracking branch that corresponds to that branch at the remote.
-+ If the push destination is a URL and exactly one configured remote has the
-+ same `remote.<name>.url`, '@\{push}' reports the remote-tracking branch for
-+ that remote.
++ If the push destination is a URL and exactly one configured remote has
++ that URL among its `remote.<name>.url` values, '@\{push}' reports that
++ remote's remote-tracking branch.
+
Here's an example to make it more clear:
+
@@ remote.c: const char *branch_get_upstream(struct branch *branch, struct strbuf *
+struct remote *repo_remote_for_push_tracking(struct repository *repo,
+ struct remote *remote)
+{
++ const struct strvec *push_urls;
+ struct remote *first_match = NULL;
+ struct remote_state *remote_state = repo->remote_state;
++ const char *check_url;
+
-+ if (remote->origin != REMOTE_UNCONFIGURED || remote->url.nr != 1)
++ if (remote->origin != REMOTE_UNCONFIGURED)
+ return remote;
+
++ push_urls = push_url_of_remote(remote);
++ if (push_urls->nr != 1)
++ return remote;
++ check_url = push_urls->v[0];
++
+ for (int i = 0; i < remote_state->remotes_nr; i++) {
+ struct remote *candidate = remote_state->remotes[i];
+
+ if (!candidate || candidate == remote ||
+ !remote_is_configured(candidate, 0) ||
-+ !remote_has_url(candidate, remote->url.v[0]))
++ !remote_has_url(candidate, check_url))
+ continue;
+ if (first_match)
+ return remote;
@@ t/t5505-remote.sh: setup_repository () {
+ rm -rf fork.git client &&
+ git clone --bare one fork.git &&
+ git clone one client &&
-+ fork_url="$TRASH_DIRECTORY/fork.git" &&
++ fork_url="file://$TRASH_DIRECTORY/fork.git" &&
+ (
+ cd client &&
+ git checkout -b topic --track origin/main &&
@@ t/t5505-remote.sh: test_expect_success 'rename a remote renames repo remote.push
+ EOF
+'
+
-+test_expect_success 'adding fork remote makes URL-valued pushRemote trackable' '
++test_expect_success 'adding matching remote makes URL-valued pushRemote trackable' '
+ setup_url_pushremote &&
+
+ (
@@ t/t5505-remote.sh: test_expect_success 'rename a remote renames repo remote.push
+ EOF
+'
+
++test_expect_success 'pushInsteadOf URL pushRemote is trackable' '
++ setup_url_pushremote &&
++ (
++ cd client &&
++ git remote rename origin upstream &&
++ git remote add -f origin "$fork_url" &&
++ git config "url.$fork_url.pushInsteadOf" fork: &&
++ git config branch.topic.pushRemote fork:
++ ) &&
++
++ check_status <<-EOF
++ On branch topic
++ Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
++
++ Your branch is up to date with ${SQ}origin/topic${SQ}.
++
++ nothing to commit, working tree clean
++ EOF
++'
++
+test_expect_success 'up-to-date URL push refreshes stale tracking branch' '
+ setup_url_pushremote &&
+ (
--
gitgitgadget