Since this is not strictly related to the topic of `git switch` I
renamed the thread.
Sergey Organov wrote:
quoted
Felipe Contreras [off-list ref] writes:
quoted
Sergey Organov wrote:
quoted
quoted
quoted
Overall, if we aim at clear documentation, we need to define our
documentation terms as precise as possible, and then use them
consistently.
For example:
"branch": a chain of commits
"branch tip": the most recent commit in a branch
"branch name": specific type of symbolic reference pointing to a
branch tip
Completely agree on all three (although I would call it "branch head",
not "branch tip").
I see why "branch head", as you later introduce "branch tail", but a
branch (of a plant) has no "head" (nor "tail"), right? BTW, how the base
of a plant branch is called in English, and how one finds "branch tail"
on a real tree anyway? I mean, there are probably a few of them, at
every fork. In Git it's even more vague, as a branch could logically
begin at any place, not necessarily at a fork point.
We don't necessarily need a 1-to-1 mapping with common English (although
that would be nice). Anoher option could be "base" and "tip".
quoted
OTOH, "head" and "tail" are obviously taken from CS "list" concept, and,
provided "chain" == "list", it does make sense.
I took it from Mercurial, where the tip of a branch is called "head",
and in fact a branch can have multiple heads.
quoted
And then we have 'HEAD' that points to the current branch tip anyway.
It actually points to a branch, or rather references a branch, since it
uses the branch name.
Yes, but it still points to the branch tip, indirectly, or even
directly, when in "detached head" state, that, by the way, I'd vote to
abandon, replacing it with more user-friendly "unnamed branch" or
something like that.
quoted
Dunno, in fact I don't have any preference among "tip" and "head".
I don't either, but from different sources (non-git-specific) I've heard
"head" more often.
quoted
As for branch tail, I do have convention of marking start of a
long-standing branch with corresponding tag, where branch "foo" has
corresponding "foo-bp" tag marking its "branch point". Recently I
started to mark start of feature branch with yet another branch "foo-bp"
rather than tag, "foo" being set to track "foo-bp", that allows to
automate rebasing of "foo" against correct base.
So foo-bp is the upstream of foo, and you do basically:
git rebase foo@{upstream}
Yep, but essential feature to me is that I in fact use tools that simply
run bare
git rebase
and that "just works" (tm).
This is works if your base (or tail, or whatever) is static, but many
branches jump around, and that's where @{tail} comes in handy.
Yeah, I see. When I need to make a branch jump around, I do need to
manually move my references, but that's fortunately very rare use-case
for me. Having direct support for that is still a win.
You can do this:
git rebase --onto foo@{upstream} foo@{tail}
This will always rebase the right commits (no need to look into the
reflog). So you can say that the branch is foo@{tail}..foo.
I see where and when it's useful, but for a feature branch 99% of times
I don't want to rebase it onto some true upstream. I rather want to just
fiddle with the branch in place, and I prefer to setup things the way
that ensures that bare "git rebase" does "the right thing".
Probably that could be solved by a branch-local configuration that makes
"git rebase" become "git rebase @{tail}" for the branch instead of "git
rebase @{upstream}"
Another advantage of having this notion is that `git rebase`
automatically updates the tail (in this case to foo@{upstream}).
Yep, looks useful. Is it all local to given repo, or else?
Thanks,
--
Sergey Organov
This is works if your base (or tail, or whatever) is static, but many
branches jump around, and that's where @{tail} comes in handy.
Yeah, I see. When I need to make a branch jump around, I do need to
manually move my references, but that's fortunately very rare use-case
for me. Having direct support for that is still a win.
quoted
You can do this:
git rebase --onto foo@{upstream} foo@{tail}
This will always rebase the right commits (no need to look into the
reflog). So you can say that the branch is foo@{tail}..foo.
Maybe I am missing something, is tail for tracking branches only, or for
just any branch?
If for any branch, looking at
A => B => C => D master
|
\ / => G => H branch_1
=> E => F
\ => I => J branch_2
Where is the base of branch_1 and branch_2?
(and does it matter if they have an upstream)
Maybe branch_1 diverged from Master, and then branch_2 from branch_1?
Maybe the other way round.
Maybe there was a branch_0 (that got removed),
and branch_0 diverged from master, and branch_1 and branch_2 both from
branch_0?
---
Also base may be misleading.
If head is the one end of the commit chains, then base should be the other.
But all branches contain commits A (and B). So the base would be A.
"fork" would be more descriptive IMHO?
Also, if that is to save the user from looking up fork points, maybe
extend the syntax
branch_1@{fork:branch_2}
branch_1@{fork:master}
Depending on some of the answers to the above
branch_1@{fork}
nearest fork, or upstream fork?
From: Felipe Contreras <hidden> Date: 2021-07-08 03:12:46
Sergey Organov wrote:
Felipe Contreras [off-list ref] writes:
quoted
Since this is not strictly related to the topic of `git switch` I
renamed the thread.
Sergey Organov wrote:
quoted
Felipe Contreras [off-list ref] writes:
quoted
Sergey Organov wrote:
quoted
quoted
quoted
Overall, if we aim at clear documentation, we need to define our
documentation terms as precise as possible, and then use them
consistently.
For example:
"branch": a chain of commits
"branch tip": the most recent commit in a branch
"branch name": specific type of symbolic reference pointing to a
branch tip
Completely agree on all three (although I would call it "branch head",
not "branch tip").
I see why "branch head", as you later introduce "branch tail", but a
branch (of a plant) has no "head" (nor "tail"), right? BTW, how the base
of a plant branch is called in English, and how one finds "branch tail"
on a real tree anyway? I mean, there are probably a few of them, at
every fork. In Git it's even more vague, as a branch could logically
begin at any place, not necessarily at a fork point.
We don't necessarily need a 1-to-1 mapping with common English (although
that would be nice). Anoher option could be "base" and "tip".
quoted
OTOH, "head" and "tail" are obviously taken from CS "list" concept, and,
provided "chain" == "list", it does make sense.
I took it from Mercurial, where the tip of a branch is called "head",
and in fact a branch can have multiple heads.
quoted
And then we have 'HEAD' that points to the current branch tip anyway.
It actually points to a branch, or rather references a branch, since it
uses the branch name.
Yes, but it still points to the branch tip, indirectly, or even
directly, when in "detached head" state, that, by the way, I'd vote to
abandon, replacing it with more user-friendly "unnamed branch" or
something like that.
Yes, but most of the time it's indirectly.
quoted
quoted
Dunno, in fact I don't have any preference among "tip" and "head".
I don't either, but from different sources (non-git-specific) I've heard
"head" more often.
quoted
As for branch tail, I do have convention of marking start of a
long-standing branch with corresponding tag, where branch "foo" has
corresponding "foo-bp" tag marking its "branch point". Recently I
started to mark start of feature branch with yet another branch "foo-bp"
rather than tag, "foo" being set to track "foo-bp", that allows to
automate rebasing of "foo" against correct base.
So foo-bp is the upstream of foo, and you do basically:
git rebase foo@{upstream}
Yep, but essential feature to me is that I in fact use tools that simply
run bare
git rebase
and that "just works" (tm).
I typed the revision explicitly, but `git rebase` would work just fine.
quoted
This is works if your base (or tail, or whatever) is static, but many
branches jump around, and that's where @{tail} comes in handy.
Yeah, I see. When I need to make a branch jump around, I do need to
manually move my references, but that's fortunately very rare use-case
for me. Having direct support for that is still a win.
quoted
You can do this:
git rebase --onto foo@{upstream} foo@{tail}
This will always rebase the right commits (no need to look into the
reflog). So you can say that the branch is foo@{tail}..foo.
I see where and when it's useful, but for a feature branch 99% of times
I don't want to rebase it onto some true upstream. I rather want to just
fiddle with the branch in place, and I prefer to setup things the way
that ensures that bare "git rebase" does "the right thing".
But that's precisely the point: when you do `git rebase` you don't have
to type the base or --onto anymore. It's done automatically.
Not just for your long-standing branches, but for *any* branch.
Probably that could be solved by a branch-local configuration that makes
"git rebase" become "git rebase @{tail}" for the branch instead of "git
rebase @{upstream}"
No. @{upstream} is where you want to rebase *to*, @{tail} is where you
want to rebase *from*.
When you do:
git rebase foo@{upstream}
This is basically the same as:
git checkout foo@{upstream}^0
git cherry-pick --right-only foo@{upstream}...foo
git is smart enough to figure out what commits are already part of
foo@{upstream}, and those are skipped, but at no point was any "base"
calculated (at least not from `git rebase`).
Most of the time `git rebase` works fine, because there aren't too many
commits to figure out where they should go, but it's definitely not
efficient, and there's many corner-cases (see a Linux kernel maintaner
baffled by what the hell `git rebase` is doing [1]).
quoted
Another advantage of having this notion is that `git rebase`
automatically updates the tail (in this case to foo@{upstream}).
Yep, looks useful. Is it all local to given repo, or else?
From: Felipe Contreras <hidden> Date: 2021-07-08 03:39:59
Martin wrote:
On 08/07/2021 00:07, Sergey Organov wrote:
quoted
Felipe Contreras [off-list ref] writes:
quoted
This is works if your base (or tail, or whatever) is static, but many
branches jump around, and that's where @{tail} comes in handy.
Yeah, I see. When I need to make a branch jump around, I do need to
manually move my references, but that's fortunately very rare use-case
for me. Having direct support for that is still a win.
quoted
You can do this:
git rebase --onto foo@{upstream} foo@{tail}
This will always rebase the right commits (no need to look into the
reflog). So you can say that the branch is foo@{tail}..foo.
Maybe I am missing something, is tail for tracking branches only, or for
just any branch?
Any branch.
If for any branch, looking at
A => B => C => D master
|
\ / => G => H branch_1
=> E => F
\ => I => J branch_2
Where is the base of branch_1 and branch_2?
It depends where the corresponding `git switch --create` command was
issued.
If you did `git switch --create branch_1 B`, then @{tail} is B.
If you did `git switch --create branch_1 F`, then @{tail} is F.
(and does it matter if they have an upstream)
No. That's completely independent.
Maybe branch_1 diverged from Master, and then branch_2 from branch_1?
Maybe the other way round.
Maybe there was a branch_0 (that got removed),
and branch_0 diverged from master, and branch_1 and branch_2 both from
branch_0?
Yeap, the tails of branch_1 and branch_2 could be literally anywhere.
That information is not recoverable from the current data structures of
git, thus the proposal to add a new one.
---
Also base may be misleading.
If head is the one end of the commit chains, then base should be the other.
But all branches contain commits A (and B). So the base would be A.
All branches contain A, but only one branch could have A as a
base/tail (under normal operations), and likely none do.
Suppose branch_2 was created this way:
git switch --create branch_2 A
Then commit B was created under branch_2. Then master was fast-forwarded
to branch_2, so you have:
A => B master
^ ^
tail/branch_2 -+ +- head/branch_2
Both branches have A, but only branch_2 has A as tail.
As both branches move forward they diverge, and the "fork-point" is B,
but B is not the tail of *any* branch.
Naturally then branch_1 would be created with F as a starting point, so
that would be the tail of branch_1.
And once again, even though F is part of both branch_1 and branch_2,
it's the tail of branch_1 *only*.
This is a convoluted way of saying: the tail of a branch is the point
where that branch was created.
"fork" would be more descriptive IMHO?
As you can see from the example above, the tail doesn't necessarily have
to be a fork-point.
Not to mention that there can be multiple forks after the tail (e.g. B
and F).
Also, if that is to save the user from looking up fork points, maybe
extend the syntax
branch_1@{fork:branch_2}
branch_1@{fork:master}
Depending on some of the answers to the above
branch_1@{fork}
nearest fork, or upstream fork?
Except it's not necessarily a fork, nor the nearest, nor related to
upstream...
So it's not a fork.
It can be literally any commit.
Cheers.
--
Felipe Contreras
Yeap, the tails of branch_1 and branch_2 could be literally anywhere.
That information is not recoverable from the current data structures of
git, thus the proposal to add a new one.
Ok, thanks for the all the explanation.
A word on the name "tail". IMHO really confusing. I get where it is
coming from.
But a lot of people will know head and tail utilities from their shell.
And "tail" is the one that shows lines on the end of the file to which
new data is added. Which is "head" in git.
Also a tail is something that follows, but (except for rebase), the base
point is fixed.
I think (despite my earlier comment) "base" is a better word.
It also goes along with "git rebase" which acts on the "base".
However wording around that topic probably still needs to be very careful.
"base" must be clearly distinguished from "start". Because "start" might
imply that only commits from here on forward are contained, but that
contradicts --contains which reports root to head.
> Suppose branch_2 was created this way:
>
> git switch --create branch_2 A
>
> Then commit B was created under branch_2. Then master was fast-forwarded
> to branch_2, so you have:
>
> A => B master
> ^ ^
> tail/branch_2 -+ +- head/branch_2
>
> Both branches have A, but only branch_2 has A as tail.
So base (tail) is the shared commit "A" on which branch_2 was created.
(rather than the first commit made in branch_2 which is "B")
I can see how that is needed for "git rebase" so @{base} can be used for
<upstream>.
What happens if branch_2 is rebased?
Will the base be set to the commit onto which the branch was rebased?
A => B => C => D => E master
\ => F => G foo (base = B)
foo was created on B, then fast forwarded to C, then diverged.
git rebase --onto A foo@{base} foo
Now that foo diverges before B, having B as base for foo seems odd.
(Also A will have C' as child, So the base really is A now)
git rebase --onto E foo@{base} foo
In this case C is already contained in master, so it will be skipped.
If the base is moved, then foo@{base}..foo will no longer contain C.
IMHO that is correct, because rebase skipped it.
The alternative if base = C would be kept, then foo@{base}..foo would
contain D and E. And that seems wrong?
Will there be a way to manually repoint the base?
A => B => C => D master
\ => E => F foo
\ => G => H bar (base = F)
If I do
git rebase --onto master bar@{base} bar
then the commits E and F will not be part of the rebase.
That is fine. I must handle them before.
But if I deleted foo (or for other reasons decide E and F should be
handled if I rebase bar) can I make them to be included?
Something like
git base --repoint B bar
From: Felipe Contreras <hidden> Date: 2021-07-08 17:33:30
Martin wrote:
On 08/07/2021 05:39, Felipe Contreras wrote:
quoted
Yeap, the tails of branch_1 and branch_2 could be literally anywhere.
That information is not recoverable from the current data structures of
git, thus the proposal to add a new one.
Ok, thanks for the all the explanation.
A word on the name "tail". IMHO really confusing. I get where it is
coming from.
But a lot of people will know head and tail utilities from their shell.
And "tail" is the one that shows lines on the end of the file to which
new data is added. Which is "head" in git.
Also a tail is something that follows, but (except for rebase), the base
point is fixed.
I think (despite my earlier comment) "base" is a better word.
It also goes along with "git rebase" which acts on the "base".
However wording around that topic probably still needs to be very careful.
"base" must be clearly distinguished from "start". Because "start" might
imply that only commits from here on forward are contained, but that
contradicts --contains which reports root to head.
I'm not really proposing such feature at this point. I did it on 2013
just to have a solution to this problem, but I didn't push for it back
then.
If I ever work on that feature again I will consider the name "base",
sure, but the only reason I mentioned this @{tail} concept is to try to
define in a more accurate way what a branch actually is.
> Suppose branch_2 was created this way:
>
> git switch --create branch_2 A
>
> Then commit B was created under branch_2. Then master was fast-forwarded
> to branch_2, so you have:
>
> A => B master
> ^ ^
> tail/branch_2 -+ +- head/branch_2
>
> Both branches have A, but only branch_2 has A as tail.
So base (tail) is the shared commit "A" on which branch_2 was created.
(rather than the first commit made in branch_2 which is "B")
I can see how that is needed for "git rebase" so @{base} can be used for
<upstream>.
Yes and no. <upstream> is where branch is rebased *to*, not where it's
rebased *from*:
git rebase --onto foo@{upstream} foo@{base} foo
This command rebases all the commits foo@{base}..foo on top of
foo@{upstream}.
Another way to think of it is that you'll cherry-pick foo@{base}..foo on
top of foo@{upstream}.
What happens if branch_2 is rebased?
Will the base be set to the commit onto which the branch was rebased?
A => B => C => D => E master
\ => F => G foo (base = B)
foo was created on B, then fast forwarded to C, then diverged.
git rebase --onto A foo@{base} foo
Now that foo diverges before B, having B as base for foo seems odd.
(Also A will have C' as child, So the base really is A now)
Yes, A is the new base.
git rebase --onto E foo@{base} foo
In this case C is already contained in master, so it will be skipped.
If the base is moved, then foo@{base}..foo will no longer contain C.
IMHO that is correct, because rebase skipped it.
The new base is E.
It's not complicated, the base is whatever --onto is.
Will there be a way to manually repoint the base?
A => B => C => D master
\ => E => F foo
\ => G => H bar (base = F)
If I do
git rebase --onto master bar@{base} bar
then the commits E and F will not be part of the rebase.
That is fine. I must handle them before.
But if I deleted foo (or for other reasons decide E and F should be
handled if I rebase bar) can I make them to be included?
Something like
git base --repoint B bar
I did not code that, but it's something people probably would need at
some point. I would do `git branch --set-base` though.
Anyway, it seems I wasn't very clear, I'm not really proposing this
feature. Although I think it's something that git is missing, it would
be a pain in the ass to attempt to get it merged, I have much more
important features I want to get done, and those don't have much chance
of being merged either.
The only reason I mentioned @{tail} (or @{base}) is to have a better
mental model of what a branch is.
1. A branch is whatever is inside `branch@{base}..branch`
2. `branch` is the branch head (`branch@{head}`), but it's not the
branch itself
For all intents and purposes on the git documentation the branch, the
branch name, and the branch head are used interchangeably, but
semantically speaking they are not the same thing.
When you change the branch head you are effectively changing the branch.
If @{base} existed, then changing the base would also change the branch
(although that would be a much less dangerous operation).
Does that make sense?
--
Felipe Contreras
The only reason I mentioned @{tail} (or @{base}) is to have a better
mental model of what a branch is.
1. A branch is whatever is inside `branch@{base}..branch`
For this part "branch" = some series of commits.
Then this is what I would say is a common misunderstanding.
Yet that may be the difference between what people want the branch to
be, and what it (afaik) technically is.
People indeed tend to thing, I branched at X, so anything before is not
part of the branch.
"--contains" says otherwise.
Thinking of it.
If I look at a feature branch, then my feature starts where I created
the branch. I want my feature branch to represent this.
But if I look at my local master branch (or any tracking branch), I like
to believe that it contains the same as the remote branch.
And well, if we just set the base for the local tracking branch to be
the same as the base for the remote branch that would be fine.
But if (after diverging, due to changes pulled from remote) then, I run
git rebase @{base} @{remote}
then rebase has to skip all the shared commits.
And since rebase also repoints the "base", my local branch then no
longer contains the same as the remote.
So limiting the branch to branch@{base}..branch only works for feature
branches.
So yes, what is a branch? More exactly what does it contain.
Two examples, that to me suggest two answers.
Also if branch@{base}..branch then there is a problem.
- branch@{base} is then correctly not part of the branch
- So immediately after "git switch -c branch" the branch is empty => ok
But if so, then what is the branch head at that time?
The Pointer would point the @{base}, but @base is outside the branch. So
the pointer of the branch points outside the branch?
2. `branch` is the branch head (`branch@{head}`), but it's not the
branch itself
Well technically "branch" is the "pointer" to the head.
Assuming we want "head" to be a commit?
Or do we want head, to be the "branch end" after the last commit? But
then still "branch is the pointer"
The only problem is:
branch is too often used for "the commits contained in the branch". That
is way to common to even try to stop it.
Yet, if branch is used for the content, then we do not have a good term
for the pointer.
For all intents and purposes on the git documentation the branch, the
branch name, and the branch head are used interchangeably, but
semantically speaking they are not the same thing.
I have not proof read all the docs for this....
But I think that "branch name" and "branch head" should or could be used
in a clear single meaning fashion each...
When you change the branch head you are effectively changing the branch.
Well if branch is the pointer, then you change the branch, and head is
being changed.
If branch is the content, then you change the head, and yes the content
changes.
From: Felipe Contreras <hidden> Date: 2021-07-08 20:38:01
Martin wrote:
On 08/07/2021 19:33, Felipe Contreras wrote:
quoted
The only reason I mentioned @{tail} (or @{base}) is to have a better
mental model of what a branch is.
1. A branch is whatever is inside `branch@{base}..branch`
For this part "branch" = some series of commits.
Then this is what I would say is a common misunderstanding.
Yet that may be the difference between what people want the branch to
be, and what it (afaik) technically is.
I'm not talking about what a branch technically is, I'm talking about
what it is semantically.
Technically a branch is a file with an object id in it. That doesn't
give the user any useful information.
What is important is the *meaning* of that file.
People indeed tend to thing, I branched at X, so anything before is not
part of the branch.
"--contains" says otherwise.
Yes, that is the status quo, but the fact that X is the case doesn't
mean it *should* be the case.
The ideal user interface doesn't need to be explained. The more you need
to explain a concept the less intuitive it is, and the more you should
look for another concept that is perhaps more intuitive.
A branch that you hold, or point to, is a concete concept easy to
underand. When I say: "me, my sister, and my father are one tiny branch
of the Contreras family", people understand what that means inuitively.
On the other hand saying "Felipe contains his great-great-grandfather"
would stop anyone on their tracks.
Thinking of it.
If I look at a feature branch, then my feature starts where I created
the branch. I want my feature branch to represent this.
But if I look at my local master branch (or any tracking branch), I like
to believe that it contains the same as the remote branch.
And well, if we just set the base for the local tracking branch to be
the same as the base for the remote branch that would be fine.
But if (after diverging, due to changes pulled from remote) then, I run
git rebase @{base} @{remote}
then rebase has to skip all the shared commits.
And since rebase also repoints the "base", my local branch then no
longer contains the same as the remote.
That is a *very* interesting case that exemplifies the lack of our
current semantic arsenal.
Every time you do a rebase you are in effect creating a new branch with
new commits, a new head, and a new base. The only thing that remains
the same is the name.
It is no longer the same as the remote branch, or an outgrowth; it's
a new branch.
If you send a pull request for your 'master' branch, which then gets
merged to 'origin/master', then you can do `git merge --ff-only` to
advance the head pointer of the 'master' branch to the remote branch so
both are in sync... Except the base won't be the same.
With the current semantics this recreated 'master' is now exactly the
same as the remote 'origin/master'. But not with the @{base} semantics;
since both branches have a different base, they are strictly speaking
different branchs.
But if you do `git reset --hard origin/master`, you are saying: drop
everything about this branch, and make it the same 'origin/master'.
*Now* we have a reason to distinguish `git merge --ff-only` from `git
reset --hard`.
So limiting the branch to branch@{base}..branch only works for feature
branches.
So yes, what is a branch? More exactly what does it contain.
Two examples, that to me suggest two answers.
Not necessarily. See above.
Also if branch@{base}..branch then there is a problem.
- branch@{base} is then correctly not part of the branch
- So immediately after "git switch -c branch" the branch is empty => ok
But if so, then what is the branch head at that time?
The Pointer would point the @{base}, but @base is outside the branch. So
the pointer of the branch points outside the branch?
Yes, the base pointer doesn't include the branch. When you do
`branch@{base}..branch` that's the same as `^branch@{base} branch` so that
excludes all the commits rechable from branch@{base} *including* that
commit iself.
quoted
2. `branch` is the branch head (`branch@{head}`), but it's not the
branch itself
Well technically "branch" is the "pointer" to the head.
Assuming we want "head" to be a commit?
No, the branch head is a reference: 'refs/heads/master'. The reference
points to a commit, but it's not the commit itself.
So it's a pointer to a pointer.
The only problem is:
branch is too often used for "the commits contained in the branch". That
is way to common to even try to stop it.
We don't need to stop it, we can sidestep it.
Instead of talking about the branch, talk about the branch head:
"the brach head is moved to X".
Or if you want to use the branch, don't assume any specifics:
"the branch is recreated to be the same as X".
quoted
When you change the branch head you are effectively changing the branch.
Well if branch is the pointer, then you change the branch, and head is
being changed.
If branch is the content, then you change the head, and yes the content
changes.
Exactly, so regardless of which semantics you choose, everyone
understands that the branch is not the same anymore.
--
Felipe Contreras
Technically a branch is a file with an object id in it. That doesn't
give the user any useful information.
What is important is the *meaning* of that file.
quoted
People indeed tend to thing, I branched at X, so anything before is not
part of the branch.
"--contains" says otherwise.
Yes, that is the status quo, but the fact that X is the case doesn't
mean it *should* be the case.
Well yes. So lets start over.
A branch is a container for commits. Those commits have a start (root or
base / not sure), and an end (head).
The commits are continuous, in that they have no gaps.
The big question is the start point of the branch.
And there is a further consequence:
If a branch "starts" at "base" then
--contains needs to be changed
--reachable needs to be added (for what contains does now)
This also complicates it, because now there are 3 types of relation
between commits and a branch
- unrelated (outside / not reachable)
- inside (base..head)
- reachable (base and all its parents) // better word needed
The last is important:
A => B => C master
\ => D foo
If I delete master, without the concept of reachable, I would expect
commit A to be dropped. Technically B should drop too, but it takes some
insight to expect that.
So then with only the branch foo left, I would also have only the commit
D (well maybe B too, if the system is lenient)
One might even go an say if master is deleted, then the base of foo is
deleted. since foo must have a base, and it no longer has, foo can not
exist any longer.
The problem here is that git permits to change history.
If branches could not be rewritten or deleted, then the "base" would be
a simple concept.
No branch would ever have to look what was before its base.
But as it stands, branches must reach to what was before their base.
A branch that you hold, or point to, is a concete concept easy to
underand. When I say: "me, my sister, and my father are one tiny branch
of the Contreras family", people understand what that means inuitively.
On the other hand saying "Felipe contains his great-great-grandfather"
would stop anyone on their tracks.
The Chicago branch of your family contains Al Capone.
That works.
Contains is also nice, because we have 2 boundaries (base/head) to
enclose the selection.
But if you do `git reset --hard origin/master`, you are saying: drop
everything about this branch, and make it the same 'origin/master'.
*Now* we have a reason to distinguish `git merge --ff-only` from `git
reset --hard`.
No you don't. IMHO not.
"reset --hard" resets the branch to a commit. You can specify that
commit by giving a branch-name (that then will be resolved). But it
could be any commit, even a detached one.
So "reset --hard" has to set the base and the head to the same commit.
Effectively creating an empty branch based at that commit.
But local tracking branches still are counter intuitive.
IMHO local tracking branches should follow one of the following
scenarios. (And ideally that should be the same for all local tracking
branches, for any user.)
1) Always have the same base as their remote branch.
Therefore always have the same content as the remote branch, up to where
they diverge, if they diverge.
2) Not include the remote branches content. Just hold my local commits,
until they will be pushed to the remote.
But neither works:
Say I have a local commit, and you pushed new changes to the remote.
git pull --rebase
My branch is rebased.
So my local tracking branch has its base at the head of the remote. It
has only local commits => case 1.
Say I have no local commits, and you pushed new changes to the remote.
git pull --ff-only
If I understand correct the --ff-only move the head of my local branch,
but leaves the base where it is.
Now I have some shared commits with the remote branch.
=> either case 2, or worse none of the 2 cases.
So, how should local tracking branches behave?
If you send a pull request for your 'master' branch, which then gets
merged to 'origin/master', then you can do `git merge --ff-only` to
advance the head pointer of the 'master' branch to the remote branch so
both are in sync... Except the base won't be the same.
There may be something I missed. ff should not touch the base?
So the 2 base will still be the same or not the same, depending on if
they were equal before the ff?
quoted
So yes, what is a branch? More exactly what does it contain.
Two examples, that to me suggest two answers.
Not necessarily. See above.
I feel we must have some understandingly on the part how base and local
branches would interact.
You agree: rebase changes the base (it creates a new branch on to --onto)
You pointed out there also is fast-forward. But see my above example.
I am not even doing a pull request. I simply go for you and I both can
push to the same remote. So we both commit to master and pull/push it.
quoted
Also if branch@{base}..branch then there is a problem.
- branch@{base} is then correctly not part of the branch
- So immediately after "git switch -c branch" the branch is empty => ok
But if so, then what is the branch head at that time?
The Pointer would point the @{base}, but @base is outside the branch. So
the pointer of the branch points outside the branch?
Yes, the base pointer doesn't include the branch. When you do
`branch@{base}..branch` that's the same as `^branch@{base} branch` so that
excludes all the commits rechable from branch@{base} *including* that
commit iself.
My question is, where you see the branch head pointing to?
If the branch is empty, i.e. if it has no commit at all, then to what
commit does the branch head point?
quoted
The only problem is:
branch is too often used for "the commits contained in the branch". That
is way to common to even try to stop it.
We don't need to stop it, we can sidestep it.
Instead of talking about the branch, talk about the branch head:
"the brach head is moved to X".
Yes well, we need to be very concise, if we speak about anything that is
not the "commits in the branch".
quoted
quoted
When you change the branch head you are effectively changing the branch.
Well if branch is the pointer, then you change the branch, and head is
being changed.
If branch is the content, then you change the head, and yes the content
changes.
Exactly, so regardless of which semantics you choose, everyone
understands that the branch is not the same anymore.
Your original text was
When you change the branch head you are effectively changing the branch.
If @{base} existed, then changing the base would also change the branch
(although that would be a much less dangerous operation).
Does that make sense?
And yes, if either boundary changes, the branch changed.
From: Felipe Contreras <hidden> Date: 2021-07-09 00:45:41
Martin wrote:
On 08/07/2021 22:37, Felipe Contreras wrote:
quoted
Technically a branch is a file with an object id in it. That doesn't
give the user any useful information.
What is important is the *meaning* of that file.
quoted
People indeed tend to thing, I branched at X, so anything before is not
part of the branch.
"--contains" says otherwise.
Yes, that is the status quo, but the fact that X is the case doesn't
mean it *should* be the case.
Well yes. So lets start over.
A branch is a container for commits. Those commits have a start (root or
base / not sure), and an end (head).
The commits are continuous, in that they have no gaps.
The big question is the start point of the branch.
And there is a further consequence:
If a branch "starts" at "base" then
--contains needs to be changed
--reachable needs to be added (for what contains does now)
Indeed, but as of this moment @{base} is not being considered, it's just
a mental model tool.
This also complicates it, because now there are 3 types of relation
between commits and a branch
- unrelated (outside / not reachable)
- inside (base..head)
- reachable (base and all its parents) // better word needed
I think that has always been the case. The fact that the git
documentation doesn't talk about that doesn't mean the concept doesn't
exist.
The last is important:
A => B => C master
\ => D foo
If I delete master, without the concept of reachable, I would expect
commit A to be dropped. Technically B should drop too, but it takes some
insight to expect that.
So then with only the branch foo left, I would also have only the commit
D (well maybe B too, if the system is lenient)
Commits don't need a branch to exist. B could have a tag 0.3.7 and no
branch pointing to it. There could be other refs pointing to that
commit.
One might even go an say if master is deleted, then the base of foo is
deleted. since foo must have a base, and it no longer has, foo can not
exist any longer.
Of course it can. The base of a branch doesn't necessarily need to be
part of any other branch.
Or another way to think of it is that B is part of an unnamed branch.
quoted
A branch that you hold, or point to, is a concete concept easy to
underand. When I say: "me, my sister, and my father are one tiny branch
of the Contreras family", people understand what that means inuitively.
On the other hand saying "Felipe contains his great-great-grandfather"
would stop anyone on their tracks.
The Chicago branch of your family contains Al Capone.
That works.
Sure, if you start from a certain grandparent, not if you start from my
grandfather.
Most humans have issue with more than 7 items. A branch containing
millions of members reaching as far back as a fish is a notion an
evolutionary biologist might not have any problem with, but most people
would struggle.
For most people a branch must start from somewhere.
quoted
But if you do `git reset --hard origin/master`, you are saying: drop
everything about this branch, and make it the same 'origin/master'.
*Now* we have a reason to distinguish `git merge --ff-only` from `git
reset --hard`.
No you don't. IMHO not.
"reset --hard" resets the branch to a commit. You can specify that
commit by giving a branch-name (that then will be resolved). But it
could be any commit, even a detached one.
OK. Sure. It could be repurposed to say what I explained, but we might
be overloading that command in that case.
How about `gt branch --reset <otherbranch>`?
So "reset --hard" has to set the base and the head to the same commit.
Effectively creating an empty branch based at that commit.
Maybe. Or maybe the base remains the same. Fortunately that's not
something we need concern ourselves with at this moment.
But local tracking branches still are counter intuitive.
IMHO local tracking branches should follow one of the following
scenarios. (And ideally that should be the same for all local tracking
branches, for any user.)
1) Always have the same base as their remote branch.
Therefore always have the same content as the remote branch, up to where
they diverge, if they diverge.
2) Not include the remote branches content. Just hold my local commits,
until they will be pushed to the remote.
But neither works:
Say I have a local commit, and you pushed new changes to the remote.
git pull --rebase
My branch is rebased.
So my local tracking branch has its base at the head of the remote. It
has only local commits => case 1.
Say I have no local commits, and you pushed new changes to the remote.
git pull --ff-only
If I understand correct the --ff-only move the head of my local branch,
but leaves the base where it is.
Now I have some shared commits with the remote branch.
=> either case 2, or worse none of the 2 cases.
There's no need for --ff-only, do `git pull --rebase` on both cases, and
the base will constantly be reset to the remote head.
However, at least I never do this. My 'master' branch doesn't contain
any commits and I always do the equivalent of `git pull --ff-only`, so
the base would never change.
quoted
If you send a pull request for your 'master' branch, which then gets
merged to 'origin/master', then you can do `git merge --ff-only` to
advance the head pointer of the 'master' branch to the remote branch so
both are in sync... Except the base won't be the same.
There may be something I missed. ff should not touch the base?
So the 2 base will still be the same or not the same, depending on if
they were equal before the ff?
That's right. Before the fast-forward the base was different (because of
the rebase), so after the fast-forward the base remains different.
quoted
quoted
So yes, what is a branch? More exactly what does it contain.
Two examples, that to me suggest two answers.
Not necessarily. See above.
I feel we must have some understandingly on the part how base and local
branches would interact.
You agree: rebase changes the base (it creates a new branch on to --onto)
You pointed out there also is fast-forward. But see my above example.
I am not even doing a pull request. I simply go for you and I both can
push to the same remote. So we both commit to master and pull/push it.
It doesn't matter who does the merge:
git merge origin/master
git push
It would be the same as a pull request followed by a fast-forward
(except with the parents reversed).
The base remains unmoved.
quoted
quoted
Also if branch@{base}..branch then there is a problem.
- branch@{base} is then correctly not part of the branch
- So immediately after "git switch -c branch" the branch is empty => ok
But if so, then what is the branch head at that time?
The Pointer would point the @{base}, but @base is outside the branch. So
the pointer of the branch points outside the branch?
Yes, the base pointer doesn't include the branch. When you do
`branch@{base}..branch` that's the same as `^branch@{base} branch` so that
excludes all the commits rechable from branch@{base} *including* that
commit iself.
My question is, where you see the branch head pointing to?
If the branch is empty, i.e. if it has no commit at all, then to what
commit does the branch head point?
To the same commit as the base: master..master contains zero commits.
quoted
quoted
The only problem is:
branch is too often used for "the commits contained in the branch". That
is way to common to even try to stop it.
We don't need to stop it, we can sidestep it.
Instead of talking about the branch, talk about the branch head:
"the brach head is moved to X".
Yes well, we need to be very concise, if we speak about anything that is
not the "commits in the branch".
quoted
quoted
quoted
When you change the branch head you are effectively changing the branch.
Well if branch is the pointer, then you change the branch, and head is
being changed.
If branch is the content, then you change the head, and yes the content
changes.
Exactly, so regardless of which semantics you choose, everyone
understands that the branch is not the same anymore.
Your original text was
quoted
When you change the branch head you are effectively changing the branch.
If @{base} existed, then changing the base would also change the branch
(although that would be a much less dangerous operation).
Does that make sense?
And yes, if either boundary changes, the branch changed.
But our immediate concern is to improve the documentation of
`git switch -C`, and perhaps improve the interface while we are at it.
I believe we have all the semantic tools needed to write something that
is understandable by most people regardless of their conception of what a
branch is.
No?
--
Felipe Contreras
I believe we have all the semantic tools needed to write something that
is understandable by most people regardless of their conception of what a
branch is.
While writing a mail on the origin topic (improve docs), I noticed that
the word "branch-ish" is still free.
Which would be anything that resolves to a "branch reference".
Currently this only is
- branch name.
- branchname@{upstream}
Btw, if branch-foo is tracking a local branch then
git checkout branch-foo@{upstream}
will switch the the tracked local branch.
* "branch-ish" could be defined as:
Anything that can be resolved to a branch-name.
A branch-name is a reference to the boundary that marks the end of a
branch.
A branch-ish can be given where a commit-ish is expected. In that case
it can be resolved to the last commit in the branch.
There may be further need to distinguish between local and remote.
For example
git checkout [<branch>]
When the <commit> argument is a branch name, the --detach option can be used to detach HEAD at the tip of the branch (git checkout <branch> would check out that branch without detaching HEAD).
Does not mention that it will also detach, if <branch> is the a remote
branch name
git checkout origin/master
I believe we have all the semantic tools needed to write something that
is understandable by most people regardless of their conception of what a
branch is.
So returning to the original topic.
While writing this, I thought maybe there is a need for a
"Guideline on writing documentation" ?
On 01/07/2021 16:58, Junio C Hamano proposed a patch that had an
interesting point.
The patch was for the docs of "git switch" and "git branch"
1)
<start-point> versus <commit[-ish]>
I am not sure that this will help much with the original issue, which is
my concern that a (new) user will be aware of why "switch -C" is a force
(i.e. what the dangers are).
But it is an interesting point.
From the synopsis of various commands (just a sample, I did not check all).
git switch (-c|-C) <new-branch> [<start-point>]
git branch <branchname> [<start-point>]
git checkout [--detach] <commit>
git checkout [[-b|-B|--orphan] <new_branch>] [<start_point>]
git reset [--soft | --mixed | --hard ] [<commit>]
With the exception for "git reset" they all use <start-point> when it
comes to branches.
The general question here is, should the synopsis say
a) this parameter should be a "commit".
And then the doc explains the commit will be used as startpoint
b) this parameter should be a "start point"
And then the doc explains the startpoint has to be given as commit.
In terms of checkout, this is especially interesting.
The 2nd form does create a new branch.
But both forms check-out the commit.
IMHO it is somewhat strange that you "check out a start-point to your
worktree".
So probably <commit> (or even <commit-ish>) may indeed be the better option.
This is however an issue that goes well beyond "git switch".
This may also affect other words used in synopsises. So this is a
general rule that needs to be decided for all of the documentation.
The issue is, that some commands take several commits.
git rebase [--onto <newbase>] [<upstream> [<branch>]]
In that case some distinguishing is needed.
There also is the option of "<base-commit[-ish]>".
This tells the user that a commit-ish is needed. But distinguishes it
from other <commit> that may be given as argument.
This may lead to rather long names (e.g. in rebase).
Though in checkout, I would use only <commit[-ish]> in both variants, as
the main action is to check out that commit.
2)
<branch> versus <branch-name>
git switch [--no-guess] <branch>
git switch (-c|-C) <new-branch> [<start-point>]
git branch <branchname> [<start-point>]
git checkout [[-b|-B|--orphan] <new_branch>] [<start_point>]
git rebase [--onto <newbase>] [<upstream> [<branch>]]
First of all "git rebase" is simply wrong. I can give a commit for all 3
arguments. So the last one does not have to be a branch. (or <branch-name>)
Then I think <branch-name> (or <branch-ish> /see other mail) should be
preferred over <branch>.
As for "git switch -C"
This should IMHO change to (the 2nd arg, actually depends on the point
"1" above)
git switch (-c|-C) <branch-name> [<base-commit>]
I suggest to not call it "new-branch-name" because, it might be an
existing name.
3)
newbbranch versus new-branch versus new_branch
That is something that just needs to be decided.
"new_branch" is in git checkout.
4)
Extend of explanation for why a command is classified as "force".
This one is the one I still lobby for support.
This is also on issue across all docs. (or most)
Currently "git switch -C" is simply stated to be --force-create.
- There is no mention what is "forced". All it says is:
if <new-branch> already exists, it will be reset to <start-point>.
I guess this is the English verb reset. Because, if the user goes to
"git reset" then the user would not know what kind of reset.
So the term "reset" is ambiguous, as it could be the verb, or the command.
Of course the "git branch" doc has the same
Reset <branchname> to <startpoint>, even if <branchname> exists already.
There is also no word, that this does not include overwriting a dirty
work tree.
git switch --force -c unused-name origin/branch
means "forcefully overwrite a dirty work tree"
git switch --force-create unused-name origin/branch
fails on the dirty work tree.
Btw similar on "git checkout"
git checkout -B unused-name origin/branch
Only difference, -B has no misleading long option.
But my point is less, the not applying danger.
My point is what danger is there, so that this was made a force command?
Look at
git checkout --force
--force
When switching branches, proceed even if the index or the working tree differs from HEAD. This is used to throw away local changes.
git switch --force
--force
An alias for --discard-changes.
and then eventually
This is used to throw away local changes.
So --force clearly says: You will loose local changes (if you have any).
The same clarity is missing for "force create branch".
Yes, sure any commits that where in the branch, may be hold by other
branches or the ref-log.
But neither is guaranteed. A branch does not need to have a reflog.
Even if we say a user must know about certain concepts (such as a
branchname is a reference, and non referenced objects may be lost), even
then the user is left to connect the dots themself.
I think it should be included in the docs (git switch/checkout/branch
and reset)
The current wording
Reset <branchname> to <startpoint>, even if
<branchname> exists already.
should be amended
Avoid "reset"
Create a new branch at <startpoint> with the name
<branchname>, even if <branchname> is already used.
Add clarity
Create a new branch at <startpoint> with the name
<branchname>.
If <branchname> already existed, then the old branch
will be removed.
If the user perceives "the old branch" as container for a "chain of
commits", then it is still up to the user to know, that any of those
commits can be part of other branches. And that "removing the branch",
may or may not include removing the commits.
However, a user not yet knowing what exactly "removing a branch" means,
does at least have the word "remove" to make him wary that they should
look up more details.
From: Felipe Contreras <hidden> Date: 2021-07-09 15:08:39
Martin wrote:
While writing a mail on the origin topic (improve docs), I noticed that
the word "branch-ish" is still free.
Which would be anything that resolves to a "branch reference".
Currently this only is
- branch name.
- branchname@{upstream}
Actually @ and HEAD too.
I don't particularly see much value in that definition since I always
use a committish when I write a branch name, and the fact that
`git switch` expects branches is one of the things that bothers me about
it.
Either way I don't think it makes much sense to do
`git switch branchnae@{upstream}`, and even less `git switch @`.
--
Felipe Contreras
and the fact that
`git switch` expects branches is one of the things that bothers me about
it.
Ah, good point.
I would word it differently though.
"git switch forces the use of --detach if switching to a non branch"
Bit of a twist.
It's a nice safety for beginners. I remember when I started, I kept
ending up detached. And I had no idea what to do next.
But once you are a bit more experienced the need to add that option can
be bothersome.
It's not common in my workflow, but I can see that it can be an issue.
So how to remedy?
- Drop the option / Make it default?
- add --allow-detach and git config switch.detach allow ?
I don't really have a preference.
I think its a nice protection, but even without it, the warning on
entering detached HEAD state is pretty good.
There is also a curious side effect.
If you went into detached, you can go back to attached using
git switch -
but not back to detached by again doing
git switch -
Even though you had been there, and that means you had used --detached,
and therefore known what you did.
From: Felipe Contreras <hidden> Date: 2021-07-09 16:10:14
Martin wrote:
On 09/07/2021 02:45, Felipe Contreras wrote:
quoted
I believe we have all the semantic tools needed to write something that
is understandable by most people regardless of their conception of what a
branch is.
On 01/07/2021 16:58, Junio C Hamano proposed a patch that had an
interesting point.
The patch was for the docs of "git switch" and "git branch"
1)
<start-point> versus <commit[-ish]>
I am not sure that this will help much with the original issue, which is
my concern that a (new) user will be aware of why "switch -C" is a force
(i.e. what the dangers are).
But it is an interesting point.
I don't think it's an improvement. What is that <commitish> used for?
That's what the user wants to know, not to mention that not any commit
works.
From the synopsis of various commands (just a sample, I did not check all).
git switch (-c|-C) <new-branch> [<start-point>]
git branch <branchname> [<start-point>]
git checkout [--detach] <commit>
git checkout [[-b|-B|--orphan] <new_branch>] [<start_point>]
git reset [--soft | --mixed | --hard ] [<commit>]
With the exception for "git reset" they all use <start-point> when it
comes to branches.
The general question here is, should the synopsis say
a) this parameter should be a "commit".
And then the doc explains the commit will be used as startpoint
I'd say no. I think it's pretty obvious what these commands accept as
input, what isn't clear is what that input is for.
b) this parameter should be a "start point"
And then the doc explains the startpoint has to be given as commit.
I don't see much value in explaining that has to be given as a commit.
How else would it be given as?
In terms of checkout, this is especially interesting.
The 2nd form does create a new branch.
You could say both forms create a new branch, except in the first form
the branch doesn't have a name.
But both forms check-out the commit.
IMHO it is somewhat strange that you "check out a start-point to your
worktree".
So probably <commit> (or even <commit-ish>) may indeed be the better option.
But we don't need all the commands to say the same thing, what we need
is something that's easy for the user to understand, and it's accurate.
This is however an issue that goes well beyond "git switch".
Indeed, but if history is an indication nothing will change (changes in
git's UI rarely do happen), so its better to minimize the possibility
that the patch will be ignored, or straight up rejected.
So it's better to stick with the experimental command and fix that
first.
This may also affect other words used in synopsises. So this is a
general rule that needs to be decided for all of the documentation.
The issue is, that some commands take several commits.
git rebase [--onto <newbase>] [<upstream> [<branch>]]
In that case some distinguishing is needed.
I'd say it shouldn't matter if it recevies one or several, what that
commit is used for is what matters.
2)
<branch> versus <branch-name>
git switch [--no-guess] <branch>
git switch (-c|-C) <new-branch> [<start-point>]
git branch <branchname> [<start-point>]
git checkout [[-b|-B|--orphan] <new_branch>] [<start_point>]
git rebase [--onto <newbase>] [<upstream> [<branch>]]
First of all "git rebase" is simply wrong. I can give a commit for all 3
arguments. So the last one does not have to be a branch. (or <branch-name>)
True. Although in most cases the last one would be a branch.
Then I think <branch-name> (or <branch-ish> /see other mail) should be
preferred over <branch>.
I don't think it makes a difference. A branch name is how you refer to a
branch (what else would be there?).
Differentiating the difference between a branch and a branch name was
done to write better sentences in the description of what the commands
do, but in the synopsis I don't see what we gain.
As for "git switch -C"
This should IMHO change to (the 2nd arg, actually depends on the point
"1" above)
git switch (-c|-C) <branch-name> [<base-commit>]
I suggest to not call it "new-branch-name" because, it might be an
existing name.
I think the name is all wrong. As Ævar pointed out --new (-n) is much
better. Also it doesn't make much sense to use "create" or "new" for
something that already exists.
I think you saw a correct issue: `git switch -C` might be used
incorrectly, but changing to the documentation would have limited value
(and only for the ones that read it).
I think if the branch already exists, the user has to be explicit to
what he wants to do and use `git switch --reset <branch> <commit>`
3)
newbbranch versus new-branch versus new_branch
That is something that just needs to be decided.
"new_branch" is in git checkout.
I'd rather have <branch>, but as I already said, the more ground you try
to cover the more impossible it will be to actually land the changes.
4)
Extend of explanation for why a command is classified as "force".
This one is the one I still lobby for support.
This is also on issue across all docs. (or most)
Currently "git switch -C" is simply stated to be --force-create.
- There is no mention what is "forced". All it says is:
quoted
if <new-branch> already exists, it will be reset to <start-point>.
I guess this is the English verb reset. Because, if the user goes to
"git reset" then the user would not know what kind of reset.
So the term "reset" is ambiguous, as it could be the verb, or the command.
Of course the "git branch" doc has the same
quoted
Reset <branchname> to <startpoint>, even if <branchname> exists already.
There is also no word, that this does not include overwriting a dirty
work tree.
git switch --force -c unused-name origin/branch
means "forcefully overwrite a dirty work tree"
git switch --force-create unused-name origin/branch
fails on the dirty work tree.
Btw similar on "git checkout"
git checkout -B unused-name origin/branch
Only difference, -B has no misleading long option.
But my point is less, the not applying danger.
My point is what danger is there, so that this was made a force command?
Look at
git checkout --force
quoted
--force
When switching branches, proceed even if the index or the working tree differs from HEAD. This is used to throw away local changes.
git switch --force
quoted
--force
An alias for --discard-changes.
and then eventually
quoted
This is used to throw away local changes.
So --force clearly says: You will loose local changes (if you have any).
The same clarity is missing for "force create branch".
Yes, sure any commits that where in the branch, may be hold by other
branches or the ref-log.
But neither is guaranteed. A branch does not need to have a reflog.
Even if we say a user must know about certain concepts (such as a
branchname is a reference, and non referenced objects may be lost), even
then the user is left to connect the dots themself.
I think it should be included in the docs (git switch/checkout/branch
and reset)
The current wording
Reset <branchname> to <startpoint>, even if
<branchname> exists already.
should be amended
Avoid "reset"
Create a new branch at <startpoint> with the name
<branchname>, even if <branchname> is already used.
Add clarity
Create a new branch at <startpoint> with the name
<branchname>.
If <branchname> already existed, then the old branch
will be removed.
If the user perceives "the old branch" as container for a "chain of
commits", then it is still up to the user to know, that any of those
commits can be part of other branches. And that "removing the branch",
may or may not include removing the commits.
However, a user not yet knowing what exactly "removing a branch" means,
does at least have the word "remove" to make him wary that they should
look up more details.
All these issues go away if we have:
git switch --reset <branch> <commit>
And instead of -C, we have:
git switch --new --reset <branch> <commit>
This creates a new branch if it doesn't exist, or if it exists resets
it.
Now the documentation writes itself.
Cheers.
--
Felipe Contreras
From: Felipe Contreras <hidden> Date: 2021-07-09 16:21:09
Martin wrote:
On 09/07/2021 17:08, Felipe Contreras wrote:
quoted
and the fact that
`git switch` expects branches is one of the things that bothers me about
it.
Ah, good point.
I would word it differently though.
"git switch forces the use of --detach if switching to a non branch"
Bit of a twist.
It's a nice safety for beginners. I remember when I started, I kept
ending up detached. And I had no idea what to do next.
Yes, and that's a good thing, but there's no need to cripple advaned
users.
But once you are a bit more experienced the need to add that option can
be bothersome.
It's not common in my workflow, but I can see that it can be an issue.
So how to remedy?
- Drop the option / Make it default?
No. As you noted it has value for beginners.
- add --allow-detach and git config switch.detach allow ?
That's a good option, but another one would be to have a core.advanced
mode, you turn it on if you are an advanced user.
I don't really have a preference.
I think its a nice protection, but even without it, the warning on
entering detached HEAD state is pretty good.
That warning olny appears with `git checkout`, not with
`git switch --detach`.
There is also a curious side effect.
If you went into detached, you can go back to attached using
git switch -
but not back to detached by again doing
git switch -
Even though you had been there, and that means you had used --detached,
and therefore known what you did.
From: Randall S. Becker <hidden> Date: 2021-07-09 16:38:26
On July 9, 2021 12:21 PM, Felipe Contreras wrote:
Martin wrote:
quoted
On 09/07/2021 17:08, Felipe Contreras wrote:
quoted
and the fact that
`git switch` expects branches is one of the things that bothers me
about it.
Ah, good point.
I would word it differently though.
"git switch forces the use of --detach if switching to a non branch"
Bit of a twist.
It's a nice safety for beginners. I remember when I started, I kept
ending up detached. And I had no idea what to do next.
Yes, and that's a good thing, but there's no need to cripple advaned users.
quoted
But once you are a bit more experienced the need to add that option
can be bothersome.
It's not common in my workflow, but I can see that it can be an issue.
So how to remedy?
- Drop the option / Make it default?
No. As you noted it has value for beginners.
quoted
- add --allow-detach and git config switch.detach allow ?
That's a good option, but another one would be to have a core.advanced mode, you turn it on if you are an advanced user.
quoted
I don't really have a preference.
I think its a nice protection, but even without it, the warning on
entering detached HEAD state is pretty good.
That warning olny appears with `git checkout`, not with `git switch --detach`.
quoted
There is also a curious side effect.
If you went into detached, you can go back to attached using
git switch -
but not back to detached by again doing
git switch -
Even though you had been there, and that means you had used
--detached, and therefore known what you did.
That's definitely a bug.
In all of this discussion, please be aware that many CI/CD systems use sparse checkout and detached heads as a matter of efficiency and certainty. Please ensure that you are not changing the semantics of existing capabilities when restricting what `git switch` will do. I am concerned about the 280,342 (as of this minute) current Jenkins users who depend on this.
Thanks,
Randall
As for "git switch -C"
This should IMHO change to (the 2nd arg, actually depends on the point
"1" above)
git switch (-c|-C) <branch-name> [<base-commit>]
I suggest to not call it "new-branch-name" because, it might be an
existing name.
I think the name is all wrong. As Ævar pointed out --new (-n) is much
better. Also it doesn't make much sense to use "create" or "new" for
something that already exists.
The n versus c issue is IMHO separate. Maybe tiny overlaps.
I see it mostly in the light of -c should be for "copy".
On "git checkout" it is "-b" for branch. That works, if you perceive
"branch" as a verb. "The action of branching creates a new branch".
If needs must, that would work as "git switch -b" to.
Actually, "new" or "create" would make sense in "git branch". But in git
switch, they actually raise the question "create what?" / "new what?".
I think you saw a correct issue: `git switch -C` might be used
incorrectly, but changing to the documentation would have limited value
(and only for the ones that read it).
I think if the branch already exists, the user has to be explicit to
what he wants to do and use `git switch --reset <branch> <commit>`
Well, that is the question as what the action is perceived.
I think the example is wrong, rather than the command.
-c / -C /-n / -N always *c*reate an *n*ew branch. (create and new really
are the same thing here)
But if the branch name Foo, is already used?
Well, it will still be a *new* branch being *created*.
To do that it has to remove the name from the old branch. (effectively
removing the old branch).
quoted
3)
newbbranch versus new-branch versus new_branch
That is something that just needs to be decided.
"new_branch" is in git checkout.
I'd rather have <branch>, but as I already said, the more ground you try
to cover the more impossible it will be to actually land the changes.
Well ok, if you shorten it to one word that solves it too.
But for anything that for some reason needs two words, IMHO there should
be one style. "one word", "-" or "_".
Currently different styles are mixed.
quoted
Look at
git checkout --force
quoted
--force
When switching branches, proceed even if the index or the working tree differs from HEAD. This is used to throw away local changes.
All these issues go away if we have:
git switch --reset <branch> <commit>
And instead of -C, we have:
git switch --new --reset <branch> <commit>
This creates a new branch if it doesn't exist, or if it exists resets
it.
Nope it does not go away.
All this has done, is that it no longer is a "force" command.
So the last bit of warning has just gone.
And it still needs to be documented inside the "git switch" doc, rather
than forwarding the user do yet another doc.
Also making the user read the "git reset" doc does not help, unless we
point out that this is a --hard reset, rather than "modifying the index".
I would on that account argue that "git reset --hard/mixed/soft" should
be "force" commands.
And the "git reset" documentation, as well as "git branch -f" / git
checkout -B", also miss the information why they are "force".
It is true, this information can be derived, if one
- knows the concepts (which one should do)
- and actually connects the dots (humans do have a tendency to overlook
things, especially if they are only indirectly referred to)
So, I still ask:
- If "--force" to overwrite the work tree can clearly state that change
to files will be "thrown away".
- Then why can "force" re-using an existing branch name not do the same?
And that is the same, never mind if we call it -C, -B or --reset.
From: Felipe Contreras <hidden> Date: 2021-07-09 17:10:39
Randall S. Becker wrote:
In all of this discussion, please be aware that many CI/CD systems use
sparse checkout and detached heads as a matter of efficiency and
certainty. Please ensure that you are not changing the semantics of
existing capabilities when restricting what `git switch` will do. I am
concerned about the 280,342 (as of this minute) current Jenkins users
who depend on this.
I doubt 280,342 Jenkins users depend on `git switch`.
And `git help switch`:
THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE.
--
Felipe Contreras
From: Felipe Contreras <hidden> Date: 2021-07-09 17:41:35
Martin wrote:
On 09/07/2021 18:10, Felipe Contreras wrote:
quoted
Martin wrote:
quoted
As for "git switch -C"
This should IMHO change to (the 2nd arg, actually depends on the point
"1" above)
git switch (-c|-C) <branch-name> [<base-commit>]
I suggest to not call it "new-branch-name" because, it might be an
existing name.
I think the name is all wrong. As Ævar pointed out --new (-n) is much
better. Also it doesn't make much sense to use "create" or "new" for
something that already exists.
The n versus c issue is IMHO separate. Maybe tiny overlaps.
I see it mostly in the light of -c should be for "copy".
On "git checkout" it is "-b" for branch. That works, if you perceive
"branch" as a verb. "The action of branching creates a new branch".
I generally view git commands as verbs. In this case "checkout" is the
verb, and "branch" is the direct object.
Actually, "new" or "create" would make sense in "git branch". But in git
switch, they actually raise the question "create what?" / "new what?".
`git switch` doesn't switch anything other branches. I don't think
`git switch-branch` would make the command somehow more understendale.
quoted
I think you saw a correct issue: `git switch -C` might be used
incorrectly, but changing to the documentation would have limited value
(and only for the ones that read it).
I think if the branch already exists, the user has to be explicit to
what he wants to do and use `git switch --reset <branch> <commit>`
Well, that is the question as what the action is perceived.
I think the example is wrong, rather than the command.
-c / -C /-n / -N always *c*reate an *n*ew branch. (create and new really
are the same thing here)
But if the branch name Foo, is already used?
Well, it will still be a *new* branch being *created*.
To do that it has to remove the name from the old branch. (effectively
removing the old branch).
But it's not removing the name, it's merely changing the head.
I don't particularly mind having -C or -N, I just would not use them
because I like to be explicit. I don't use --new for something that
already exists.
quoted
quoted
3)
newbbranch versus new-branch versus new_branch
That is something that just needs to be decided.
"new_branch" is in git checkout.
I'd rather have <branch>, but as I already said, the more ground you try
to cover the more impossible it will be to actually land the changes.
Well ok, if you shorten it to one word that solves it too.
But for anything that for some reason needs two words, IMHO there should
be one style. "one word", "-" or "_".
Currently different styles are mixed.
I don't see the need for that, <new branch> would do the trick, no need
for hyphens or underscores.
quoted
quoted
Look at
git checkout --force
quoted
--force
When switching branches, proceed even if the index or the working tree differs from HEAD. This is used to throw away local changes.
All these issues go away if we have:
git switch --reset <branch> <commit>
And instead of -C, we have:
git switch --new --reset <branch> <commit>
This creates a new branch if it doesn't exist, or if it exists resets
it.
Nope it does not go away.
All this has done, is that it no longer is a "force" command.
So the last bit of warning has just gone.
And it still needs to be documented inside the "git switch" doc, rather
than forwarding the user do yet another doc.
Yes, but as I said: the documentation writes itself.
-n <branch>, --new <branch>
Creates a new branch.
--reset <branch>
Resets the branch to <head>.
Also making the user read the "git reset" doc does not help, unless we
point out that this is a --hard reset, rather than "modifying the index".
Nobody is suggesting that. --reset refers to the English word "reset",
not `get reset`.
So, I still ask:
- If "--force" to overwrite the work tree can clearly state that change
to files will be "thrown away".
- Then why can "force" re-using an existing branch name not do the same?
Because we would be forcing two things now. I'd rather not overload
concepts.
Cheers.
--
Felipe Contreras
Well, that is the question as what the action is perceived.
I think the example is wrong, rather than the command.
-c / -C /-n / -N always *c*reate an *n*ew branch. (create and new really
are the same thing here)
But if the branch name Foo, is already used?
Well, it will still be a *new* branch being *created*.
To do that it has to remove the name from the old branch. (effectively
removing the old branch).
But it's not removing the name, it's merely changing the head.
I don't particularly mind having -C or -N, I just would not use them
because I like to be explicit. I don't use --new for something that
already exists.
But that comes down to the "what is a branch" discussion.
It is not creating a new branchname. But it is creating a new branch.
And then the branchname refers to that new branch.
It changes head, base, and the entire content. That effectively makes it
new.
If you have a 10 year old car that you nicknamed "speedy", and I come
along and I replace every part (every screw, every whatever...) with a
brand new part, would you still call the result a 10 year old car (even
if (or just because) you still use the nickname) ?
Using "reset", it's similar. Except that human language is slopy.
If I play WOW, and I reset the game. Actually that is already wrong. I
do not reset the game. It is still the same code, the same images.... I
do reset my session or status. And after that, I will be in a new
session, or have a new status.
- "creating" the branch is "setting (up) the branch"
- "re-setting" is doing doing this (creation) again.
quoted
Nope it does not go away.
All this has done, is that it no longer is a "force" command.
So the last bit of warning has just gone.
And it still needs to be documented inside the "git switch" doc, rather
than forwarding the user do yet another doc.
Yes, but as I said: the documentation writes itself.
-n <branch>, --new <branch>
Creates a new branch.
--reset <branch>
Resets the branch to <head>.
And that still leaves it to the user to connect the dots, and come to
the conclusion that the old branch is no longer holding his valued commits.
We don't ask the user to go make this sort of "connecting the dots",
when he uses force to override changes in his worktree.
Why?
quoted
So, I still ask:
- If "--force" to overwrite the work tree can clearly state that change
to files will be "thrown away".
- Then why can "force" re-using an existing branch name not do the same?
Because we would be forcing two things now.
Which 2 things?
The worktree overwriting is *not* forced by -C
git switch -C b1 b2
git checkout -B b1 b2
both give an error if the worktree has changed files.
This is only about what happens to the branch.
I.e we force the branchname to point to our new branch.
And that means the branchname no longe points to the old branch, and the
old branch therefore is removed.
I'd rather not overload
concepts.
Sorry the concepts are there by whatever the implementation does.
Documenting them does not overload concepts. If they indeed already are
overloaded, then documentation does not change that.
Btw, not sure what is overloaded here?
From: Felipe Contreras <hidden> Date: 2021-07-10 19:45:28
Martin wrote:
On 09/07/2021 19:41, Felipe Contreras wrote:
quoted
Martin wrote:
quoted
Well, that is the question as what the action is perceived.
I think the example is wrong, rather than the command.
-c / -C /-n / -N always *c*reate an *n*ew branch. (create and new really
are the same thing here)
But if the branch name Foo, is already used?
Well, it will still be a *new* branch being *created*.
To do that it has to remove the name from the old branch. (effectively
removing the old branch).
But it's not removing the name, it's merely changing the head.
I don't particularly mind having -C or -N, I just would not use them
because I like to be explicit. I don't use --new for something that
already exists.
But that comes down to the "what is a branch" discussion.
It is not creating a new branchname. But it is creating a new branch.
And then the branchname refers to that new branch.
It changes head, base, and the entire content. That effectively makes it
new.
Yes, it is a new branch, but the name doesn't change.
If you have a 10 year old car that you nicknamed "speedy", and I come
along and I replace every part (every screw, every whatever...) with a
brand new part, would you still call the result a 10 year old car (even
if (or just because) you still use the nickname) ?
Yeah but you are entering into metaphysics of identity, see the Ship of
Theseus [1]. By that same logic why are you still called Martin if every
cell in your body wasn't there when you were originally born?
These thought experiments are interesting, but philosphers have discused
about this for thousands of years and the conclussion is still
undecided, so I don't think we'll come to a conclussion here.
Moreover, I don't even think it's relevant. We agree that the branch is
a different branch, we agree that the name doesn't change, and we agree
that the user doesn't want the name to change. We don't need to enter
into a philosophical discussion to see if the name *should* change.
Using "reset", it's similar. Except that human language is slopy.
If I play WOW, and I reset the game. Actually that is already wrong. I
do not reset the game. It is still the same code, the same images.... I
do reset my session or status. And after that, I will be in a new
session, or have a new status.
Words mean whatever humans using those words intend them to mean. If
most people use the word "reset" in a certin way, that's what the word
means. Even if you have a good ontological reason why reset shouldn't be
used like that, it's used like that.
- "creating" the branch is "setting (up) the branch"
- "re-setting" is doing doing this (creation) again.
Resetting is not necesarilly creating again, it can mean setting up
again.
quoted
quoted
Nope it does not go away.
All this has done, is that it no longer is a "force" command.
So the last bit of warning has just gone.
And it still needs to be documented inside the "git switch" doc, rather
than forwarding the user do yet another doc.
Yes, but as I said: the documentation writes itself.
-n <branch>, --new <branch>
Creates a new branch.
--reset <branch>
Resets the branch to <head>.
And that still leaves it to the user to connect the dots, and come to
the conclusion that the old branch is no longer holding his valued commits.
No. You can add all the explanation you want after "Resets the branch to
<head>.", but most of that explanation would be redundant, because as we
already agreed, there's no way to reset the head of a branch without
changing the branch.
quoted
quoted
So, I still ask:
- If "--force" to overwrite the work tree can clearly state that change
to files will be "thrown away".
- Then why can "force" re-using an existing branch name not do the same?
Because we would be forcing two things now.
Which 2 things?
The worktree overwriting is *not* forced by -C
git switch -C b1 b2
git checkout -B b1 b2
both give an error if the worktree has changed files.
This is only about what happens to the branch.
I.e we force the branchname to point to our new branch.
And that means the branchname no longe points to the old branch, and the
old branch therefore is removed.
It seems your proposal is to make `git switch -c --force b1 b2` be the same as
`git switch -C b1 b2`, but that would also make it the same as
`git switch -C --force b1 b2`. Therefore it would be forcing two things.
Or is your proposal something else?
[1] https://en.wikipedia.org/wiki/Ship_of_Theseus
--
Felipe Contreras
Martin wrote:
No. You can add all the explanation you want after "Resets the branch to
<head>.", but most of that explanation would be redundant, because as we
already agreed, there's no way to reset the head of a branch without
changing the branch.
By that logic a lot of explanations are redundant, because on some
lever, if every user thinks far enough lots of things can be concluded.
From the docs (and similar on git checkout)
--force
An alias for --discard-changes.
--discard-changes
Proceed even if the index or the working tree differs from HEAD.
Both the index and working tree are restored to match the switching
target. If --recurse-submodules is specified, submodule content is
also restored to match the switching target. This is used to throw
away local changes.
If the working tree is made to match the target, then it can not retain
local changes. That can be concluded.
Yet, it is explicitly mentioned.
Does it really hurt to mention it?
People overlook details that to others are blaring obvious.
I agree, we can not mention every potential possibility. But as a
general rule, if data could be lost, then a mention (an explicit
mention) should be made.
Yes, commits may be hold by the reflog. Except the reflog is optional.
And more to the point, the reflog is unknown to plenty of people (never
mind if they should know it, they do not) So the possibility of loss is
rather real.
But anyway.
I brought forward my idea. I explained my reasoning.
If it (this part) is downvoted/rejected then that it how it is.
There still is the idea to replace the word "branch" by "branch name" in
some parts of the git switch documentation.
quoted
quoted
quoted
So, I still ask:
- If "--force" to overwrite the work tree can clearly state that change
to files will be "thrown away".
- Then why can "force" re-using an existing branch name not do the same?
Because we would be forcing two things now.
Which 2 things?
The worktree overwriting is *not* forced by -C
git switch -C b1 b2
git checkout -B b1 b2
both give an error if the worktree has changed files.
This is only about what happens to the branch.
I.e we force the branchname to point to our new branch.
And that means the branchname no longe points to the old branch, and the
old branch therefore is removed.
It seems your proposal is to make `git switch -c --force b1 b2` be the same as
`git switch -C b1 b2`, but that would also make it the same as
`git switch -C --force b1 b2`. Therefore it would be forcing two things.
Or is your proposal something else?
No. I definitely want to keep those 2 apart from each other.
For each force-needing action, you should have to specify it's own force
flag.
I do not want to change the behaviour on that part.
I only compared the
- doc of "-f" for worktree overwrites
with the
- doc -C for branch overwrites.
And I found that the former makes explicit mention of what can be lost,
the latter leaves it to be concluded.
From: Felipe Contreras <hidden> Date: 2021-07-10 20:49:47
Martin wrote:
On 10/07/2021 21:45, Felipe Contreras wrote:
quoted
Martin wrote:
No. You can add all the explanation you want after "Resets the branch to
<head>.", but most of that explanation would be redundant, because as we
already agreed, there's no way to reset the head of a branch without
changing the branch.
By that logic a lot of explanations are redundant, because on some
lever, if every user thinks far enough lots of things can be concluded.
Yes. And that's what a good writer aims for: to minimize the number of
words needed for the vast majority of readers to understand the point.
The more work you as a writer put into a sentence, the less work
hundreds or thousands of readers have to do while reading that sentence.
Rendundancy is only good when you are trying to reach a certain
word count for a university essay.
From the docs (and similar on git checkout)
quoted
--force
An alias for --discard-changes.
--discard-changes
Proceed even if the index or the working tree differs from HEAD.
Both the index and working tree are restored to match the switching
target. If --recurse-submodules is specified, submodule content is
also restored to match the switching target. This is used to throw
away local changes.
There's no adjective I can use for the official git documentation that
isn't crass, so let's just say that I find it extremelly lacking.
That paragraph above is a great example: it's a) hard to read, b)
unecessarily verbose, c) is wrongly ordered, d) redundant, and e) not
even correct.
If the working tree is made to match the target, then it can not retain
local changes. That can be concluded.
Yet, it is explicitly mentioned.
Does it really hurt to mention it?
Yes it does.
Time is the most precious resource we all have. We should not waste the
most precious resource of our readers.
Throw away local changes.
That does a much better job.
If you want to be more explicit, you can add a bit more information:
Throw away local changes either in the staging area or the working
tree.
Why does the user have to know what HEAD is? And why does it matter that
the staging area is held in a file called "index"?
The current explanation is just bad.
But as I said, if you want to replicate the current style of the
documentation, go ahead, but it would be pretty much a bloated version
of "resets the branch to <head>".
But anyway.
I brought forward my idea. I explained my reasoning.
If it (this part) is downvoted/rejected then that it how it is.
It's not a matter of consensus. There are proposals where literally
everyone is in favor, and yet they are never merged.
There's only one person you need to convince.
So, what I suggest you to do is take into consideration all we have
discussed and send another patch, because that's ultimately all that
matters. Moreover, it usually happens to me that while I write the patch
is when finally the previously-discussed ideas start to click.
quoted
quoted
quoted
quoted
So, I still ask:
- If "--force" to overwrite the work tree can clearly state that change
to files will be "thrown away".
- Then why can "force" re-using an existing branch name not do the same?
Because we would be forcing two things now.
Which 2 things?
The worktree overwriting is *not* forced by -C
git switch -C b1 b2
git checkout -B b1 b2
both give an error if the worktree has changed files.
This is only about what happens to the branch.
I.e we force the branchname to point to our new branch.
And that means the branchname no longe points to the old branch, and the
old branch therefore is removed.
It seems your proposal is to make `git switch -c --force b1 b2` be the same as
`git switch -C b1 b2`, but that would also make it the same as
`git switch -C --force b1 b2`. Therefore it would be forcing two things.
Or is your proposal something else?
No. I definitely want to keep those 2 apart from each other.
For each force-needing action, you should have to specify it's own force
flag.
OK, but I don't see the concrete proposal. What would be the flag that
makes -c "forceful"?
Cheers.
--
Felipe Contreras
For each force-needing action, you should have to specify it's own force
flag. >
> OK, but I don't see the concrete proposal. What would be the
> flag that
> makes -c "forceful"?
>
Well that starts yet another topic.
At the moment, it is
--force-create which is absorbing the flag into the option.
And by (apparent) convention it also is the uppercasing of the option
-C
same as the uppercasing of the -B in checkout.
I am not really sure if the uppercasing is the best idea.
If your suggestion "core.advanced " were to come, I would vote that
uppercase single letter force options should be restricted to advanced.
If -n is introduced, we can think about what to do about -N.
Should the --force-* style be kept?
--force-new
-N
Or the (unfortunate? / see below ) "--discard-changes" style:
--discard-existing-branch -n <branchname>
I am against using --reset instead of --force-new.
At least I can say, if I use "-N", I want a *new* branch. I don't care
about any old branch under that name.
Also "--reset" does not have the same alerting properties to me, as
"force" or "discard" have.
This may be my English, but to me "reset" does not have the same
alerting property.
The general problem is, if there is more than one force-needing action,
then which one does -f act on?
Any force-needing action, that only applies with another option (such as
-N) can have a --force-*. So the plain -f is not used for it.
But, what if more than one force-needing event can happen (not just
switch, but any command), even without any extra options? (May not yet
be the case / not checked).
git switch has attempted to solve that.
The result IMHO is a disaster.
"-f" / "--force" is made an alias in favour for
--discard-changes
What changes?
--force
An alias for --discard-changes.
--discard-changes
Proceed even if the index or the working tree differs from HEAD.
Both the index and working tree are restored to match the switching
target. If --recurse-submodules is specified, submodule content is
also restored to match the switching target. This is used to throw
away local changes.
....
quoted
If the working tree is made to match the target, then it can not retain
local changes. That can be concluded.
Yet, it is explicitly mentioned.
Does it really hurt to mention it?
Yes it does.
Time is the most precious resource we all have. We should not waste the
most precious resource of our readers.
Throw away local changes.
That does a much better job.
If you want to be more explicit, you can add a bit more information:
Throw away local changes either in the staging area or the working
tree.
Why does the user have to know what HEAD is? And why does it matter that
the staging area is held in a file called "index"?
The current explanation is just bad.
Time is precious, but to really save on it, you have to invest some of it.
About the HEAD/index stuff => that was not at all related to the point I
was making.
But I agree that bit can be shortened
The thing that I was pointing out, is the last sentence only.
> This is used to throw away local changes.
But even that can be reduced to your proposal
> Throw away local changes.
It still supports my point. It does state explicitly that data is (or
can be) thrown away.
Now, if that can be stated on this option, then all I ask is to add a
similar statement (as short as possible) to "-C".
It should indicate that *commit* may be *dropped".
Find a better word for dropped: lost, unreachable, removed.....
Currently only the branch is mentioned.
Currently nothing does explicitly say that *commits* can be affected.
At the end of the current or rewritten "-C" doc, add:
> This can drop commits
4 words. All that is needed.
There's only one person you need to convince.
So, what I suggest you to do is take into consideration all we have
discussed and send another patch, because that's ultimately all that
matters. Moreover, it usually happens to me that while I write the patch
is when finally the previously-discussed ideas start to click.
Well, I will see to make some time and put something together.
Might be a bit before I get to it, but that gives some time to think about.
From: Felipe Contreras <hidden> Date: 2021-07-10 23:18:52
Martin wrote:
On 10/07/2021 22:49, Felipe Contreras wrote:
quoted
Martin wrote:
quoted
For each force-needing action, you should have to specify it's own force
flag. >
> OK, but I don't see the concrete proposal. What would be the
> flag that
> makes -c "forceful"?
>
Well that starts yet another topic.
At the moment, it is
--force-create which is absorbing the flag into the option.
And by (apparent) convention it also is the uppercasing of the option
-C
same as the uppercasing of the -B in checkout.
I am not really sure if the uppercasing is the best idea.
Me neither, and it's not something I generally use.
If your suggestion "core.advanced " were to come, I would vote that
uppercase single letter force options should be restricted to advanced.
I would not count on it. I suggested core.mode back in 2013 [1], so...
If -n is introduced, we can think about what to do about -N.
Should the --force-* style be kept?
--force-new
-N
Or the (unfortunate? / see below ) "--discard-changes" style:
--discard-existing-branch -n <branchname>
I am against using --reset instead of --force-new.
That's OK, and in fact I can see how '--reset --new' is clunky, I'm just
saying it is a possibility. But the main point is that something like
`git switch --reset` is missing, although `git switch --move` would
probably do the trick.
At least I can say, if I use "-N", I want a *new* branch. I don't care
about any old branch under that name.
Right, I would as well, but in fact I would expect the same from -n
(although I can see how a newbie might not).
Also "--reset" does not have the same alerting properties to me, as
"force" or "discard" have.
This may be my English, but to me "reset" does not have the same
alerting property.
OK, maybe it's a language issue. I'm not a native English speaker, my
mother tongue is Spanish, but I'm pretty sure my understanding of
"reset" is what most people understand: set again.
Using Merriam Webster [2]:
1: to set again or anew
2: to change the reading of often to zero
And there's plenty of corroboration; reorder: order again (whatever
order you had is lost), reassign: assign again (whatever assignment you
had is gone), replay: play again (whatever you were playing is gone),
and so on.
The general problem is, if there is more than one force-needing action,
then which one does -f act on?
Any force-needing action, that only applies with another option (such as
-N) can have a --force-*. So the plain -f is not used for it.
But, what if more than one force-needing event can happen (not just
switch, but any command), even without any extra options? (May not yet
be the case / not checked).
git switch has attempted to solve that.
The result IMHO is a disaster.
"-f" / "--force" is made an alias in favour for
--discard-changes
What changes?
From: Felipe Contreras <hidden> Date: 2021-07-10 23:35:24
Martin wrote:
On 10/07/2021 22:49, Felipe Contreras wrote:
quoted
Martin wrote:
quoted
From the docs (and similar on git checkout)
quoted
--force
An alias for --discard-changes.
--discard-changes
Proceed even if the index or the working tree differs from HEAD.
Both the index and working tree are restored to match the switching
target. If --recurse-submodules is specified, submodule content is
also restored to match the switching target. This is used to throw
away local changes.
....
quoted
quoted
If the working tree is made to match the target, then it can not retain
local changes. That can be concluded.
Yet, it is explicitly mentioned.
Does it really hurt to mention it?
Yes it does.
Time is the most precious resource we all have. We should not waste the
most precious resource of our readers.
Throw away local changes.
That does a much better job.
If you want to be more explicit, you can add a bit more information:
Throw away local changes either in the staging area or the working
tree.
Why does the user have to know what HEAD is? And why does it matter that
the staging area is held in a file called "index"?
The current explanation is just bad.
Time is precious, but to really save on it, you have to invest some of it.
Sure.
About the HEAD/index stuff => that was not at all related to the point I
was making.
But I agree that bit can be shortened
The thing that I was pointing out, is the last sentence only.
> This is used to throw away local changes.
But even that can be reduced to your proposal
> Throw away local changes.
It still supports my point. It does state explicitly that data is (or
can be) thrown away.
OK, yeah, it does state explicitly that data is thrown away, but it's
the *last* sentence, when it should be the first, and everything else is
redundant.
Now, if that can be stated on this option, then all I ask is to add a
similar statement (as short as possible) to "-C".
It should indicate that *commit* may be *dropped".
Find a better word for dropped: lost, unreachable, removed.....
Currently only the branch is mentioned.
Currently nothing does explicitly say that *commits* can be affected.
At the end of the current or rewritten "-C" doc, add:
> This can drop commits
4 words. All that is needed.
OK. I'm not opposed to that, that would definitely be an improvement
from the current text.
What I'm saying is that if we are trying to improve the text, it would
behoove us to consider all other options, and instead if adding a note
at the end (which is correct), reconsider the whole thing to *start*
with what's important:
Instead of this:
-C <new-branch>::
--force-create <new-branch>::
Similar to `--create` except that if `<new-branch>` already
exists, it will be reset to `<start-point>`. This is a
convenient shortcut for:
+
------------
$ git branch -f <new-branch>
$ git switch <new-branch>
------------
Do this:
-N <branch>::
Create a new branch like '--new', but if it already exists reset it
like '--reset'.
I don't know how is that unclear in any way.
quoted
There's only one person you need to convince.
So, what I suggest you to do is take into consideration all we have
discussed and send another patch, because that's ultimately all that
matters. Moreover, it usually happens to me that while I write the patch
is when finally the previously-discussed ideas start to click.
Well, I will see to make some time and put something together.
Might be a bit before I get to it, but that gives some time to think about.
Take your time. One of the good things about open source is that there's
no rush.
Cheers.
--
Felipe Contreras
That's OK, and in fact I can see how '--reset --new' is clunky, I'm just
saying it is a possibility. But the main point is that something like
`git switch --reset` is missing, although `git switch --move` would
probably do the trick.
How would "git switch --reset" be different from "git switch -N" ?
"--move" is problematic.
- it reminds me of moving the commits. I.e. rebase.
- it actually stands for "rename" (in git branch -m).
The apparent idea: "move the branch under a new name"
But the branch (base..head) itself stays where it is.
"rename" would be so much more intuitive to me.
quoted
At least I can say, if I use "-N", I want a *new* branch. I don't care
about any old branch under that name.
Right, I would as well, but in fact I would expect the same from -n
(although I can see how a newbie might not).
That's why we have to keep in mind that -N is really --force-new.
A non-force option should not lead to data loss.
Or if something can be lost, then "force" needs to be used.
If the branch-name already points to a branch then of that branch, you
stand to loose:
- the branch boundaries (base..head)
- in some cases, (some of) the commits hold by it.
So by convention a simple "-n" is protecting me from that.
IMHO that should be expected.
quoted
Also "--reset" does not have the same alerting properties to me, as
"force" or "discard" have.
This may be my English, but to me "reset" does not have the same
alerting property.
OK, maybe it's a language issue. I'm not a native English speaker, my
mother tongue is Spanish, but I'm pretty sure my understanding of
"reset" is what most people understand: set again.
I am German. And yes "set again" (sometimes "restart", but that does not
matter here)
If a branch is set, as base and head. Then "reset" means to set those
two again.
"set again" => They will still be there.
(changed indeed, but there)
The commits hold by that branch, are not "set again".
They may become unreachable.
The word "reset" gives no indication on knock on effects.
However, I prefer if those effects are made clear.
> meriam webster
Quite some of the examples are "put back into working order"
(broken leg / circuit breaker => reset does not loose anything)
Others are restart (at zero) "reset an odometer".
To me personally the emphasis is the "start again", the loss of the
previous value is a side effect.
Maybe others will take see "loss" part as more prominent.
The question then is, how many might not be that wary of the potential loss?
So *if* --force was not an alias for --discard-changes, then this would
make sense:
git switch --new --force topic
It would _force_ the creation of a _new_ branch called "topic".
Is this close to what you are thinking?
No, again no. I said "I want them to be separate force flags"
As long as we have the unspecific "--force" this must be limited to
event *not* triggered by added options.
That is
git switch foo
is not forceful, therefore not allowed to destroy data.
Hence it can not overwrite local changes.
So --force applies to that.
Now if you were currently detached, and made new commits while detached,
then
git switch foo
would loose those commits.
Hypothetical, that could require force.
And like the first example it is part of the default behaviour. No
options are given.
But then using the same --force to force something else would be bad.
So then we need --force-discard-local-changed and
--force-unlink-detached-commit
git -n newbranch commit
is not default behaviour. It is triggered by the -n option.
If this endangers any data (other than what is covered by default
cases), then this always needs its own force.
And it has --force-new
It is possible, but I dislike it very much to define that
--force affects the next option that follows.
So that, thin is -N
git switch --force --new
But those are not
git switch --new --force
git switch --force - --new // the single dash separates the force
I do not like that idea...
At the end of the current or rewritten "-C" doc, add:
> This can drop commits
4 words. All that is needed.
OK. I'm not opposed to that, that would definitely be an improvement
from the current text.
What I'm saying is that if we are trying to improve the text, it would
behoove us to consider all other options, and instead if adding a note
at the end (which is correct), reconsider the whole thing to *start*
with what's important:
Ah, ok. So we have been missing each others point.
Instead of this:
-C <new-branch>::
--force-create <new-branch>::
Similar to `--create` except that if `<new-branch>` already
exists, it will be reset to `<start-point>`. This is a
convenient shortcut for:
+
------------
$ git branch -f <new-branch>
$ git switch <new-branch>
------------
Do this:
-N <branch>::
Create a new branch like '--new', but if it already exists reset it
like '--reset'.
As I said, I try to avoid reset, and also there is no "--reset" to
match. Only a "reset" command, and it does a wide range of diff things
-force-new <branch-name> <commit>
-N <branch-name> <commit>
See the --new option.
Allows to [re-]use the name of an existing branch.
This may drop commits of that branch.
Or
See the --new option.
Can use the name of an existing branch.
Removing that branch may drop commits.
If needs must
"Removing" => "Resetting"
Or even shorter
See the --new option.
Allows to re-use a branch-name and may drop commits
[resetting it].
From: Felipe Contreras <hidden> Date: 2021-07-12 16:15:30
Martin wrote:
On 11/07/2021 01:18, Felipe Contreras wrote:
quoted
quoted
Also "--reset" does not have the same alerting properties to me, as
"force" or "discard" have.
This may be my English, but to me "reset" does not have the same
alerting property.
OK, maybe it's a language issue. I'm not a native English speaker, my
mother tongue is Spanish, but I'm pretty sure my understanding of
"reset" is what most people understand: set again.
I am German. And yes "set again" (sometimes "restart", but that does not
matter here)
If a branch is set, as base and head. Then "reset" means to set those
two again.
"set again" => They will still be there.
(changed indeed, but there)
The commits hold by that branch, are not "set again".
They may become unreachable.
The word "reset" gives no indication on knock on effects.
However, I prefer if those effects are made clear.
I gave plenty of examples where "reset" implies the previous state is
gone after it.
--
Felipe Contreras
Do this:
-N <branch>::
Create a new branch like '--new', but if it already exists reset it
like '--reset'.
quoted
Or even shorter
See the --new option.
Allows to re-use a branch-name and may drop commits
[resetting it].
Yes, it is shorter, but now it doesn't even say what it does.
Ok instead of " see the --new option"
use "Same as the --new option, but allows...."
Yeah, that explains more, but what happend when you use a branch name
that already exists? Still not explained.
I have to look back in the mails.
There was a lot about getting it shorter, I am happy with a verbose
version too.
Taking a step back.
-c <new-branch>
--create <new-branch>
Create a new branch named <new-branch> starting at
<start-point> before switching to the branch.
This is a convenient shortcut for:
Should that actually say, that it will fail if the branch-name is
already taken?
IMHO yes.
The "-C" option could then be (incorporating the "could be lost" from a
prior mail.
> -C <new-branch> <commit>
> Same the --new option.
> But allows to use an existing branch-name. The
> [existing|old] branch [for the name] will be removed, and
> its commits could be lost.
If using "existing" or "old" then "for the name" is *not* needed, and
vice versa.
And, yes they can be lost. They can be found again, if one knows where
to look.
From: Felipe Contreras <hidden> Date: 2021-07-12 19:08:10
Martin wrote:
On 12/07/2021 18:58, Felipe Contreras wrote:
The "-C" option could then be (incorporating the "could be lost" from a
prior mail.
> -C <new-branch> <commit>
> Same the --new option.
But it's not the same as --new.
> But allows to use an existing branch-name.
If this is an essential part of the previous sentence, it should be part
of the sentence:.
Same [as] the --new option, but allows to use an existing branch name.
But this is wasted space:
Shouting is the same as talking, but with a different volume.
There's no need for another sentence explaining in what way it is
different (higher volume), do it in the same sentence.
What happens when we use an existing branch name?
> The [existing|old] branch [for the name] will be removed,
Except this is a lie. At no point is the branch removed; the branch name
is never gone, neither are the commits.
What is actually happening is that the branch head is changed. That is
all. And as I already explained in the subthread, everyone understands
what changing the branch head does to the branch.
Everyone knows what happens when you reset your computer without saving
your Excel spreadsheet. The word "reset" implies loosing state.
If you don't want to use the word "reset", or the term "branch head",
then you can say:
Same as --new, but if the branch already exists it's replaced.
This *still* doesn't explain what it is doing, you would need to read
--new.
Create a new branch like '--new', but if the branch already exists
it's replaced.
Now it is actually self-contained.
quoted
and its commits could be lost.
The commits are not lost, they are just not part of this branch anymore.
They could easily be part of another branch already.
Create a new branch like '--new', but if the branch already exists
it's replaced. The commits that are initially part of the branch might
not be part of the branch afterwards.
I think the last sentence is superfluous and obvious. Everyone
understands that if A is replaced by B, B might be different from A, and
thus not everything of A might end up in B.
If you want to send a patch with that unnecessary information, go ahead,
what I'm saying is that if the first part is written correctly the last
part is obvious.
Cheers.
--
Felipe Contreras