# [RFC] New kind of upstream branch: base branch

7 messages from 2013-05-15 to 2013-05-15. Participants: Felipe Contreras, Junio C Hamano.
Thread: https://gitlist.dev/t/33826

## Felipe Contreras, 2013-05-15 20:28

Subject: [RFC] New kind of upstream branch: base branch
Message-ID: <5193efe6.c42ab60a.0319.5f2c@mx.google.com>
URL: https://gitlist.dev/e/5193efe6.c42ab60a.0319.5f2c%40mx.google.com

```
Hi,

I've been using Git from the start, but only lately have I forced myself to
configure upstream branches for all my branches, and I've found a few things
more convenient, but others completely contrary to what I expected.

Inconvenient:

Before, I used to do 'git fetch' to simply fetch from 'origin', but now, it
depends on where 'upstream' is set to.

Convinient:

Now, I can just do 'git rebase --interactive' and I don't have to specify the
starting point, which is particularily useful when there's a lot of branches
one depending on another.

I think I'm using 'upstream' for something it was not intended to, and I think
the current 'upstream' behavior should be split into 'upstream' and 'base'.

== base ==

The 'base' branch will be set each time you create a branch from another;
'git checkout -b foobar master' sets 'master' as the 'base' of 'foobar'.

Then you can do 'git rebase foobar@{base}' or simply 'git rebase', and Git will
pick the right branch to rebase unto, even if you have no 'upstream'
configured.

This way 'git fetch' will keep picking 'origin', and other commands that make
use of 'upstrem' would be undisturbed.

If both 'base' and 'upstream' are defined, I think 'git rebase' should use
'base', but since that would break old behavior, perhaps there should be a
configuration variable to enable a different behavior.

I already started writting the patches, and although tedious, I think they
they'll be rather straightforward, but I thought it would be best to hear some
opinions first.

What do you think?

Cheers.

-- 
Felipe Contreras

```

## Junio C Hamano, 2013-05-15 22:20

Subject: Re: [RFC] New kind of upstream branch: base branch
Message-ID: <7vzjvvanro.fsf@alter.siamese.dyndns.org>
URL: https://gitlist.dev/e/7vzjvvanro.fsf%40alter.siamese.dyndns.org
In-Reply-To: <5193efe6.c42ab60a.0319.5f2c@mx.google.com>

```
Felipe Contreras <felipe.contreras@gmail.com> writes:

> The 'base' branch will be set each time you create a branch from another;
> 'git checkout -b foobar master' sets 'master' as the 'base' of 'foobar'.

"git checkout -t -b foobar mastee" would instead set 'upstream' of
'foobar' to the branch 'master' of remote '.' (the current one).
This 'base' is a new mechanism to explicitly say "The upstream of
this branch lives locally" by not setting "branch.foobar.remote".

> Then you can do 'git rebase foobar@{base}' or simply 'git rebase', and Git will
> pick the right branch to rebase unto, even if you have no 'upstream'
> configured.

Surely you can teach rebase to pay attention to 'base' and achieve
that.  But you can already do so with upstream, so this is not an
advertisement of a 'plus', but rather a lack of 'minus' (which is
not a bad thing at all).

> This way 'git fetch' will keep picking 'origin', and other commands that make
> use of 'upstrem' would be undisturbed.

And this is the true plus, because 'git fetch' with the current
"setting a local base using the same upstream mechanism to point at
a branch of _this_ repository, indirectly setting the upstream
_repository_ for this branch to the current repository" will end up
making you fetch from yourself, which is not very interesting.

So I think I understand your itch and I agree that it is a valid
one.

I however am not yet convinced if that direction is what you really
want go in, though.  What should your 'git pull' on that branch do,
for example?

When you are on foobar and want to integrate with the branch you
based your work on (i.e. local 'master'), you can do one of these:

    $ git pull
    $ git pull --rebase

to "fetch the upstream branch and integrate with it", without having
to even care if that upstream branch is from the remote, or happens
to be truly local.  By making 'git fetch' to go to the remote origin
site, what will you be merging (or rebasing on) when you do the
above two?

Incidentally, I suspect you can do exactly the same thing without
introducing a new concept "base" and instead special casing a remote
whose URL is "."; you essentially declare that "The upstream of this
branch whose branch.$name.remote is set to '.' lives locally", which
is not all that different from saying "The upstream of this branch
whose branch.$name.base exists lives locally", which is what you
seem to be proposing.  One of the things this alternative approach
would "special case" such remote is probably to cause "git fetch" to
ignore such a branch.$name.remote setting and instead go fetch from
'origin', just like your "if there is branch.$name.base, but no
branch.$name.remote, fetch will go to 'origin'" does.

But it has exactly the same "what happens when you do 'git pull'"
problem, so even though it is conceptually a lot simpler, it has the
same brokenness.

```

