threads / rfc / 43276

patchRe: [PATCH/RFC] Convenient support of remote branches in git-checkout

Subject: Re: [PATCH/RFC] Convenient support of remote branches in git-checkout

## tl;dr

14 messages between Nov 6, 2006 and Nov 7, 2006. Diffs are folded; open one to read it.

replies: 13people: 3as markdown or json

Josef Weidendorfer· Nov 6, 2006, 23:26 UTC · lore

[PATCH/RFC] Convenient support of remote branches in git-checkout

- When requested to checkout read-only remote branches,
  we try to create a new local development branch with same name.
- When we branch off a remote branch, set up default merge source.
---

This patch does the sensible thing when the user requests to check-out a remote read-only branch (eg. origin/master). It also automatically sets up the default merge source (with remote entry) for a new branch.

Example to hack on git's next branch:
 git-clone --use-separate-remote http://www.kernel.org/pub/scm/git/git.git
 cd git
 git-checkout origin/next
 <hack on next>
 git pull (to merge patches from remote 'next')

The checkout creates local branch 'master' to checkout read-only remote branch 'remotes/origin/master'. Additionally, it sets up 'remotes/origin/master' from remote repository 'origin' as default merge source for the new development branch.

Missing:
- "git branch -D <branch>" should remove remote branch attributes like
  "remote" and "merge"
- As "git-clone" already sets up a local development branch "master", it also
  should set up a default merge source for it
Josef
 git-checkout.sh |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)
Show changes to git-checkout.sh +29 −0
diff --git a/git-checkout.sh b/git-checkout.sh
index 119bca1..63b622e 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -12,6 +12,8 @@ force=
 branch=
 newbranch=
 newbranch_log=
+remote=
+remotebranch=
 merge=
 while [ "$#" != "0" ]; do
     arg="$1"
@@ -55,6 +57,11 @@ while [ "$#" != "0" ]; do
 			then
 				branch="$arg"
 			fi
+			if git-show-ref --verify --quiet -- "refs/remotes/$arg"
+			then
+				remote=$(echo $arg | sed -ne 's!/.*$!!p')
+				remotebranch=$(echo $arg | sed -e 's!^.*/!!')
+			fi
 		elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null)
 		then
 			# checking out selected paths from a tree-ish.
@@ -77,6 +84,20 @@ while [ "$#" != "0" ]; do
     esac
 done
 
+# Create a new local branch when checking out remote read-only branch
+if test -z "$newbranch" -a ! -z "$remotebranch"
+then
+	newbranch=$remotebranch
+	if git-show-ref --verify --quiet -- "refs/heads/$newbranch"
+	then
+		echo "Proposed new branch '$newbranch' to checkout read-only remote branch 'remotes/$remote/$remotebranch' exists!"
+		echo "To checkout, specify a new branch name with -b"
+		exit 1
+	fi
+	echo "Creating local branch '$newbranch' to checkout read-only remote branch 'remotes/$remote/$remotebranch'."
+fi
+
+
 # The behaviour of the command with and without explicit path
 # parameters is quite different.
 #
@@ -211,6 +232,14 @@ if [ "$?" -eq 0 ]; then
 			touch "$GIT_DIR/logs/refs/heads/$newbranch"
 		fi
 		git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
+
+		if test '' != "$remote"
+		then
+			echo "Using 'remotes/$remote/$remotebranch' from remote repository '$remote' as default merge source."
+			git-repo-config branch."$newbranch".remote "$remote"
+			git-repo-config branch."$newbranch".merge "remotes/$remote/$remotebranch"
+		fi
+
 		branch="$newbranch"
 	fi
 	[ "$branch" ] &&
-- 
1.4.3.rc2.gf8ffb
Josef Weidendorfer· Nov 6, 2006, 23:30 UTC · re: Josef Weidendorfer · lore
Oops.
Corrections in example:
On Tuesday 07 November 2006 00:26, Josef Weidendorfer wrote:
Show 9 quoted lines
> Example to hack on git's next branch:
> 
>  git-clone --use-separate-remote http://www.kernel.org/pub/scm/git/git.git
>  cd git
>  git-checkout origin/next
>  <hack on next>
>  git pull (to merge patches from remote 'next')
> 
> The checkout creates local branch 'master' to checkout read-only remote branch
                                     ^ next
> 'remotes/origin/master'. Additionally, it sets up 'remotes/origin/master' from
                  ^next                                             ^next
