Skip to content

Commit e291c75

Browse files
peffgitster
authored andcommitted
remote.c: add branch_get_push
In a triangular workflow, the place you pull from and the place you push to may be different. As we have branch_get_upstream for the former, this patch adds branch_get_push for the latter (and as the former implements @{upstream}, so will this implement @{push} in a future patch). Note that the memory-handling for the return value bears some explanation. Some code paths require allocating a new string, and some let us return an existing string. We should provide a consistent interface to the caller, so it knows whether to free the result or not. We could do so by xstrdup-ing any existing strings, and having the caller always free. But that makes us inconsistent with branch_get_upstream, so we would prefer to simply take ownership of the resulting string. We do so by storing it inside the "struct branch", just as we do with the upstream refname (in that case we compute it when the branch is created, but there's no reason not to just fill it in lazily in this case). Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 979cb24 commit e291c75

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

remote.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,91 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
17441744
return branch->merge[0]->dst;
17451745
}
17461746

1747+
static const char *tracking_for_push_dest(struct remote *remote,
1748+
const char *refname,
1749+
struct strbuf *err)
1750+
{
1751+
char *ret;
1752+
1753+
ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
1754+
if (!ret)
1755+
return error_buf(err,
1756+
_("push destination '%s' on remote '%s' has no local tracking branch"),
1757+
refname, remote->name);
1758+
return ret;
1759+
}
1760+
1761+
static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
1762+
{
1763+
struct remote *remote;
1764+
1765+
if (!branch)
1766+
return error_buf(err, _("HEAD does not point to a branch"));
1767+
1768+
remote = remote_get(pushremote_for_branch(branch, NULL));
1769+
if (!remote)
1770+
return error_buf(err,
1771+
_("branch '%s' has no remote for pushing"),
1772+
branch->name);
1773+
1774+
if (remote->push_refspec_nr) {
1775+
char *dst;
1776+
const char *ret;
1777+
1778+
dst = apply_refspecs(remote->push, remote->push_refspec_nr,
1779+
branch->refname);
1780+
if (!dst)
1781+
return error_buf(err,
1782+
_("push refspecs for '%s' do not include '%s'"),
1783+
remote->name, branch->name);
1784+
1785+
ret = tracking_for_push_dest(remote, dst, err);
1786+
free(dst);
1787+
return ret;
1788+
}
1789+
1790+
if (remote->mirror)
1791+
return tracking_for_push_dest(remote, branch->refname, err);
1792+
1793+
switch (push_default) {
1794+
case PUSH_DEFAULT_NOTHING:
1795+
return error_buf(err, _("push has no destination (push.default is 'nothing')"));
1796+
1797+
case PUSH_DEFAULT_MATCHING:
1798+
case PUSH_DEFAULT_CURRENT:
1799+
return tracking_for_push_dest(remote, branch->refname, err);
1800+
1801+
case PUSH_DEFAULT_UPSTREAM:
1802+
return branch_get_upstream(branch, err);
1803+
1804+
case PUSH_DEFAULT_UNSPECIFIED:
1805+
case PUSH_DEFAULT_SIMPLE:
1806+
{
1807+
const char *up, *cur;
1808+
1809+
up = branch_get_upstream(branch, err);
1810+
if (!up)
1811+
return NULL;
1812+
cur = tracking_for_push_dest(remote, branch->refname, err);
1813+
if (!cur)
1814+
return NULL;
1815+
if (strcmp(cur, up))
1816+
return error_buf(err,
1817+
_("cannot resolve 'simple' push to a single destination"));
1818+
return cur;
1819+
}
1820+
}
1821+
1822+
die("BUG: unhandled push situation");
1823+
}
1824+
1825+
const char *branch_get_push(struct branch *branch, struct strbuf *err)
1826+
{
1827+
if (!branch->push_tracking_ref)
1828+
branch->push_tracking_ref = branch_get_push_1(branch, err);
1829+
return branch->push_tracking_ref;
1830+
}
1831+
17471832
static int ignore_symref_update(const char *refname)
17481833
{
17491834
unsigned char sha1[20];

remote.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ struct branch {
209209
struct refspec **merge;
210210
int merge_nr;
211211
int merge_alloc;
212+
213+
const char *push_tracking_ref;
212214
};
213215

214216
struct branch *branch_get(const char *name);
@@ -229,6 +231,14 @@ int branch_merge_matches(struct branch *, int n, const char *);
229231
*/
230232
const char *branch_get_upstream(struct branch *branch, struct strbuf *err);
231233

234+
/**
235+
* Return the tracking branch that corresponds to the ref we would push to
236+
* given a bare `git push` while `branch` is checked out.
237+
*
238+
* The return value and `err` conventions match those of `branch_get_upstream`.
239+
*/
240+
const char *branch_get_push(struct branch *branch, struct strbuf *err);
241+
232242
/* Flags to match_refs. */
233243
enum match_refs_flags {
234244
MATCH_REFS_NONE = 0,

0 commit comments

Comments
 (0)