## Junio C Hamano, 2013-05-15 22:50

Subject: Re: [RFC] New kind of upstream branch: base branch
Message-ID: <7vvc6jame7.fsf@alter.siamese.dyndns.org>
URL: https://gitlist.dev/e/7vvc6jame7.fsf%40alter.siamese.dyndns.org
In-Reply-To: <7vzjvvanro.fsf@alter.siamese.dyndns.org>

```
Junio C Hamano <gitster@pobox.com> writes:

> I however am not yet convinced if that direction is what you really
> want go in, though.  What should your 'git pull' on that branch do,
> for example?
>
> When you are on foobar and want to integrate with the branch you
> based your work on (i.e. local 'master'), you can do one of these:
>
>     $ git pull
>     $ git pull --rebase
>
> to "fetch the upstream branch and integrate with it", without having
> to even care if that upstream branch is from the remote, or happens
> to be truly local.  By making 'git fetch' to go to the remote origin
> site, what will you be merging (or rebasing on) when you do the
> above two?
>
> Incidentally, I suspect you can do exactly the same thing without
> introducing a new concept "base" and instead special casing a remote
> whose URL is "."; you essentially declare that "The upstream of this
> branch whose branch.$name.remote is set to '.' lives locally", which
> is not all that different from saying "The upstream of this branch
> whose branch.$name.base exists lives locally", which is what you
> seem to be proposing.  One of the things this alternative approach
> would "special case" such remote is probably to cause "git fetch" to
> ignore such a branch.$name.remote setting and instead go fetch from
> 'origin', just like your "if there is branch.$name.base, but no
> branch.$name.remote, fetch will go to 'origin'" does.
>
> But it has exactly the same "what happens when you do 'git pull'"
> problem, so even though it is conceptually a lot simpler, it has the
> same brokenness.

I do not think of a good way to fix the 'git pull' confusion; the
desire to 'fetch from the overall upstream repository regardless of
which branch I am on' is a valid and understandable one, but that
does not mesh well with "'git pull' is 'git fetch' followed by
either merge or rebase to integrate the result" and "git merge or
git rebase pays attention to the other branch that is specified to
integrate with".  The best we could do might be to simply forbid
"git pull" if your current branch is marked with branch.$name.base
but still allow "git fetch".

The changes that are involved are:

 * Do not change anything to @{upstream}'s definition, that is,
   "checkout -t -b A localbranch" will set branch.A.remote to '.',
   and "git log A@{u}..A" will stand for "git log localbranch..A".

 * Current 'git fetch' pays attention to branch.A.remote when you
   are on branch A, and tries to fetch from there.  Stop doing that
   when branch.A.remote is set to '.' (the current repository) and
   let other rules in the current implementation decide what remote
   to fetch from. Also teach it to error out when branch.A.remote is
   set to '.' when a new --forbid-local option is passed.

 * Teach 'git pull' to pass --forbid-local option to 'git fetch',
   and let an error return fail the whole thing.

Ah, alternatively, instead of adding --forbid-local, we could modify
the changes for 'git fetch' and 'git pull' to read like this:

 * Current 'git fetch' pays attention to branch.A.remote when you
   are on branch A, and tries to fetch from there.  Stop doing that
   when branch.A.remote is set to '.' (the current repository) and
   let other rules in the current implementation decide what remote
   to fetch from, unless a new --allow-fetch-from-local option is
   passed.

 * Teach 'git pull' to pass --allow-fetch-from-local to 'git fetch'.

If we did this, we can keep the "git pull [--rebase]" as a way to
integrate with what you specified as your upstream, which is a
common expectation, without forcing you to say "git fetch origin".

```