> remote repository 'origin' as default merge source for the new development branch.
Junio C Hamano· Nov 7, 2006, 00:13 UTC · re: Josef Weidendorfer · lore
Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:
Show 12 quoted lines
> Example to hack on git's next branch:
>
>  git-clone --use-separate-remote http://www.kernel.org/pub/scm/git/git.git
>  cd git
>  git-checkout origin/next
>  <hack on next>
>  git pull (to merge patches from remote 'next')
>
> The checkout creates local branch 'next' to checkout read-only
> remote branch 'remotes/origin/next'. Additionally, it sets up
> 'remotes/origin/next' from remote repository 'origin' as
> default merge source for the new development branch.
I am disturbed by an inconsistency here.
Show 6 quoted lines
> +	if git-show-ref --verify --quiet -- "refs/heads/$newbranch"
> +	then
> +		echo "Proposed new branch '$newbranch' to checkout...
> +		echo "To checkout, specify a new branch name with -b"
> +		exit 1
> +	fi

This logic is guarding against already having a local branch that is called 'next', and that is why the "Proposed new branch" message needs to be there. One explanation of why 'next' exists in the local branch namespace in the first place is probably there are other remote branches than origin that have 'next' and the user previously checked it out. Or perhaps the user has already done this "checkout origin/next" once already.

I wonder if it is more consistent and easy to use to just make this:

	git checkout origin/next
a synonym to:
	git checkout -b origin/next remotes/origin/next
when remotes/origin/next exists and heads/origin/next does not.

Then "git checkout origin/next" would always mean "I want to switch to the branch I use to hack on the branch 'next' Junio has". Do it once and you will get exactly my tip, hack on it, switch out of it and then do it again and you won't lose your previous work but just switch to that branch.

That is, something like this...
---
Show changes to git-checkout.sh +10 −0
diff --git a/git-checkout.sh b/git-checkout.sh
index 119bca1..f6486c6 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -4,6 +4,16 @@ USAGE='[-f] [-b <new_branch>] [-m] [<bra
 SUBDIRECTORY_OK=Sometimes
 . git-sh-setup
 
+# Automatic forking of local branch based on remote
+if test $# = 1 &&
+   git show-ref --verify --quiet -- "refs/remotes/$1" &&
+   ! git show-ref --verify --quiet -- "refs/heads/$1"
+then
+	set x -b "$1" "remotes/$1"
+	echo >&2 "* Forking local branch $1 off of remotes/$1..."
+	shift
+fi
+
 old_name=HEAD
 old=$(git-rev-parse --verify $old_name 2>/dev/null)
 new=
Josef Weidendorfer· Nov 7, 2006, 01:25 UTC · re: Junio C Hamano · lore
On Tuesday 07 November 2006 01:13, Junio C Hamano wrote:
Show 10 quoted lines
> I wonder if it is more consistent and easy to use to just make
> this:
> 
> 	git checkout origin/next
> 
> a synonym to:
> 
> 	git checkout -b origin/next remotes/origin/next
> 
> when remotes/origin/next exists and heads/origin/next does not.
Interesting.

I wonder how often there is a real need for that long branch names. IMHO this convenience behavior of git-checkout should target the majority of possible use cases. Is it really better to default to long branch names instead of asking for explicit branch name in the rare case of conflict (ie. multiple remote repositories with same branch names and you want to develop on both these branches locally)?

Suppose developer2 uses this scheme, and has a local development branch "junio/next", which is based on your "next" branch. Now I want to work on the "next" branch of developer2, getting a local branch name "developer2/junio/next".

Show 5 quoted lines
> Then "git checkout origin/next" would always mean "I want to
> switch to the branch I use to hack on the branch 'next' Junio
> has".  Do it once and you will get exactly my tip, hack on it,
> switch out of it and then do it again and you won't lose your
> previous work but just switch to that branch.

Ah, now I understand your thinking. I admit it has a compelling elegance.

However. Would it not be confusing for newbies (and not only for them) to first reference the remote branch with "origin/next", and afterwards, you get your own development branch by using the exactly same name?

IMHO this kind of aliasing is awkward. When you want to start another topic branch on the remote branch, or want to reference the remote branch for diffs, you have to explicitly specify "remotes/origin/next", making for more typing.

