Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Documentation/RelNotes/2.56.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,24 @@ Fixes since v2.55
'gitweb' has been corrected to work even when the index line carries
a trailing file mode.
(merge fda513d6fe tl/gitweb-shorten-hashes-with-modes later to maint).

* When the push remote is specified as a URL, the fetch refspec of a
uniquely matching configured remote is now used to find and update
the remote-tracking branch (e.g., '@{push}').

* Traversals with '--exclude-first-parent-only' have been corrected
to properly stop after the first parent even when it has already
been marked as 'SEEN'.
(merge 47382f7398 jc/exclude-first-parent-seen later to maint).

* A segfault when 'git clone --revision' talks to a server that does not
support protocol v2 (falling back to protocol v0) has been corrected.
(merge 1034ad383f af/clone-revision-v0-segfault-fix later to maint).

* rewrites_release() in 'remote.c' has been updated to free 'struct
rewrite' instances, their '.instead_of' arrays, and their contents.
(merge dcef3bf041 jc/remote-insteadof-leakfix later to maint).

* The remote-matching logic for submodules has been corrected to resolve
'url.*.insteadOf' aliases before comparing the inventoried URL from
'.gitmodules' with the URLs of configured remotes.
1 change: 1 addition & 0 deletions Documentation/config/branch.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ 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.