## Felipe Contreras, 2013-05-15 22:56

Subject: Re: [RFC] New kind of upstream branch: base branch
Message-ID: <CAMP44s1kWkNgFMAvThczCvTZppWGca0YvGv=CQ15GtViA1zy_Q@mail.gmail.com>
URL: https://gitlist.dev/e/CAMP44s1kWkNgFMAvThczCvTZppWGca0YvGv%3DCQ15GtViA1zy_Q%40mail.gmail.com
In-Reply-To: <7vzjvvanro.fsf@alter.siamese.dyndns.org>

```
On Wed, May 15, 2013 at 5:20 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
>> The 'base' branch will be set each time you create a branch from another;
>> 'git checkout -b foobar master' sets 'master' as the 'base' of 'foobar'.
>
> "git checkout -t -b foobar mastee" would instead set 'upstream' of
> 'foobar' to the branch 'master' of remote '.' (the current one).

Yeah, but I don't to set an upstream, because 'master' is not the
upstream of 'foobar'.

> This 'base' is a new mechanism to explicitly say "The upstream of
> this branch lives locally" by not setting "branch.foobar.remote".

No, 'base' can point to a remote tracking branch.

>> Then you can do 'git rebase foobar@{base}' or simply 'git rebase', and Git will
>> pick the right branch to rebase unto, even if you have no 'upstream'
>> configured.
>
> Surely you can teach rebase to pay attention to 'base' and achieve
> that.  But you can already do so with upstream, so this is not an
> advertisement of a 'plus', but rather a lack of 'minus' (which is
> not a bad thing at all).

Only if there's an upstream configured, which many times is not the
case, and many times causes side-effects the user doesn't want to.

The purpose of 'upstream' is completely different.

>> This way 'git fetch' will keep picking 'origin', and other commands that make
>> use of 'upstrem' would be undisturbed.
>
> And this is the true plus, because 'git fetch' with the current
> "setting a local base using the same upstream mechanism to point at
> a branch of _this_ repository, indirectly setting the upstream
> _repository_ for this branch to the current repository" will end up
> making you fetch from yourself, which is not very interesting.
>
> So I think I understand your itch and I agree that it is a valid
> one.
>
> I however am not yet convinced if that direction is what you really
> want go in, though.  What should your 'git pull' on that branch do,
> for example?

Exactly the same as 'git pull' does right now.

'base' has absolutely nothing to do with pulling or pushing.

> When you are on foobar and want to integrate with the branch you
> based your work on (i.e. local 'master'), you can do one of these:
>
>     $ git pull
>     $ git pull --rebase
>
> to "fetch the upstream branch and integrate with it", without having
> to even care if that upstream branch is from the remote, or happens
> to be truly local.  By making 'git fetch' to go to the remote origin
> site, what will you be merging (or rebasing on) when you do the
> above two?

The same as we do now.

> Incidentally, I suspect you can do exactly the same thing without
> introducing a new concept "base" and instead special casing a remote
> whose URL is "."; you essentially declare that "The upstream of this
> branch whose branch.$name.remote is set to '.' lives locally", which
> is not all that different from saying "The upstream of this branch
> whose branch.$name.base exists lives locally", which is what you
> seem to be proposing.

That would be good, but it doesn't have the same benefits:

If I have set an 'upstream' branch, say 'github/master', and I have
'base' branch, say 'origin/master'. I would expect 'git rebase' to
rebase onto 'origin/master'. When I do 'git push', I expect to push to
'github/master'. Moreover, I would expect 'git fetch' to fetch from
'origin', but that can be discussed later.

Right now I'm forced to choose a single branch and set it to
'upstream'. If I choose 'origin/master' (which is not really the
upstream), the rebase would do what I want, but not the push, unless I
have configured a remote.pushdefault, but that would only work if I
the real upstream is the common one. If I choose 'github/master', then
rebase would not do what I want (and neither would fetch).

> One of the things this alternative approach
> would "special case" such remote is probably to cause "git fetch" to
> ignore such a branch.$name.remote setting and instead go fetch from
> 'origin', just like your "if there is branch.$name.base, but no
> branch.$name.remote, fetch will go to 'origin'" does.
>
> But it has exactly the same "what happens when you do 'git pull'"
> problem, so even though it is conceptually a lot simpler, it has the
> same brokenness.

There is no brokenness; 'git pull' does different things depending on
the configuration. With my proposal nothing would change.

-- 
Felipe Contreras

```