Show 21 quoted lines
> That is, something like this...
> 
> ---
> 
> diff --git a/git-checkout.sh b/git-checkout.sh
> index 119bca1..f6486c6 100755
> --- a/git-checkout.sh
> +++ b/git-checkout.sh
> @@ -4,6 +4,16 @@ USAGE='[-f] [-b <new_branch>] [-m] [<bra
>  SUBDIRECTORY_OK=Sometimes
>  . git-sh-setup
>  
> +# Automatic forking of local branch based on remote
> +if test $# = 1 &&
> +   git show-ref --verify --quiet -- "refs/remotes/$1" &&
> +   ! git show-ref --verify --quiet -- "refs/heads/$1"
> +then
> +	set x -b "$1" "remotes/$1"
> +	echo >&2 "* Forking local branch $1 off of remotes/$1..."
> +	shift
> +fi

I didn't know about "set x" before. Thanks, you never end learning :-)

"git-checkout remotes/origin/next" does not work as expected, and if fixed, it still should guard against an existing local branch "origin/next", don't you think? (Ok, it does not work in my patch, too.)

What do you think about the setup of the default for "git-pull"?
Junio C Hamano· Nov 7, 2006, 02:03 UTC · re: Josef Weidendorfer · lore
Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:
> What do you think about the setup of the default for "git-pull"?

I personally feel that is loading "checkout" with too many different things.

It might be easier to maintain in the long term to have a helper command 'git-fork' to handle the gory details of forking off from an existing branch (merge default setting, branch creation, what else will we have next month? ;-) and perhaps automatically call it from git-checkout as a short-hand.

Junio C Hamano· Nov 7, 2006, 02:08 UTC · re: Junio C Hamano · lore
Junio C Hamano <junkio@cox.net> writes:
Show 12 quoted lines
> Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:
>
>> What do you think about the setup of the default for "git-pull"?
>
> I personally feel that is loading "checkout" with too many
> different things.
>
> It might be easier to maintain in the long term to have a helper
> command 'git-fork' to handle the gory details of forking off
> from an existing branch (merge default setting, branch creation,
> what else will we have next month? ;-) and perhaps automatically
> call it from git-checkout as a short-hand.

Ah, one should never open one's mouth before thinking things twice and then sleeping on it.

I do not think we want a _new_ command. It may make sense to enrich 'git-branch' for that.

If "git-checkout -b" does not use 'git-branch' to create a new branch in the current code, maybe we should, regardless of the usability enhancements under discussion.

Josef Weidendorfer· Nov 7, 2006, 10:18 UTC · re: Junio C Hamano · lore
On Tuesday 07 November 2006 03:08, you wrote:
> I do not think we want a _new_ command.  It may make sense to
> enrich 'git-branch' for that.
I agree.
Probably similar, git-clone should use git-checkout at the end.
> If "git-checkout -b" does not use 'git-branch' to create a new
> branch in the current code, maybe we should, regardless of the
> usability enhancements under discussion.
Yes.
Josef
PS: This is one of the moments I wished git-branch still was a shell
Junio C Hamano· Nov 7, 2006, 02:27 UTC · re: Josef Weidendorfer · lore
Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:
Show 13 quoted lines
>> Then "git checkout origin/next" would always mean "I want to
>> switch to the branch I use to hack on the branch 'next' Junio
>> has".  Do it once and you will get exactly my tip, hack on it,
>> switch out of it and then do it again and you won't lose your
>> previous work but just switch to that branch.
>
> Ah, now I understand your thinking.
> I admit it has a compelling elegance.
>
> However.
> Would it not be confusing for newbies (and not only for them) to
> first reference the remote branch with "origin/next", and afterwards, you
> get your own development branch by using the exactly same name?

When we get these per-branch attributes used widely enough, we might add new vocabulary to our extended sha1 expressions that denotes "the branch I forked this branch off of".

If refs/heads/next is created from refs/remotes/origin/next, perhaps with an updated git-branch command that knows how to help set things up, we might want to be able to refer to remotes/origin/next as "next's upstream". While we are on 'next' branch, we might want to refer to "HEAD's upstream".

I am not sure what the syntax for that should be, though. Perhaps "HEAD@upstream"?

Unlike the regular extended sha1 expression modifiers such as name~n, name^n, and name^{type}, it does not work with arbitrary object name; it can only work with a refname. Which is similar to the '@{time}' notation we added when we started using ref-log. Strictly speaking these should not belong to the sha1 naming layer, but we can have them anyway for the user's convenience.

