Mark Levedahl [off-list ref] writes:
On Thursday 09 April 2009 20:39:46 Junio C Hamano wrote:
quoted
Mark Levedahl [off-list ref] writes:
quoted
This change allows, for instance
git branch -d refs/heads/foo
to succeed. Without this patch, the code just assumes that the
given branch name should be appended to "refs/heads" or
"refs/remotes", thus attempting (and failing) in the above case
to delete "refs/heads/refs/heads/foo"
Your logic is broken.
Why doesn't the user simply say "git branch -d foo"? The command takes
"the branch name", not "arbitrary ref name".
1) git branch -d refs/<whatever> used to work, I haven't bisected to find
when this stopped working, but the change broke one of my scripts, so this is
not new behavior, it is restoration of previous behavior.
I need to look at the history, if that is the case then perhaps Ok.
2) If I create branch refs/frotz/bar , how do I ever delete it?
By this you must mean .git/refs/heads/refs/frotz/bar, right? Then
shouldn't "git branch -d refs/frotz/bar" just work as is? If you are
talking about .git/refs/frotz/bar, "git branch" should not touch it, it is
not even a branch.
Also, the following all work
3) git branch refs/heads/foo
4) git branch -m refs/heads/foo refs/heads/bar
If you mean it creates .git/refs/heads/refs/heads/foo, then sure it
should. If it creates .git/refs/heads/foo, I think it is broken.
5) git [checkout|pull|push|fetch|show] refs/heads/foo
Among these, checkout is the only special case that can take "branch name"
to switch branches. Everything else takes extended SHA-1 expression.
Checkout can interpret the first argument as "branch to switch to", but it
does not necessarily so---think "detached HEAD", and also think "checking
out paths out of tree-ish".
So, why is "git branch -d" so special?
"git branch -d", "git branch -m" and friends all take branch name, and as
such it can use "@{-1}" to _name_ 'the previous branch". In that context,
you are _not_ naming the commit at the tip of the branch. You are naming
the branch itself.
All other commands happen to take a branch name because that is just one
case of extended SHA-1 expression to name an object. In that context, a
refname (which a branch name is a special case of) refers to the commit
pointed by it. E.g.
"git checkout HEAD~20 -- Makefile"
"git show refs/heads/foo"
"git show heads/foo"
"git show foo"
On Thursday 09 April 2009 23:18:01 you wrote:
All other commands happen to take a branch name because that is just one
case of extended SHA-1 expression to name an object. In that context, a
refname (which a branch name is a special case of) refers to the commit
pointed by it. E.g.
"git checkout HEAD~20 -- Makefile"
"git show refs/heads/foo"
"git show heads/foo"
"git show foo"
I think my underlying problem here is the porcelain's ability to use either a
branch name or a ref name, in different contexts, leading to a sometimes
inconsistent interface. Consider the following
$ git checkout master
checks out branch master
$ git checkout refs/heads/master
checks out commit pointed to by refs/heads/master on a detached
HEAD
$ git checkout -b refs/heads/master refs/heads/master
creates a new branch, refname = refs/heads/refs/heads/master
The last command is the one that I find most curious. The exact same string
has two entirely different meanings to the same command. I can explain why
this happens, but I cannot explain why this is a good thing.
A model I could explain without mental gymnastics would be "branch names are
simply refnames without the leading refs/heads or refs/remotes, and a refname
may be used wherever a branch name is requested. While branch names are
potentially ambiguous, refnames never are." Of course, this would mean that
the refs/heads/refs/... namespace is illegal. I don't know of any other
downside (except of course to someone using that namespace), and frankly I
don't think the existence of the refs/heads/refs namespace is a good thing,
given the potential for confusion.
Just a thought.
Mark
On Sat, Apr 11, 2009 at 01:01:30PM -0400, Mark Levedahl wrote:
$ git checkout -b refs/heads/master refs/heads/master
creates a new branch, refname = refs/heads/refs/heads/master
The last command is the one that I find most curious. The exact same string
has two entirely different meanings to the same command. I can explain why
this happens, but I cannot explain why this is a good thing.
A command like "grep foo foo" has the same property (one string with
different meanings based on argument position). The problem is that you
are thinking of it as:
git checkout -b <branch> <branch>
And I can see why you might think of it that way, because that is what
the synopsis in git-checkout(1) says. :) But it is really:
git checkout -b <branch> <commit>
I'm not sure if changing that synopsis would really help, or if it is
a bit too subtle.
A model I could explain without mental gymnastics would be "branch names are
simply refnames without the leading refs/heads or refs/remotes, and a refname
may be used wherever a branch name is requested. While branch names are
potentially ambiguous, refnames never are." Of course, this would mean that
So the current model is: "branch names are simply refnames without the
leading refs/heads or refs/remotes. A <commit> can be referenced by the
usual names (see git rev-parse, "specifying revisions" for details)".
The thing that I think is more confusing about that is not the
final example you pointed out, but the difference between
git checkout master
and
git checkout refs/heads/master
Which is explained by the fact that the usage for checkout is not
git checkout <branch>
but actually
git checkout <branch|commit>
If a branch, then we checkout the branch. If a commit, then we detach on
that commit.
I'm not sure if that explanation helps you at all, but that is how I
think of it (and it makes sense to me).
-Peff