## Junio C Hamano, 2013-05-15 23:14

Subject: Re: [RFC] New kind of upstream branch: base branch
Message-ID: <7vr4h7al9k.fsf@alter.siamese.dyndns.org>
URL: https://gitlist.dev/e/7vr4h7al9k.fsf%40alter.siamese.dyndns.org
In-Reply-To: <CAMP44s1kWkNgFMAvThczCvTZppWGca0YvGv=CQ15GtViA1zy_Q@mail.gmail.com>

```
Felipe Contreras <felipe.contreras@gmail.com> writes:

> Exactly the same as 'git pull' does right now.

You set

	[branch 'foobar']
        	base = refs/heads/master

then 'git pull [--rebase]' will go to 'origin' due to the default,
but then what branch from the 'origin' are you integrating with?

Do you mean you are also setting both remote and upstream, perhaps
like this, for all of your branches?

	[remote "origin"]
		url = github.com:maintainer/hisproject
                fetch = +refs/heads/*:refs/remotes/origin/*

	[remote "github"]
        	url = github.com:felipec/myproject
                fetch = +refs/heads/*:refs/remotes/github/*

	[branch 'foobar']
        	base = refs/heads/master
		remote = github ;# or is it origin???
                upstream = refs/heads/foobar

Then your 'git pull' will fetch from remote 'origin' and integrate
with its 'foobar' and 'push' may go to update 'foobar' at 'github'.

Perhaps that is what you meant.

> 'base' has absolutely nothing to do with pulling or pushing.

I agree it shouldn't have anything to do with pushing, but given
that 'git pull [--rebase]' is a way to do a 'merge' or 'rebase' with
what you 'git fetch', introducing something that 'merge' or 'rebase'
pays attention to that does not have anything to do with 'pull'
sounds like it breaks existing end user expectation.

But that does not mean it is a bad idea. The behaviour changes only
when you have branch.$name.base, so I suspect that we do not need to
worry about "what if the user has both?" case you mentioned in your
first message.

I think I misunderstood what you meant.  If it is the norm to have
both base and upstream/remote in branch.$name (as opposed to "have
only branch.$name.base and not branch.$name.remote to force fetch to
go to the default 'origin'), then 'git pull' will not break and I
can see how many things would work naturally (admittedly I can only
say 'many things', not 'everything', at this point, as I haven't
thought things through).

```

## Felipe Contreras, 2013-05-15 23:18

Subject: Re: [RFC] New kind of upstream branch: base branch
Message-ID: <CAMP44s2BXoUWGPWof+0u6=_UQXpPjA3jtBh0sKS+hhsd-=ViVg@mail.gmail.com>
URL: https://gitlist.dev/e/CAMP44s2BXoUWGPWof%2B0u6%3D_UQXpPjA3jtBh0sKS%2Bhhsd-%3DViVg%40mail.gmail.com
In-Reply-To: <7vvc6jame7.fsf@alter.siamese.dyndns.org>