Josef Weidendorfer· Nov 7, 2006, 10:28 UTC · re: Junio C Hamano · lore
On Tuesday 07 November 2006 03:27, Junio C Hamano wrote:
Show 5 quoted lines
> remotes/origin/next as "next's upstream".  While we are on
> 'next' branch, we might want to refer to "HEAD's upstream".
> 
> I am not sure what the syntax for that should be, though.
> Perhaps "HEAD@upstream"?
I remember an idea floating around was to use a virtual
branch "ORIGIN" which always maps to the upstream of the current
branch.
 
Show 7 quoted lines
> Unlike the regular extended sha1 expression modifiers such as
> name~n, name^n, and name^{type}, it does not work with arbitrary
> object name; it can only work with a refname.  Which is similar
> to the '@{time}' notation we added when we started using
> ref-log.  Strictly speaking these should not belong to the sha1
> naming layer, but we can have them anyway for the user's
> convenience.

Yes, this makes sense. Branch relations like "upstream" is a local configuration issue, similar to reflogs.

I vote for "HEAD@up", short form "@up".
Karl Hasselström· Nov 7, 2006, 06:54 UTC · re: Josef Weidendorfer · lore
On 2006-11-07 02:25:24 +0100, Josef Weidendorfer wrote:
Show 10 quoted lines
> On Tuesday 07 November 2006 01:13, Junio C Hamano wrote:
>
> > Then "git checkout origin/next" would always mean "I want to
> > switch to the branch I use to hack on the branch 'next' Junio
> > has". Do it once and you will get exactly my tip, hack on it,
> > switch out of it and then do it again and you won't lose your
> > previous work but just switch to that branch.
>
> Ah, now I understand your thinking. I admit it has a compelling
> elegance.

I agree. The name is slightly longer than necessary in the common case of only one remote repository, but the reduction of newbie confusion will be worth it. (Non-newbies know how to give the branch any name they want.)

> However. Would it not be confusing for newbies (and not only for
> them) to first reference the remote branch with "origin/next", and
> afterwards, you get your own development branch by using the exactly
> same name?

Not necessarily. As long as they know that there are two kinds of branches, remote and local, it should be perfectly obvious. You check out and modify your local copy of a remote branch, and occasionally pull updates from the remote branch. If there is no local branch corresponding to a certain remote branch, git will make one for you.

> IMHO this kind of aliasing is awkward. When you want to start
> another topic branch on the remote branch, or want to reference the
> remote branch for diffs, you have to explicitly specify
> "remotes/origin/next", making for more typing.

Having more than one local branch for a remote branch is advanced enough that the user should know how to create branches with any name they choose.

But I do agree that calling it "origin/next" the first time you branch, and "remotes/origin/next" subsequent times, is nonintuitive. However, this could be solved by the following message being printed the first time:

  $ git checkout origin/next
  No local branch "origin/next" exists. Creating new local branch
  "origin/next" off of remote branch "remotes/origin/next".
-- 
Karl Hasselström, kha@treskal.com
Josef Weidendorfer· Nov 7, 2006, 10:53 UTC · re: Karl Hasselström · lore
On Tuesday 07 November 2006 07:54, you wrote:
Show 8 quoted lines
> > IMHO this kind of aliasing is awkward. When you want to start
> > another topic branch on the remote branch, or want to reference the
> > remote branch for diffs, you have to explicitly specify
> > "remotes/origin/next", making for more typing.
> 
> Having more than one local branch for a remote branch is advanced
> enough that the user should know how to create branches with any name
> they choose.

But such an advanced szenario is exactly the reason to introduce these long branch names like "origin/next", isn't it? When a newbie probably never is confronted with this szenario, then why give him longer branch names per default? Do you see the contradiction in this argument?

IMHO it should be the other way around: when an advanced user gets this conflict, he knows how to rename the branches by using this more elaborated scheme.

I understand that these long branch names implicity give you information
about the upstream (by including the remote shortcut in front),
but this information (like all branch attributes) should also be
easy available with "git branch --info" or similar. Especially,
when we introduce shortcuts like "@up" (i.e. git-show-ref @up).
 
Show 8 quoted lines
> But I do agree that calling it "origin/next" the first time you
> branch, and "remotes/origin/next" subsequent times, is nonintuitive.
> However, this could be solved by the following message being printed
> the first time:
> 
>   $ git checkout origin/next
>   No local branch "origin/next" exists. Creating new local branch
>   "origin/next" off of remote branch "remotes/origin/next".
My patch already does something like this.
Karl Hasselström· Nov 7, 2006, 13:56 UTC · re: Josef Weidendorfer · lore
On 2006-11-07 11:53:32 +0100, Josef Weidendorfer wrote:
Show 11 quoted lines
> On Tuesday 07 November 2006 07:54, you wrote:
>
> > Having more than one local branch for a remote branch is advanced
> > enough that the user should know how to create branches with any
> > name they choose.
>
> But such an advanced szenario is exactly the reason to introduce
> these long branch names like "origin/next", isn't it? When a newbie
> probably never is confronted with this szenario, then why give him
> longer branch names per default? Do you see the contradiction in
> this argument?