`branch.<name>.merge`::
Defines, together with `branch.<name>.remote`, the upstream branch
Expand Down
3 changes: 3 additions & 0 deletions Documentation/revisions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ some output processing may assume ref names in UTF-8.
`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 uses
that URL for pushing, '@\{push}' reports that remote's remote-tracking
branch.
+
Here's an example to make it more clear:
+
Expand Down
2 changes: 1 addition & 1 deletion builtin/clone.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ static void update_remote_refs(const struct ref *refs,
write_followtags(refs, msg);
}

if (remote_head_points_at && !option_bare) {
if (remote_head_points_at && remote_head_points_at->peer_ref && !option_bare) {
struct strbuf head_ref = STRBUF_INIT;
strbuf_addstr(&head_ref, branch_top);
strbuf_addstr(&head_ref, "HEAD");
Expand Down
79 changes: 69 additions & 10 deletions remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,15 @@ static struct rewrite *make_rewrite(struct rewrites *r,

static void rewrites_release(struct rewrites *r)
{
for (int i = 0; i < r->rewrite_nr; i++)
free((char *)r->rewrite[i]->base);
for (int i = 0; i < r->rewrite_nr; i++) {
struct rewrite *rewrite = r->rewrite[i];

free((char *)rewrite->base);
for (int j = 0; j < rewrite->instead_of_nr; j++)
free((char *)rewrite->instead_of[j].s);
free(rewrite->instead_of);
free(rewrite);
}
free(r->rewrite);
memset(r, 0, sizeof(*r));
}
Expand Down Expand Up @@ -954,6 +961,17 @@ struct strvec *push_url_of_remote(struct remote *remote)
return remote->pushurl.nr ? &remote->pushurl : &remote->url;
}

static bool remote_has_push_url(struct remote *remote, const char *url)
{
const struct strvec *push_urls = push_url_of_remote(remote);

for (size_t i = 0; i < push_urls->nr; i++) {
if (!strcmp(push_urls->v[i], url))
return true;
}
return false;
}

void ref_push_report_free(struct ref_push_report *report)
{
while (report) {
Expand Down Expand Up @@ -1821,17 +1839,25 @@ const char *repo_default_remote(struct repository *repo)

const char *repo_remote_from_url(struct repository *repo, const char *url)
{
char *rewritten_url;
const char *remote_name = NULL;

read_config(repo, 0);
if ((rewritten_url = alias_url(url, &repo->remote_state->rewrites)))
url = rewritten_url;

for (int i = 0; i < repo->remote_state->remotes_nr; i++) {
struct remote *remote = repo->remote_state->remotes[i];
if (!remote)
continue;

if (remote_has_url(remote, url))
return remote->name;
if (remote_has_url(remote, url)) {
remote_name = remote->name;
break;
}
}
return NULL;
free(rewritten_url);
return remote_name;
}

int branch_has_merge_config(struct branch *branch)
Expand Down Expand Up @@ -1887,12 +1913,45 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
return branch->merge[0]->dst;
}

static char *tracking_for_push_dest(struct remote *remote,
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)
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_push_url(candidate, check_url))
continue;
if (first_match)
return remote;
first_match = candidate;
}

return first_match ? first_match : remote;
}

static char *tracking_for_push_dest(struct repository *repo,
struct remote *remote,
const char *refname,
struct strbuf *err)
{
char *ret;

remote = repo_remote_for_push_tracking(repo, remote);
ret = apply_refspecs(&remote->fetch, refname);
if (!ret)
return error_buf(err,
Expand Down Expand Up @@ -1925,21 +1984,21 @@ static char *branch_get_push_1(struct repository *repo,
_("push refspecs for '%s' do not include '%s'"),
remote->name, branch->name);

ret = tracking_for_push_dest(remote, dst, err);
ret = tracking_for_push_dest(repo, remote, dst, err);
free(dst);
return ret;
}

if (remote->mirror)
return tracking_for_push_dest(remote, branch->refname, err);
return tracking_for_push_dest(repo, remote, branch->refname, err);

switch (repo_config_values(repo)->push_default) {
case PUSH_DEFAULT_NOTHING:
return error_buf(err, _("push has no destination (push.default is 'nothing')"));

case PUSH_DEFAULT_MATCHING:
case PUSH_DEFAULT_CURRENT:
return tracking_for_push_dest(remote, branch->refname, err);
return tracking_for_push_dest(repo, remote, branch->refname, err);

case PUSH_DEFAULT_UPSTREAM:
return xstrdup_or_null(branch_get_upstream(branch, err));
Expand All @@ -1953,7 +2012,7 @@ static char *branch_get_push_1(struct repository *repo,
up = branch_get_upstream(branch, err);
if (!up)
return NULL;
cur = tracking_for_push_dest(remote, branch->refname, err);
cur = tracking_for_push_dest(repo, remote, branch->refname, err);
if (!cur)
return NULL;
if (strcmp(cur, up)) {
Expand Down
2 changes: 2 additions & 0 deletions remote.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ char *remote_ref_for_branch(struct branch *branch, int for_push);

const char *repo_default_remote(struct repository *repo);
const char *repo_remote_from_url(struct repository *repo, const char *url);
struct remote *repo_remote_for_push_tracking(struct repository *repo,
struct remote *remote);

/* returns true if the given branch has merge configuration given. */
int branch_has_merge_config(struct branch *branch);
Expand Down
10 changes: 8 additions & 2 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,12 +1153,18 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
if (p)
p->object.flags |= UNINTERESTING |
CHILD_VISITED;
if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
if (repo_parse_commit_gently(revs->repo, p, 1) < 0) {
if (revs->exclude_first_parent_only)
break;
continue;
}
if (p->parents)
mark_parents_uninteresting(revs, p);
if (p->object.flags & SEEN)
if (p->object.flags & SEEN) {
if (revs->exclude_first_parent_only)
break;
continue;
}
p->object.flags |= (SEEN | NOT_USER_GIVEN);
if (queue)
prio_queue_put(queue, p);
Expand Down
144 changes: 144 additions & 0 deletions t/t5505-remote.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ setup_repository () {
)
}

setup_url_pushremote () {
rm -rf fork.git client &&
git clone --bare one fork.git &&
git clone one client &&
fork_url="file://$TRASH_DIRECTORY/fork.git" &&
(
cd client &&
git checkout -b topic --track origin/main &&
git commit --allow-empty -m topic-change &&
git config push.default current &&
git config status.compareBranches "@{upstream} @{push}" &&
git config branch.topic.pushRemote "$fork_url" &&
git push
)
}

check_status () {
git -C client status >actual &&
cat >expected &&
test_cmp expected actual
}

tokens_match () {
echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect &&
echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual &&
Expand Down Expand Up @@ -1018,6 +1040,128 @@ test_expect_success 'rename a remote renames repo remote.pushDefault but keeps g
)
'

test_expect_success 'URL-valued pushRemote without matching remote is not trackable' '
setup_url_pushremote &&

check_status <<-EOF
On branch topic
Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean
EOF
'

test_expect_success 'adding matching remote makes URL-valued pushRemote trackable' '
setup_url_pushremote &&

(
cd client &&
git remote rename origin upstream &&
git remote add -f origin "$fork_url"
) &&

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 'configured pushurl makes URL-valued pushRemote trackable' '
setup_url_pushremote &&

(
cd client &&
git remote rename origin upstream &&
git remote add -f origin ../fork.git &&
git remote set-url --push origin "$fork_url"
) &&

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 '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 &&
(
cd client &&
git remote rename origin upstream &&
git remote add -f origin "$fork_url" &&
git commit --allow-empty -m another-topic-change &&
git -C ../fork.git fetch ../client topic:topic
) &&

check_status <<-EOF &&
On branch topic
Your branch is ahead of ${SQ}upstream/main${SQ} by 2 commits.

Your branch is ahead of ${SQ}origin/topic${SQ} by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean
EOF

git -C client push >actual 2>&1 &&
test_grep "Everything up-to-date" actual &&

check_status <<-EOF
On branch topic
Your branch is ahead of ${SQ}upstream/main${SQ} by 2 commits.

Your branch is up to date with ${SQ}origin/topic${SQ}.

nothing to commit, working tree clean
EOF
'

test_expect_success 'duplicate remote URL leaves URL-valued pushRemote ambiguous' '
setup_url_pushremote &&
(
cd client &&
git remote rename origin upstream &&
git remote add -f origin "$fork_url" &&
git remote add duplicate "$fork_url"
) &&

check_status <<-EOF
On branch topic
Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
(use "git push" to publish your local commits)

nothing to commit, working tree clean
EOF
'

test_expect_success 'rename handles remote without fetch refspec' '
git clone --bare one no-refspec.git &&
# confirm assumption that bare clone does not create refspec
Expand Down
Loading