```
On Wed, May 15, 2013 at 5:50 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> I however am not yet convinced if that direction is what you really
>> want go in, though.  What should your 'git pull' on that branch do,
>> for example?
>>
>> When you are on foobar and want to integrate with the branch you
>> based your work on (i.e. local 'master'), you can do one of these:
>>
>>     $ git pull
>>     $ git pull --rebase
>>
>> to "fetch the upstream branch and integrate with it", without having
>> to even care if that upstream branch is from the remote, or happens
>> to be truly local.  By making 'git fetch' to go to the remote origin
>> site, what will you be merging (or rebasing on) when you do the
>> above two?
>>
>> Incidentally, I suspect you can do exactly the same thing without
>> introducing a new concept "base" and instead special casing a remote
>> whose URL is "."; you essentially declare that "The upstream of this
>> branch whose branch.$name.remote is set to '.' lives locally", which
>> is not all that different from saying "The upstream of this branch
>> whose branch.$name.base exists lives locally", which is what you
>> seem to be proposing.  One of the things this alternative approach
>> would "special case" such remote is probably to cause "git fetch" to
>> ignore such a branch.$name.remote setting and instead go fetch from
>> 'origin', just like your "if there is branch.$name.base, but no
>> branch.$name.remote, fetch will go to 'origin'" does.
>>
>> But it has exactly the same "what happens when you do 'git pull'"
>> problem, so even though it is conceptually a lot simpler, it has the
>> same brokenness.
>
> I do not think of a good way to fix the 'git pull' confusion; the
> desire to 'fetch from the overall upstream repository regardless of
> which branch I am on' is a valid and understandable one, but that
> does not mesh well with "'git pull' is 'git fetch' followed by
> either merge or rebase to integrate the result"

But that is not true. It's only true when merge.defaulttoupstream=true.

> and "git merge or
> git rebase pays attention to the other branch that is specified to
> integrate with".  The best we could do might be to simply forbid
> "git pull" if your current branch is marked with branch.$name.base
> but still allow "git fetch".

Why forbid it? Why not leave it as it is?

> The changes that are involved are:
>
>  * Do not change anything to @{upstream}'s definition, that is,
>    "checkout -t -b A localbranch" will set branch.A.remote to '.',
>    and "git log A@{u}..A" will stand for "git log localbranch..A".

Yes, but in addition it would set 'branch.A.base'.

>  * Current 'git fetch' pays attention to branch.A.remote when you
>    are on branch A, and tries to fetch from there.  Stop doing that
>    when branch.A.remote is set to '.' (the current repository) and
>    let other rules in the current implementation decide what remote
>    to fetch from. Also teach it to error out when branch.A.remote is
>    set to '.' when a new --forbid-local option is passed.

I don't see the point of --forbid-local, but otherwise yeah.

> Ah, alternatively, instead of adding --forbid-local, we could modify
> the changes for 'git fetch' and 'git pull' to read like this:
>
>  * Current 'git fetch' pays attention to branch.A.remote when you
>    are on branch A, and tries to fetch from there.  Stop doing that
>    when branch.A.remote is set to '.' (the current repository) and
>    let other rules in the current implementation decide what remote
>    to fetch from, unless a new --allow-fetch-from-local option is
>    passed.
>
>  * Teach 'git pull' to pass --allow-fetch-from-local to 'git fetch'.

Perhaps.

> If we did this, we can keep the "git pull [--rebase]" as a way to
> integrate with what you specified as your upstream, which is a
> common expectation, without forcing you to say "git fetch origin".

I'm starting to change my mind. Perhaps instead of aiming to change
the behavior 'git rebase', it would make more sense to change the
behavior of 'git push'. So that 'git rebase', 'git fetch' and 'git
pull' all use 'upstream' as is currently the case, but 'git push'
would use something different. That would solve my 'origin/master' vs.
'github/master' distinction. But I would like 'git checkout -b foo
master' to automatically setup 'upstream', and 'git checkout -t -b foo
master' to in addition set this other kind that only affects 'git
push'. This would be a much more intrusive change though.

So, 'branch.A.merge' would become 'branch.A.upstream' and have the
full tracking branch (e.g. 'refs/remotes/github/master'), and
'branch.A.remote' would only affect 'git push'.

-- 
Felipe Contreras

```