Well, I see your point. However, forcing users to have to unlearn and relearn when they want to use more of git's power feels wrong. It would present an artificial barrier for users wishing to proceed from the newbie stage.

It's more important to have simple rules than to make these rules generate short names. Long names are not conceptually difficult, just a bit cumbersome at times.

> IMHO it should be the other way around: when an advanced user gets
> this conflict, he knows how to rename the branches by using this
> more elaborated scheme.

But what happens when an unexperienced user gets this conflict for the first time (having for the first time used two different remotes)? Your scheme forces her to learn two new things instead of one, creating the artificial barrier I mentioned above.

-- 
Karl Hasselström, kha@treskal.com
Josef Weidendorfer· Nov 7, 2006, 17:04 UTC · re: Karl Hasselström · lore
On Tuesday 07 November 2006 14:56, you wrote:
> But what happens when an unexperienced user gets this conflict for the
> first time (having for the first time used two different remotes)?
> Your scheme forces her to learn two new things instead of one,
> creating the artificial barrier I mentioned above.

I give the user a warning that she has to specify a branch name herself. This does not force her to rename all her branches and go with the new naming <remote>/<remote branch>, but probably makes her do

 repo developer1, branch next => next (magic behavior)
 repo developer2, branch next => next2 (manual specification)
and perhaps rename next to next1 afterwards.

At least I do not want to type long branch names; most of the cloned repos I have do have only one remote. So I would rename the branches names created with the complex magic scheme.

Of course, another way is to be more smart with branch name parsing.
Currently, a given name is searched in
	.git/
	.git/refs/
	.git/refs/tags/
	.git/refs/heads/
	.git/refs/remotes/
	.git/refs/remotes/*/HEAD
What about adding before remotes
	.git/refs/heads/<first-part-of-current-branchname>/
and at the end
	.git/refs/remotes/<first-part-of-current-branchname>/
Ie. when on branch "origin/next", a given name "master" is
parsed as "refs/heads/origin/master" when existing?
So the parsing rule is: "With current branch X and given name Y,
search for a branch as near as possible to X which has Y as
last name component".
This would match current UI, where you have simple branch names
like "master" or "next".
With above rule, you can use "master" to refer
to "refs/heads/origin/master" in the complex model,
and for a read-only remote head "refs/heads/remotes/origin/next",
it is enough to say
	git-checkout next
to get a new local branch "refs/heads/origin/next" created
to work on.

You keep the simple UI and still get the perfect overview with eg. with "gitk --all" even in the case where you work on 10s of remote branches from multiple repository.

Junio C Hamano· Nov 7, 2006, 07:07 UTC · re: Josef Weidendorfer · lore
Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:
Show 13 quoted lines
>> Then "git checkout origin/next" would always mean "I want to
>> switch to the branch I use to hack on the branch 'next' Junio
>> has".  Do it once and you will get exactly my tip, hack on it,
>> switch out of it and then do it again and you won't lose your
>> previous work but just switch to that branch.
>
> Ah, now I understand your thinking.
> I admit it has a compelling elegance.
>
> However.
> Would it not be confusing for newbies (and not only for them) to
> first reference the remote branch with "origin/next", and afterwards, you
> get your own development branch by using the exactly same name?

In that example, the user types "git checkout origin/next". I do not think there is any confusion.

You come from git background from the era git checkout did _not_ have this magic (in other words, "today"), so you implicitly see remotes/ prefixed in front of the "origin/next" string there.

But new people do not see remotes/ prefixed there. To them, the example command line says "Now I want to be on my 'origin/next' branch", and there is nothing 'remote' about it.

The magic under discussion happens to create your 'origin/next' branch automagically from 'remotes/origin/next' when it exists, but that can be transparent to the user [*1*].

Well, your original magic did not propose it that way, but I twisted it.

[Footnote]
*1* This is in line with what I wanted to say in my earlier "if
    I were redoing git from scratch" message when I talked about
    making "remotes" less visible.

← back to recent threads