## Felipe Contreras, 2013-05-15 23:32

Subject: Re: [RFC] New kind of upstream branch: base branch
Message-ID: <CAMP44s3AwODeOE-D+xYyBQzu4TQfbs1P1N6tG4CW9w5LPH424A@mail.gmail.com>
URL: https://gitlist.dev/e/CAMP44s3AwODeOE-D%2BxYyBQzu4TQfbs1P1N6tG4CW9w5LPH424A%40mail.gmail.com
In-Reply-To: <7vr4h7al9k.fsf@alter.siamese.dyndns.org>

```
On Wed, May 15, 2013 at 6:14 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Felipe Contreras <felipe.contreras@gmail.com> writes:
>
>> Exactly the same as 'git pull' does right now.
>
> You set
>
>         [branch 'foobar']
>                 base = refs/heads/master
>
> then 'git pull [--rebase]' will go to 'origin' due to the default,
> but then what branch from the 'origin' are you integrating with?

The same as if you didn't have 'branch.foobar.base': none. You get an
error because there's no upstream.

> Do you mean you are also setting both remote and upstream, perhaps
> like this, for all of your branches?
>
>         [remote "origin"]
>                 url = github.com:maintainer/hisproject
>                 fetch = +refs/heads/*:refs/remotes/origin/*
>
>         [remote "github"]
>                 url = github.com:felipec/myproject
>                 fetch = +refs/heads/*:refs/remotes/github/*
>
>         [branch 'foobar']
>                 base = refs/heads/master
>                 remote = github ;# or is it origin???
>                 upstream = refs/heads/foobar
>
> Then your 'git pull' will fetch from remote 'origin' and integrate
> with its 'foobar' and 'push' may go to update 'foobar' at 'github'.
>
> Perhaps that is what you meant.

That's what I described in my example.

>> 'base' has absolutely nothing to do with pulling or pushing.
>
> I agree it shouldn't have anything to do with pushing, but given
> that 'git pull [--rebase]' is a way to do a 'merge' or 'rebase' with
> what you 'git fetch', introducing something that 'merge' or 'rebase'
> pays attention to that does not have anything to do with 'pull'
> sounds like it breaks existing end user expectation.

But it's already broken.

'git pull' is not the same as 'git fetch'+'git merge'. That *only*
works  when merge.defaulttoupstream is set.

> But that does not mean it is a bad idea. The behaviour changes only
> when you have branch.$name.base, so I suspect that we do not need to
> worry about "what if the user has both?" case you mentioned in your
> first message.

Indeed. It wouldn't be an intrusive change; it would only affect 'git
rebase', and nothing more.

But I'm wondering if the behavior of 'git fecth' should change as
well. And in fact it should be the other way around.

> I think I misunderstood what you meant.  If it is the norm to have
> both base and upstream/remote in branch.$name (as opposed to "have
> only branch.$name.base and not branch.$name.remote to force fetch to
> go to the default 'origin'), then 'git pull' will not break and I
> can see how many things would work naturally (admittedly I can only
> say 'many things', not 'everything', at this point, as I haven't
> thought things through).

But I think the norm would be to have only 'base', because a lot of
people don't manually set an upstream branch.

In the end it all boils down to; which is the upstream
'origin/master', or 'github/master'? Well, it is 'origin/master', so
'upstream' should point there, but I don't want to push to the
'upstream', I want to push somewhere else by default.

-- 
Felipe Contreras

```
