Git fetch not working as expected “:gone]” is not being added to branch description2019 Community Moderator ElectionCheck if a git branch is ahead of another using a scriptgit branch ahead and behind for local branch?How to remove local (untracked) files from the current Git working tree?How to clone all remote branches in Git?What is the difference between 'git pull' and 'git fetch'?Make an existing Git branch track a remote branch?How do you create a remote Git branch?Move the most recent commit(s) to a new branch with GitHow do I check out a remote Git branch?How do I delete a Git branch both locally and remotely?How do I rename a local Git branch?Git fetch remote branch
When was drinking water recognized as crucial in marathon running?
Where is this quote about overcoming the impossible said in "Interstellar"?
I can't die. Who am I?
How to mitigate "bandwagon attacking" from players?
Why won't the strings command stop?
How to fix my table, centering of columns
When to use mean vs median
Wardrobe above a wall with fuse boxes
3.5% Interest Student Loan or use all of my savings on Tuition?
1970s scifi/horror novel where protagonist is used by a crablike creature to feed its larvae, goes mad, and is defeated by retraumatising him
A bug in Excel? Conditional formatting for marking duplicates also highlights unique value
Book about a time-travel war fought by computers
Is there a math equivalent to the conditional ternary operator?
“I had a flat in the centre of town, but I didn’t like living there, so …”
Can I solder 12/2 Romex to extend wire 5 ft?
Is there a frame of reference in which I was born before I was conceived?
Has Wakanda ever accepted refugees?
Sometimes a banana is just a banana
PTIJ: Why can't I sing about soda on certain days?
PTIJ: Aharon, King of Egypt
Did Amazon pay $0 in taxes last year?
Meaning of word ягоза
Is there a limit on the maximum number of future jobs queued in an org?
School performs periodic password audits. Is my password compromised?
Git fetch not working as expected “:gone]” is not being added to branch description
2019 Community Moderator ElectionCheck if a git branch is ahead of another using a scriptgit branch ahead and behind for local branch?How to remove local (untracked) files from the current Git working tree?How to clone all remote branches in Git?What is the difference between 'git pull' and 'git fetch'?Make an existing Git branch track a remote branch?How do you create a remote Git branch?Move the most recent commit(s) to a new branch with GitHow do I check out a remote Git branch?How do I delete a Git branch both locally and remotely?How do I rename a local Git branch?Git fetch remote branch
If we execute git fetch -p
or git fetch --prune
it will prune branches if deleted on remote.
After executing this command if we do git branch -vv
it suppose to show : gone]
for the local branches which are deleted from remote.
In my case sometimes it is working as expected but not always. Sometimes it is not adding : gone]
to the branches deleted on remote.
My goal here is to delete branche if local branch is deleted on remote.
I am wondering why this is happening ?
git github
add a comment |
If we execute git fetch -p
or git fetch --prune
it will prune branches if deleted on remote.
After executing this command if we do git branch -vv
it suppose to show : gone]
for the local branches which are deleted from remote.
In my case sometimes it is working as expected but not always. Sometimes it is not adding : gone]
to the branches deleted on remote.
My goal here is to delete branche if local branch is deleted on remote.
I am wondering why this is happening ?
git github
I am also interested in this question. I have an unsatisfactory alias (to output local branches which lack a remote counterpart) that I'd love to replace one of these days (git config --global alias.brloc '!git branch -vv | grep -v origin; git branch -vv | grep gone'
).
– RomainValeri
15 hours ago
add a comment |
If we execute git fetch -p
or git fetch --prune
it will prune branches if deleted on remote.
After executing this command if we do git branch -vv
it suppose to show : gone]
for the local branches which are deleted from remote.
In my case sometimes it is working as expected but not always. Sometimes it is not adding : gone]
to the branches deleted on remote.
My goal here is to delete branche if local branch is deleted on remote.
I am wondering why this is happening ?
git github
If we execute git fetch -p
or git fetch --prune
it will prune branches if deleted on remote.
After executing this command if we do git branch -vv
it suppose to show : gone]
for the local branches which are deleted from remote.
In my case sometimes it is working as expected but not always. Sometimes it is not adding : gone]
to the branches deleted on remote.
My goal here is to delete branche if local branch is deleted on remote.
I am wondering why this is happening ?
git github
git github
edited 16 hours ago
Sanjay Yadav
asked 16 hours ago
Sanjay YadavSanjay Yadav
8929
8929
I am also interested in this question. I have an unsatisfactory alias (to output local branches which lack a remote counterpart) that I'd love to replace one of these days (git config --global alias.brloc '!git branch -vv | grep -v origin; git branch -vv | grep gone'
).
– RomainValeri
15 hours ago
add a comment |
I am also interested in this question. I have an unsatisfactory alias (to output local branches which lack a remote counterpart) that I'd love to replace one of these days (git config --global alias.brloc '!git branch -vv | grep -v origin; git branch -vv | grep gone'
).
– RomainValeri
15 hours ago
I am also interested in this question. I have an unsatisfactory alias (to output local branches which lack a remote counterpart) that I'd love to replace one of these days (
git config --global alias.brloc '!git branch -vv | grep -v origin; git branch -vv | grep gone'
).– RomainValeri
15 hours ago
I am also interested in this question. I have an unsatisfactory alias (to output local branches which lack a remote counterpart) that I'd love to replace one of these days (
git config --global alias.brloc '!git branch -vv | grep -v origin; git branch -vv | grep gone'
).– RomainValeri
15 hours ago
add a comment |
1 Answer
1
active
oldest
votes
It's not necessarily wise to delete your branch named X just because someone else deleted his branch named X. If you're working on a new feature and you called it feat
, and Bob gave up on his new feature that he was calling feat
and Bob deletes Bob's feat
, that doesn't mean you should delete your feat
!
That aside, let's look at a special feature of branches. This special feature is only available for (local) branches, not for things like origin/master
, which parts of Git call remote-tracking branches and which are listed under git branch -r
output.1 They actually have several special features—for instance, you can get "on" a branch using git checkout
, as in after git checkout master
, the git status
command will say on branch master
. But that's not the special feature I mean here.
The special feature we care about here is that they can have an upstream setting. That is, your master
can have one—and only one—upstream, and your develop
, if you have a develop
, can have one upstream, and your feat
, if you have one, can have one upstream, and so on. Your choice here is to have an upstream, or not have an upstream. You make this choice one branch at a time, for each branch.
To take away the upstream setting of a branch, use git branch --unset-upstream name
. The branch named name
now has no upstream. If you leave out the name
part, it applies to the current branch, i.e., the one to which your HEAD
is attached. This is not the only way to unset it, but it's usually the best way.
To set or change the upstream setting of a branch, use git branch --set-upstream-to=upstream name
. The branch named name
now has an upstream; the upstream is has is the argument you gave as upstream
. As with --unset-upstream
, leaving out name
means the current branch (you're not allowed to leave out upstream
). Likewise, this is not the only way to set it, but usually it's the best way, because the git branch
command will verify whether the upstream
argument is sensible before it lets you set it.
Sometimes branches have an upstream setting right from the get-go, and sometimes they don't. We'll look at when and why in a moment.
1I've gotten to the point where I try to avoid the word branch for the remote-tracking things. I now just call remote-tracking names, because they're so different from local branch names. Note that git fetch -p
, or git remote prune origin
, or setting fetch.prune
to true
, affects only these remote-tracking names.
What exactly is an upstream?
An upstream is just another branch name—and here, by branch name, I mean either a local branch like master
or develop
, or a remote-tracking name like origin/master
. So you can have the upstream of develop
set to master
if you like, or to origin/master
, or to origin/develop
. The git branch --set-upstream-to
operation will let you set anything that exists and, to Git's mind, makes sense here.
Git has a weird flaw of sorts here. Part of this is historical. Back in the ancient past, Git did not have remotes—there was no origin
—so Git did not have remote-tracking names either without origin
, there could be no origin/master
. But part is due to the simple fact you may or may not have an origin/xyz
, and origin/xyz
could even go away while you're hard at work on your xyz
branch (because Bob thought you were done and went and deleted xyz
over on origin).
The flaw is, to put it a bit too simply, that the upstream you've set—back when it existed and git branch --set-upstream-to
therefore let you set it—might be gone now. And that's the case when you see : gone
in your git branch -vv
output. At some point in the past, you told your Git that your branch xyz
should have origin/xyz
as its upstream, and origin/xyz
existed at the time. So git branch
verified that all was good and made the setting. But now the name is gone, so the setting is no longer valid and git branch -vv
makes note of that.
In fact, the upstream setting of a branch is built out of two parts, and you can configure either or both parts with git config
, or even bring your configuration in your chosen editor (git config --edit
) and mess with them directly:
[branch "master"]
remote = origin
merge = refs/heads/master
This setting, in this particular repository, says that the upstream of master
is origin/master
. You might think you can just take the origin
part from the remote =
line and the master
part from the merge =
line, but this is a sort of trap: there's a secret complicated mapping. To find the upstream setting of some branch, use git rev-parse
with the @upstream
suffix:
$ git rev-parse --symbolic-full-name master@upstream
refs/remotes/origin/master
This works for branches whose upstream is set to be another local branch too.
Anyway, because it is two parts like this and you can mess with it outside the normal git branch --set-upstream-to
mechanism, you can break the upstream setting. If you do break it—if you set it to something nonsensical—git branch -vv
will list the upstream as "gone", using the same technique as when normal, everyday Git operations break it because it really did go away. Git does not care why it's broken, only that it is or is not broken right now: if it is broken right now, Git says "gone" and otherwise pretends it's unset.
Note that this also means that if Bob accidentally removed xyz
from origin
and Bob fixes his mistake and puts xyz
back, your Git can go from "gone" to "not gone, everything is fine" after another git fetch
. That's yet another reason you should not have your Git remove your local branch just because someone messed with some other Git somewhere else.
What good is an upstream?
The upstream setting only offers a few minor features. Sometimes, some people really like these features, but they're never required. So in one sense, an upstream is no good at all. You don't have to use them, ever, provided you avoid git pull
.
The features, though, are:
Easier push: the standard
push.default
setting requires that your branch have an upstream if you want to rungit push
without extra arguments.Easier merge and rebase: the
git merge
andgit rebase
commands can use it. Thegit pull
wrapper, which you don't have to use ever and which I recommend avoiding anyway, require an upstream. The wrapper first runsgit fetch
for you, and then immediately—before you have a chance to decide whether it's a good idea based on whatgit fetch
did—runsgit merge
orgit rebase
for you.More-useful
git status
: the first line ofgit status
tells you whether you're in detached HEAD mode, and if not, which branch you're on. Having an upstream means that there's a second line, right after the one about the branch you're on, when you're on a branch. The second line compares the branch to its upstream and tells you whether it is "up to date" with its upstream.
So that's what an upstream is good for: it makes some things easier and/or more useful. But you are limited to one upstream at a time. You may, sometimes, want to get the equivalent of the git status
second-line output for some arbitrary pair of names. There is a way to do that using git rev-list --count
, but we'll leave that for other answers.
Why do some branches already have an upstream set, and others don't?
There are lots of ways to create branches in Git. Each one does something a little bit different from its other alternatives—which is why each of these methods exists, but also gives you this profusion of confusion.
The two main commands for creating new branches are git branch
and git checkout
. When you use git branch
to create a new branch, you are in full control at all times:
git branch --track newbranch origin/upstreamname
or:
git branch --no-track newbranch origin/upstreamname
Despite the upstream being called upstream, the argument controlling it is spelled --track
—another historical accident or mistake, really.
If you don't use --track
or --no-track
here, git branch
uses whatever you've chosen as a default when you configured branch.autoSetupMerge
. See below for what that means.
When you use git checkout
, you can use --track
or --no-track
, but if you don't use either option, it gets more complicated. The branch.autoSetupMerge
setting still matters but ... well:
git checkout -b newbranch
createsnewbranch
, but creates it fromHEAD
, so it doesn't set an upstream. That is, the new namenewbranch
now identifies the same commit thatHEAD
identified just before this.git checkout -b newbranch origin/name
createsnewbranch
and creates it usingorigin/name
. That is, the new namenewbranch
now identifies the same commit asorigin/name
. This obeys yourbranch.autoSetupMerge
configuration.git checkout -b newbranch existing-branch
createsnewbranch
, usingexisting-branch
as its starting point. That is, the new name and the existing name now identify the same commit. This also obeys yourbranch.autoSetupMerge
configuration, but see below for why I separated this out.git checkout name
either uses an existingname
, or creates a new branch namedname
usingorigin/name
. If it creates a new branch, it obeysbranch.autoSetupMerge
.Note that this will never create a new branch using a local branch name, i.e., it can't make a local branch that has another local branch as its upstream. If that were going to be the case, the two *
name
*s would be the same, so the branch by definition already exists, so it just checks out the local branch without creating anything.git checkout --track origin/name
creates a new branch namedname
and does set its upstream.git checkout --no-track origin/name
creates a new branch namedname
and makes sure it has no upstream.
So, as you can see, it's complicated! The exact method of creating the branch determines in part whether it has an upstream, with the rest of the determination being based on your branch.autoSetupMerge
configuration. This setting has three possible values:
true
: set the upstream when the starting-point is a remote-tracking name.false
: don't set the upstream.always
: set the upstream when the starting-point is either a remote-tracking name or a (local) branch name.
The default, if you did not set branch.autoSetupMerge
at all, is to pretend you set it to true
. So by default, all of these branch-creating options act like you said --track
if you give them an origin/*
name, or any other remote-tracking name, as their starting-point. That's true even if the starting point is merely implied, as in the:
git checkout develop
case where you don't have a local develop
but do have a remote-tracking origin/develop
: this creates your new local develop
from your remote-tracking origin/develop
, and if branch.autoSetupMerge
is true
—including if you haven't set it at all—now your develop
"tracks"—has as its upstream—your origin/develop
.
Summary
Some of your branches have upstreams set. Which branches have upstreams, and what those upstreams are, is up to you, controlled by your configuration and/or by your command-line options and/or by additional git branch --set-upstream-to
or git branch --unset-upstream
commands.
Those branches that do have upstreams set will get some useful features. If the upstream that is set "goes away" by whatever process, git branch -vv
will list your local branches and say that their upstream is "gone". Other Git commands will just pretend you unset their upstream. If the upstream comes back again, the upstream will resume its usefulness.
Deleting a local branch only because its upstream is gone is a mistake. Deleting it because you are done with it is fine. You don't need to have a local branch named develop
, or even one named master
, if you're done with them, even if they still exist as upstream origin/develop
or origin/master
names. You can just use origin/master
to refer to your Git's memory of the master
on origin
—you don't need to have your own master
at all.
Sometimes, you are done with a branch because of some underlying reason that also means that the upstream is going to be gone, or is already gone. In that case, deleting your local branch is fine—you're deleting it because you're done with it. The fact that it will be deleted on origin
, or already has been deleted on origin
, is irrelevant here!
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55020899%2fgit-fetch-not-working-as-expected-gone-is-not-being-added-to-branch-descript%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
It's not necessarily wise to delete your branch named X just because someone else deleted his branch named X. If you're working on a new feature and you called it feat
, and Bob gave up on his new feature that he was calling feat
and Bob deletes Bob's feat
, that doesn't mean you should delete your feat
!
That aside, let's look at a special feature of branches. This special feature is only available for (local) branches, not for things like origin/master
, which parts of Git call remote-tracking branches and which are listed under git branch -r
output.1 They actually have several special features—for instance, you can get "on" a branch using git checkout
, as in after git checkout master
, the git status
command will say on branch master
. But that's not the special feature I mean here.
The special feature we care about here is that they can have an upstream setting. That is, your master
can have one—and only one—upstream, and your develop
, if you have a develop
, can have one upstream, and your feat
, if you have one, can have one upstream, and so on. Your choice here is to have an upstream, or not have an upstream. You make this choice one branch at a time, for each branch.
To take away the upstream setting of a branch, use git branch --unset-upstream name
. The branch named name
now has no upstream. If you leave out the name
part, it applies to the current branch, i.e., the one to which your HEAD
is attached. This is not the only way to unset it, but it's usually the best way.
To set or change the upstream setting of a branch, use git branch --set-upstream-to=upstream name
. The branch named name
now has an upstream; the upstream is has is the argument you gave as upstream
. As with --unset-upstream
, leaving out name
means the current branch (you're not allowed to leave out upstream
). Likewise, this is not the only way to set it, but usually it's the best way, because the git branch
command will verify whether the upstream
argument is sensible before it lets you set it.
Sometimes branches have an upstream setting right from the get-go, and sometimes they don't. We'll look at when and why in a moment.
1I've gotten to the point where I try to avoid the word branch for the remote-tracking things. I now just call remote-tracking names, because they're so different from local branch names. Note that git fetch -p
, or git remote prune origin
, or setting fetch.prune
to true
, affects only these remote-tracking names.
What exactly is an upstream?
An upstream is just another branch name—and here, by branch name, I mean either a local branch like master
or develop
, or a remote-tracking name like origin/master
. So you can have the upstream of develop
set to master
if you like, or to origin/master
, or to origin/develop
. The git branch --set-upstream-to
operation will let you set anything that exists and, to Git's mind, makes sense here.
Git has a weird flaw of sorts here. Part of this is historical. Back in the ancient past, Git did not have remotes—there was no origin
—so Git did not have remote-tracking names either without origin
, there could be no origin/master
. But part is due to the simple fact you may or may not have an origin/xyz
, and origin/xyz
could even go away while you're hard at work on your xyz
branch (because Bob thought you were done and went and deleted xyz
over on origin).
The flaw is, to put it a bit too simply, that the upstream you've set—back when it existed and git branch --set-upstream-to
therefore let you set it—might be gone now. And that's the case when you see : gone
in your git branch -vv
output. At some point in the past, you told your Git that your branch xyz
should have origin/xyz
as its upstream, and origin/xyz
existed at the time. So git branch
verified that all was good and made the setting. But now the name is gone, so the setting is no longer valid and git branch -vv
makes note of that.
In fact, the upstream setting of a branch is built out of two parts, and you can configure either or both parts with git config
, or even bring your configuration in your chosen editor (git config --edit
) and mess with them directly:
[branch "master"]
remote = origin
merge = refs/heads/master
This setting, in this particular repository, says that the upstream of master
is origin/master
. You might think you can just take the origin
part from the remote =
line and the master
part from the merge =
line, but this is a sort of trap: there's a secret complicated mapping. To find the upstream setting of some branch, use git rev-parse
with the @upstream
suffix:
$ git rev-parse --symbolic-full-name master@upstream
refs/remotes/origin/master
This works for branches whose upstream is set to be another local branch too.
Anyway, because it is two parts like this and you can mess with it outside the normal git branch --set-upstream-to
mechanism, you can break the upstream setting. If you do break it—if you set it to something nonsensical—git branch -vv
will list the upstream as "gone", using the same technique as when normal, everyday Git operations break it because it really did go away. Git does not care why it's broken, only that it is or is not broken right now: if it is broken right now, Git says "gone" and otherwise pretends it's unset.
Note that this also means that if Bob accidentally removed xyz
from origin
and Bob fixes his mistake and puts xyz
back, your Git can go from "gone" to "not gone, everything is fine" after another git fetch
. That's yet another reason you should not have your Git remove your local branch just because someone messed with some other Git somewhere else.
What good is an upstream?
The upstream setting only offers a few minor features. Sometimes, some people really like these features, but they're never required. So in one sense, an upstream is no good at all. You don't have to use them, ever, provided you avoid git pull
.
The features, though, are:
Easier push: the standard
push.default
setting requires that your branch have an upstream if you want to rungit push
without extra arguments.Easier merge and rebase: the
git merge
andgit rebase
commands can use it. Thegit pull
wrapper, which you don't have to use ever and which I recommend avoiding anyway, require an upstream. The wrapper first runsgit fetch
for you, and then immediately—before you have a chance to decide whether it's a good idea based on whatgit fetch
did—runsgit merge
orgit rebase
for you.More-useful
git status
: the first line ofgit status
tells you whether you're in detached HEAD mode, and if not, which branch you're on. Having an upstream means that there's a second line, right after the one about the branch you're on, when you're on a branch. The second line compares the branch to its upstream and tells you whether it is "up to date" with its upstream.
So that's what an upstream is good for: it makes some things easier and/or more useful. But you are limited to one upstream at a time. You may, sometimes, want to get the equivalent of the git status
second-line output for some arbitrary pair of names. There is a way to do that using git rev-list --count
, but we'll leave that for other answers.
Why do some branches already have an upstream set, and others don't?
There are lots of ways to create branches in Git. Each one does something a little bit different from its other alternatives—which is why each of these methods exists, but also gives you this profusion of confusion.
The two main commands for creating new branches are git branch
and git checkout
. When you use git branch
to create a new branch, you are in full control at all times:
git branch --track newbranch origin/upstreamname
or:
git branch --no-track newbranch origin/upstreamname
Despite the upstream being called upstream, the argument controlling it is spelled --track
—another historical accident or mistake, really.
If you don't use --track
or --no-track
here, git branch
uses whatever you've chosen as a default when you configured branch.autoSetupMerge
. See below for what that means.
When you use git checkout
, you can use --track
or --no-track
, but if you don't use either option, it gets more complicated. The branch.autoSetupMerge
setting still matters but ... well:
git checkout -b newbranch
createsnewbranch
, but creates it fromHEAD
, so it doesn't set an upstream. That is, the new namenewbranch
now identifies the same commit thatHEAD
identified just before this.git checkout -b newbranch origin/name
createsnewbranch
and creates it usingorigin/name
. That is, the new namenewbranch
now identifies the same commit asorigin/name
. This obeys yourbranch.autoSetupMerge
configuration.git checkout -b newbranch existing-branch
createsnewbranch
, usingexisting-branch
as its starting point. That is, the new name and the existing name now identify the same commit. This also obeys yourbranch.autoSetupMerge
configuration, but see below for why I separated this out.git checkout name
either uses an existingname
, or creates a new branch namedname
usingorigin/name
. If it creates a new branch, it obeysbranch.autoSetupMerge
.Note that this will never create a new branch using a local branch name, i.e., it can't make a local branch that has another local branch as its upstream. If that were going to be the case, the two *
name
*s would be the same, so the branch by definition already exists, so it just checks out the local branch without creating anything.git checkout --track origin/name
creates a new branch namedname
and does set its upstream.git checkout --no-track origin/name
creates a new branch namedname
and makes sure it has no upstream.
So, as you can see, it's complicated! The exact method of creating the branch determines in part whether it has an upstream, with the rest of the determination being based on your branch.autoSetupMerge
configuration. This setting has three possible values:
true
: set the upstream when the starting-point is a remote-tracking name.false
: don't set the upstream.always
: set the upstream when the starting-point is either a remote-tracking name or a (local) branch name.
The default, if you did not set branch.autoSetupMerge
at all, is to pretend you set it to true
. So by default, all of these branch-creating options act like you said --track
if you give them an origin/*
name, or any other remote-tracking name, as their starting-point. That's true even if the starting point is merely implied, as in the:
git checkout develop
case where you don't have a local develop
but do have a remote-tracking origin/develop
: this creates your new local develop
from your remote-tracking origin/develop
, and if branch.autoSetupMerge
is true
—including if you haven't set it at all—now your develop
"tracks"—has as its upstream—your origin/develop
.
Summary
Some of your branches have upstreams set. Which branches have upstreams, and what those upstreams are, is up to you, controlled by your configuration and/or by your command-line options and/or by additional git branch --set-upstream-to
or git branch --unset-upstream
commands.
Those branches that do have upstreams set will get some useful features. If the upstream that is set "goes away" by whatever process, git branch -vv
will list your local branches and say that their upstream is "gone". Other Git commands will just pretend you unset their upstream. If the upstream comes back again, the upstream will resume its usefulness.
Deleting a local branch only because its upstream is gone is a mistake. Deleting it because you are done with it is fine. You don't need to have a local branch named develop
, or even one named master
, if you're done with them, even if they still exist as upstream origin/develop
or origin/master
names. You can just use origin/master
to refer to your Git's memory of the master
on origin
—you don't need to have your own master
at all.
Sometimes, you are done with a branch because of some underlying reason that also means that the upstream is going to be gone, or is already gone. In that case, deleting your local branch is fine—you're deleting it because you're done with it. The fact that it will be deleted on origin
, or already has been deleted on origin
, is irrelevant here!
add a comment |
It's not necessarily wise to delete your branch named X just because someone else deleted his branch named X. If you're working on a new feature and you called it feat
, and Bob gave up on his new feature that he was calling feat
and Bob deletes Bob's feat
, that doesn't mean you should delete your feat
!
That aside, let's look at a special feature of branches. This special feature is only available for (local) branches, not for things like origin/master
, which parts of Git call remote-tracking branches and which are listed under git branch -r
output.1 They actually have several special features—for instance, you can get "on" a branch using git checkout
, as in after git checkout master
, the git status
command will say on branch master
. But that's not the special feature I mean here.
The special feature we care about here is that they can have an upstream setting. That is, your master
can have one—and only one—upstream, and your develop
, if you have a develop
, can have one upstream, and your feat
, if you have one, can have one upstream, and so on. Your choice here is to have an upstream, or not have an upstream. You make this choice one branch at a time, for each branch.
To take away the upstream setting of a branch, use git branch --unset-upstream name
. The branch named name
now has no upstream. If you leave out the name
part, it applies to the current branch, i.e., the one to which your HEAD
is attached. This is not the only way to unset it, but it's usually the best way.
To set or change the upstream setting of a branch, use git branch --set-upstream-to=upstream name
. The branch named name
now has an upstream; the upstream is has is the argument you gave as upstream
. As with --unset-upstream
, leaving out name
means the current branch (you're not allowed to leave out upstream
). Likewise, this is not the only way to set it, but usually it's the best way, because the git branch
command will verify whether the upstream
argument is sensible before it lets you set it.
Sometimes branches have an upstream setting right from the get-go, and sometimes they don't. We'll look at when and why in a moment.
1I've gotten to the point where I try to avoid the word branch for the remote-tracking things. I now just call remote-tracking names, because they're so different from local branch names. Note that git fetch -p
, or git remote prune origin
, or setting fetch.prune
to true
, affects only these remote-tracking names.
What exactly is an upstream?
An upstream is just another branch name—and here, by branch name, I mean either a local branch like master
or develop
, or a remote-tracking name like origin/master
. So you can have the upstream of develop
set to master
if you like, or to origin/master
, or to origin/develop
. The git branch --set-upstream-to
operation will let you set anything that exists and, to Git's mind, makes sense here.
Git has a weird flaw of sorts here. Part of this is historical. Back in the ancient past, Git did not have remotes—there was no origin
—so Git did not have remote-tracking names either without origin
, there could be no origin/master
. But part is due to the simple fact you may or may not have an origin/xyz
, and origin/xyz
could even go away while you're hard at work on your xyz
branch (because Bob thought you were done and went and deleted xyz
over on origin).
The flaw is, to put it a bit too simply, that the upstream you've set—back when it existed and git branch --set-upstream-to
therefore let you set it—might be gone now. And that's the case when you see : gone
in your git branch -vv
output. At some point in the past, you told your Git that your branch xyz
should have origin/xyz
as its upstream, and origin/xyz
existed at the time. So git branch
verified that all was good and made the setting. But now the name is gone, so the setting is no longer valid and git branch -vv
makes note of that.
In fact, the upstream setting of a branch is built out of two parts, and you can configure either or both parts with git config
, or even bring your configuration in your chosen editor (git config --edit
) and mess with them directly:
[branch "master"]
remote = origin
merge = refs/heads/master
This setting, in this particular repository, says that the upstream of master
is origin/master
. You might think you can just take the origin
part from the remote =
line and the master
part from the merge =
line, but this is a sort of trap: there's a secret complicated mapping. To find the upstream setting of some branch, use git rev-parse
with the @upstream
suffix:
$ git rev-parse --symbolic-full-name master@upstream
refs/remotes/origin/master
This works for branches whose upstream is set to be another local branch too.
Anyway, because it is two parts like this and you can mess with it outside the normal git branch --set-upstream-to
mechanism, you can break the upstream setting. If you do break it—if you set it to something nonsensical—git branch -vv
will list the upstream as "gone", using the same technique as when normal, everyday Git operations break it because it really did go away. Git does not care why it's broken, only that it is or is not broken right now: if it is broken right now, Git says "gone" and otherwise pretends it's unset.
Note that this also means that if Bob accidentally removed xyz
from origin
and Bob fixes his mistake and puts xyz
back, your Git can go from "gone" to "not gone, everything is fine" after another git fetch
. That's yet another reason you should not have your Git remove your local branch just because someone messed with some other Git somewhere else.
What good is an upstream?
The upstream setting only offers a few minor features. Sometimes, some people really like these features, but they're never required. So in one sense, an upstream is no good at all. You don't have to use them, ever, provided you avoid git pull
.
The features, though, are:
Easier push: the standard
push.default
setting requires that your branch have an upstream if you want to rungit push
without extra arguments.Easier merge and rebase: the
git merge
andgit rebase
commands can use it. Thegit pull
wrapper, which you don't have to use ever and which I recommend avoiding anyway, require an upstream. The wrapper first runsgit fetch
for you, and then immediately—before you have a chance to decide whether it's a good idea based on whatgit fetch
did—runsgit merge
orgit rebase
for you.More-useful
git status
: the first line ofgit status
tells you whether you're in detached HEAD mode, and if not, which branch you're on. Having an upstream means that there's a second line, right after the one about the branch you're on, when you're on a branch. The second line compares the branch to its upstream and tells you whether it is "up to date" with its upstream.
So that's what an upstream is good for: it makes some things easier and/or more useful. But you are limited to one upstream at a time. You may, sometimes, want to get the equivalent of the git status
second-line output for some arbitrary pair of names. There is a way to do that using git rev-list --count
, but we'll leave that for other answers.
Why do some branches already have an upstream set, and others don't?
There are lots of ways to create branches in Git. Each one does something a little bit different from its other alternatives—which is why each of these methods exists, but also gives you this profusion of confusion.
The two main commands for creating new branches are git branch
and git checkout
. When you use git branch
to create a new branch, you are in full control at all times:
git branch --track newbranch origin/upstreamname
or:
git branch --no-track newbranch origin/upstreamname
Despite the upstream being called upstream, the argument controlling it is spelled --track
—another historical accident or mistake, really.
If you don't use --track
or --no-track
here, git branch
uses whatever you've chosen as a default when you configured branch.autoSetupMerge
. See below for what that means.
When you use git checkout
, you can use --track
or --no-track
, but if you don't use either option, it gets more complicated. The branch.autoSetupMerge
setting still matters but ... well:
git checkout -b newbranch
createsnewbranch
, but creates it fromHEAD
, so it doesn't set an upstream. That is, the new namenewbranch
now identifies the same commit thatHEAD
identified just before this.git checkout -b newbranch origin/name
createsnewbranch
and creates it usingorigin/name
. That is, the new namenewbranch
now identifies the same commit asorigin/name
. This obeys yourbranch.autoSetupMerge
configuration.git checkout -b newbranch existing-branch
createsnewbranch
, usingexisting-branch
as its starting point. That is, the new name and the existing name now identify the same commit. This also obeys yourbranch.autoSetupMerge
configuration, but see below for why I separated this out.git checkout name
either uses an existingname
, or creates a new branch namedname
usingorigin/name
. If it creates a new branch, it obeysbranch.autoSetupMerge
.Note that this will never create a new branch using a local branch name, i.e., it can't make a local branch that has another local branch as its upstream. If that were going to be the case, the two *
name
*s would be the same, so the branch by definition already exists, so it just checks out the local branch without creating anything.git checkout --track origin/name
creates a new branch namedname
and does set its upstream.git checkout --no-track origin/name
creates a new branch namedname
and makes sure it has no upstream.
So, as you can see, it's complicated! The exact method of creating the branch determines in part whether it has an upstream, with the rest of the determination being based on your branch.autoSetupMerge
configuration. This setting has three possible values:
true
: set the upstream when the starting-point is a remote-tracking name.false
: don't set the upstream.always
: set the upstream when the starting-point is either a remote-tracking name or a (local) branch name.
The default, if you did not set branch.autoSetupMerge
at all, is to pretend you set it to true
. So by default, all of these branch-creating options act like you said --track
if you give them an origin/*
name, or any other remote-tracking name, as their starting-point. That's true even if the starting point is merely implied, as in the:
git checkout develop
case where you don't have a local develop
but do have a remote-tracking origin/develop
: this creates your new local develop
from your remote-tracking origin/develop
, and if branch.autoSetupMerge
is true
—including if you haven't set it at all—now your develop
"tracks"—has as its upstream—your origin/develop
.
Summary
Some of your branches have upstreams set. Which branches have upstreams, and what those upstreams are, is up to you, controlled by your configuration and/or by your command-line options and/or by additional git branch --set-upstream-to
or git branch --unset-upstream
commands.
Those branches that do have upstreams set will get some useful features. If the upstream that is set "goes away" by whatever process, git branch -vv
will list your local branches and say that their upstream is "gone". Other Git commands will just pretend you unset their upstream. If the upstream comes back again, the upstream will resume its usefulness.
Deleting a local branch only because its upstream is gone is a mistake. Deleting it because you are done with it is fine. You don't need to have a local branch named develop
, or even one named master
, if you're done with them, even if they still exist as upstream origin/develop
or origin/master
names. You can just use origin/master
to refer to your Git's memory of the master
on origin
—you don't need to have your own master
at all.
Sometimes, you are done with a branch because of some underlying reason that also means that the upstream is going to be gone, or is already gone. In that case, deleting your local branch is fine—you're deleting it because you're done with it. The fact that it will be deleted on origin
, or already has been deleted on origin
, is irrelevant here!
add a comment |
It's not necessarily wise to delete your branch named X just because someone else deleted his branch named X. If you're working on a new feature and you called it feat
, and Bob gave up on his new feature that he was calling feat
and Bob deletes Bob's feat
, that doesn't mean you should delete your feat
!
That aside, let's look at a special feature of branches. This special feature is only available for (local) branches, not for things like origin/master
, which parts of Git call remote-tracking branches and which are listed under git branch -r
output.1 They actually have several special features—for instance, you can get "on" a branch using git checkout
, as in after git checkout master
, the git status
command will say on branch master
. But that's not the special feature I mean here.
The special feature we care about here is that they can have an upstream setting. That is, your master
can have one—and only one—upstream, and your develop
, if you have a develop
, can have one upstream, and your feat
, if you have one, can have one upstream, and so on. Your choice here is to have an upstream, or not have an upstream. You make this choice one branch at a time, for each branch.
To take away the upstream setting of a branch, use git branch --unset-upstream name
. The branch named name
now has no upstream. If you leave out the name
part, it applies to the current branch, i.e., the one to which your HEAD
is attached. This is not the only way to unset it, but it's usually the best way.
To set or change the upstream setting of a branch, use git branch --set-upstream-to=upstream name
. The branch named name
now has an upstream; the upstream is has is the argument you gave as upstream
. As with --unset-upstream
, leaving out name
means the current branch (you're not allowed to leave out upstream
). Likewise, this is not the only way to set it, but usually it's the best way, because the git branch
command will verify whether the upstream
argument is sensible before it lets you set it.
Sometimes branches have an upstream setting right from the get-go, and sometimes they don't. We'll look at when and why in a moment.
1I've gotten to the point where I try to avoid the word branch for the remote-tracking things. I now just call remote-tracking names, because they're so different from local branch names. Note that git fetch -p
, or git remote prune origin
, or setting fetch.prune
to true
, affects only these remote-tracking names.
What exactly is an upstream?
An upstream is just another branch name—and here, by branch name, I mean either a local branch like master
or develop
, or a remote-tracking name like origin/master
. So you can have the upstream of develop
set to master
if you like, or to origin/master
, or to origin/develop
. The git branch --set-upstream-to
operation will let you set anything that exists and, to Git's mind, makes sense here.
Git has a weird flaw of sorts here. Part of this is historical. Back in the ancient past, Git did not have remotes—there was no origin
—so Git did not have remote-tracking names either without origin
, there could be no origin/master
. But part is due to the simple fact you may or may not have an origin/xyz
, and origin/xyz
could even go away while you're hard at work on your xyz
branch (because Bob thought you were done and went and deleted xyz
over on origin).
The flaw is, to put it a bit too simply, that the upstream you've set—back when it existed and git branch --set-upstream-to
therefore let you set it—might be gone now. And that's the case when you see : gone
in your git branch -vv
output. At some point in the past, you told your Git that your branch xyz
should have origin/xyz
as its upstream, and origin/xyz
existed at the time. So git branch
verified that all was good and made the setting. But now the name is gone, so the setting is no longer valid and git branch -vv
makes note of that.
In fact, the upstream setting of a branch is built out of two parts, and you can configure either or both parts with git config
, or even bring your configuration in your chosen editor (git config --edit
) and mess with them directly:
[branch "master"]
remote = origin
merge = refs/heads/master
This setting, in this particular repository, says that the upstream of master
is origin/master
. You might think you can just take the origin
part from the remote =
line and the master
part from the merge =
line, but this is a sort of trap: there's a secret complicated mapping. To find the upstream setting of some branch, use git rev-parse
with the @upstream
suffix:
$ git rev-parse --symbolic-full-name master@upstream
refs/remotes/origin/master
This works for branches whose upstream is set to be another local branch too.
Anyway, because it is two parts like this and you can mess with it outside the normal git branch --set-upstream-to
mechanism, you can break the upstream setting. If you do break it—if you set it to something nonsensical—git branch -vv
will list the upstream as "gone", using the same technique as when normal, everyday Git operations break it because it really did go away. Git does not care why it's broken, only that it is or is not broken right now: if it is broken right now, Git says "gone" and otherwise pretends it's unset.
Note that this also means that if Bob accidentally removed xyz
from origin
and Bob fixes his mistake and puts xyz
back, your Git can go from "gone" to "not gone, everything is fine" after another git fetch
. That's yet another reason you should not have your Git remove your local branch just because someone messed with some other Git somewhere else.
What good is an upstream?
The upstream setting only offers a few minor features. Sometimes, some people really like these features, but they're never required. So in one sense, an upstream is no good at all. You don't have to use them, ever, provided you avoid git pull
.
The features, though, are:
Easier push: the standard
push.default
setting requires that your branch have an upstream if you want to rungit push
without extra arguments.Easier merge and rebase: the
git merge
andgit rebase
commands can use it. Thegit pull
wrapper, which you don't have to use ever and which I recommend avoiding anyway, require an upstream. The wrapper first runsgit fetch
for you, and then immediately—before you have a chance to decide whether it's a good idea based on whatgit fetch
did—runsgit merge
orgit rebase
for you.More-useful
git status
: the first line ofgit status
tells you whether you're in detached HEAD mode, and if not, which branch you're on. Having an upstream means that there's a second line, right after the one about the branch you're on, when you're on a branch. The second line compares the branch to its upstream and tells you whether it is "up to date" with its upstream.
So that's what an upstream is good for: it makes some things easier and/or more useful. But you are limited to one upstream at a time. You may, sometimes, want to get the equivalent of the git status
second-line output for some arbitrary pair of names. There is a way to do that using git rev-list --count
, but we'll leave that for other answers.
Why do some branches already have an upstream set, and others don't?
There are lots of ways to create branches in Git. Each one does something a little bit different from its other alternatives—which is why each of these methods exists, but also gives you this profusion of confusion.
The two main commands for creating new branches are git branch
and git checkout
. When you use git branch
to create a new branch, you are in full control at all times:
git branch --track newbranch origin/upstreamname
or:
git branch --no-track newbranch origin/upstreamname
Despite the upstream being called upstream, the argument controlling it is spelled --track
—another historical accident or mistake, really.
If you don't use --track
or --no-track
here, git branch
uses whatever you've chosen as a default when you configured branch.autoSetupMerge
. See below for what that means.
When you use git checkout
, you can use --track
or --no-track
, but if you don't use either option, it gets more complicated. The branch.autoSetupMerge
setting still matters but ... well:
git checkout -b newbranch
createsnewbranch
, but creates it fromHEAD
, so it doesn't set an upstream. That is, the new namenewbranch
now identifies the same commit thatHEAD
identified just before this.git checkout -b newbranch origin/name
createsnewbranch
and creates it usingorigin/name
. That is, the new namenewbranch
now identifies the same commit asorigin/name
. This obeys yourbranch.autoSetupMerge
configuration.git checkout -b newbranch existing-branch
createsnewbranch
, usingexisting-branch
as its starting point. That is, the new name and the existing name now identify the same commit. This also obeys yourbranch.autoSetupMerge
configuration, but see below for why I separated this out.git checkout name
either uses an existingname
, or creates a new branch namedname
usingorigin/name
. If it creates a new branch, it obeysbranch.autoSetupMerge
.Note that this will never create a new branch using a local branch name, i.e., it can't make a local branch that has another local branch as its upstream. If that were going to be the case, the two *
name
*s would be the same, so the branch by definition already exists, so it just checks out the local branch without creating anything.git checkout --track origin/name
creates a new branch namedname
and does set its upstream.git checkout --no-track origin/name
creates a new branch namedname
and makes sure it has no upstream.
So, as you can see, it's complicated! The exact method of creating the branch determines in part whether it has an upstream, with the rest of the determination being based on your branch.autoSetupMerge
configuration. This setting has three possible values:
true
: set the upstream when the starting-point is a remote-tracking name.false
: don't set the upstream.always
: set the upstream when the starting-point is either a remote-tracking name or a (local) branch name.
The default, if you did not set branch.autoSetupMerge
at all, is to pretend you set it to true
. So by default, all of these branch-creating options act like you said --track
if you give them an origin/*
name, or any other remote-tracking name, as their starting-point. That's true even if the starting point is merely implied, as in the:
git checkout develop
case where you don't have a local develop
but do have a remote-tracking origin/develop
: this creates your new local develop
from your remote-tracking origin/develop
, and if branch.autoSetupMerge
is true
—including if you haven't set it at all—now your develop
"tracks"—has as its upstream—your origin/develop
.
Summary
Some of your branches have upstreams set. Which branches have upstreams, and what those upstreams are, is up to you, controlled by your configuration and/or by your command-line options and/or by additional git branch --set-upstream-to
or git branch --unset-upstream
commands.
Those branches that do have upstreams set will get some useful features. If the upstream that is set "goes away" by whatever process, git branch -vv
will list your local branches and say that their upstream is "gone". Other Git commands will just pretend you unset their upstream. If the upstream comes back again, the upstream will resume its usefulness.
Deleting a local branch only because its upstream is gone is a mistake. Deleting it because you are done with it is fine. You don't need to have a local branch named develop
, or even one named master
, if you're done with them, even if they still exist as upstream origin/develop
or origin/master
names. You can just use origin/master
to refer to your Git's memory of the master
on origin
—you don't need to have your own master
at all.
Sometimes, you are done with a branch because of some underlying reason that also means that the upstream is going to be gone, or is already gone. In that case, deleting your local branch is fine—you're deleting it because you're done with it. The fact that it will be deleted on origin
, or already has been deleted on origin
, is irrelevant here!
It's not necessarily wise to delete your branch named X just because someone else deleted his branch named X. If you're working on a new feature and you called it feat
, and Bob gave up on his new feature that he was calling feat
and Bob deletes Bob's feat
, that doesn't mean you should delete your feat
!
That aside, let's look at a special feature of branches. This special feature is only available for (local) branches, not for things like origin/master
, which parts of Git call remote-tracking branches and which are listed under git branch -r
output.1 They actually have several special features—for instance, you can get "on" a branch using git checkout
, as in after git checkout master
, the git status
command will say on branch master
. But that's not the special feature I mean here.
The special feature we care about here is that they can have an upstream setting. That is, your master
can have one—and only one—upstream, and your develop
, if you have a develop
, can have one upstream, and your feat
, if you have one, can have one upstream, and so on. Your choice here is to have an upstream, or not have an upstream. You make this choice one branch at a time, for each branch.
To take away the upstream setting of a branch, use git branch --unset-upstream name
. The branch named name
now has no upstream. If you leave out the name
part, it applies to the current branch, i.e., the one to which your HEAD
is attached. This is not the only way to unset it, but it's usually the best way.
To set or change the upstream setting of a branch, use git branch --set-upstream-to=upstream name
. The branch named name
now has an upstream; the upstream is has is the argument you gave as upstream
. As with --unset-upstream
, leaving out name
means the current branch (you're not allowed to leave out upstream
). Likewise, this is not the only way to set it, but usually it's the best way, because the git branch
command will verify whether the upstream
argument is sensible before it lets you set it.
Sometimes branches have an upstream setting right from the get-go, and sometimes they don't. We'll look at when and why in a moment.
1I've gotten to the point where I try to avoid the word branch for the remote-tracking things. I now just call remote-tracking names, because they're so different from local branch names. Note that git fetch -p
, or git remote prune origin
, or setting fetch.prune
to true
, affects only these remote-tracking names.
What exactly is an upstream?
An upstream is just another branch name—and here, by branch name, I mean either a local branch like master
or develop
, or a remote-tracking name like origin/master
. So you can have the upstream of develop
set to master
if you like, or to origin/master
, or to origin/develop
. The git branch --set-upstream-to
operation will let you set anything that exists and, to Git's mind, makes sense here.
Git has a weird flaw of sorts here. Part of this is historical. Back in the ancient past, Git did not have remotes—there was no origin
—so Git did not have remote-tracking names either without origin
, there could be no origin/master
. But part is due to the simple fact you may or may not have an origin/xyz
, and origin/xyz
could even go away while you're hard at work on your xyz
branch (because Bob thought you were done and went and deleted xyz
over on origin).
The flaw is, to put it a bit too simply, that the upstream you've set—back when it existed and git branch --set-upstream-to
therefore let you set it—might be gone now. And that's the case when you see : gone
in your git branch -vv
output. At some point in the past, you told your Git that your branch xyz
should have origin/xyz
as its upstream, and origin/xyz
existed at the time. So git branch
verified that all was good and made the setting. But now the name is gone, so the setting is no longer valid and git branch -vv
makes note of that.
In fact, the upstream setting of a branch is built out of two parts, and you can configure either or both parts with git config
, or even bring your configuration in your chosen editor (git config --edit
) and mess with them directly:
[branch "master"]
remote = origin
merge = refs/heads/master
This setting, in this particular repository, says that the upstream of master
is origin/master
. You might think you can just take the origin
part from the remote =
line and the master
part from the merge =
line, but this is a sort of trap: there's a secret complicated mapping. To find the upstream setting of some branch, use git rev-parse
with the @upstream
suffix:
$ git rev-parse --symbolic-full-name master@upstream
refs/remotes/origin/master
This works for branches whose upstream is set to be another local branch too.
Anyway, because it is two parts like this and you can mess with it outside the normal git branch --set-upstream-to
mechanism, you can break the upstream setting. If you do break it—if you set it to something nonsensical—git branch -vv
will list the upstream as "gone", using the same technique as when normal, everyday Git operations break it because it really did go away. Git does not care why it's broken, only that it is or is not broken right now: if it is broken right now, Git says "gone" and otherwise pretends it's unset.
Note that this also means that if Bob accidentally removed xyz
from origin
and Bob fixes his mistake and puts xyz
back, your Git can go from "gone" to "not gone, everything is fine" after another git fetch
. That's yet another reason you should not have your Git remove your local branch just because someone messed with some other Git somewhere else.
What good is an upstream?
The upstream setting only offers a few minor features. Sometimes, some people really like these features, but they're never required. So in one sense, an upstream is no good at all. You don't have to use them, ever, provided you avoid git pull
.
The features, though, are:
Easier push: the standard
push.default
setting requires that your branch have an upstream if you want to rungit push
without extra arguments.Easier merge and rebase: the
git merge
andgit rebase
commands can use it. Thegit pull
wrapper, which you don't have to use ever and which I recommend avoiding anyway, require an upstream. The wrapper first runsgit fetch
for you, and then immediately—before you have a chance to decide whether it's a good idea based on whatgit fetch
did—runsgit merge
orgit rebase
for you.More-useful
git status
: the first line ofgit status
tells you whether you're in detached HEAD mode, and if not, which branch you're on. Having an upstream means that there's a second line, right after the one about the branch you're on, when you're on a branch. The second line compares the branch to its upstream and tells you whether it is "up to date" with its upstream.
So that's what an upstream is good for: it makes some things easier and/or more useful. But you are limited to one upstream at a time. You may, sometimes, want to get the equivalent of the git status
second-line output for some arbitrary pair of names. There is a way to do that using git rev-list --count
, but we'll leave that for other answers.
Why do some branches already have an upstream set, and others don't?
There are lots of ways to create branches in Git. Each one does something a little bit different from its other alternatives—which is why each of these methods exists, but also gives you this profusion of confusion.
The two main commands for creating new branches are git branch
and git checkout
. When you use git branch
to create a new branch, you are in full control at all times:
git branch --track newbranch origin/upstreamname
or:
git branch --no-track newbranch origin/upstreamname
Despite the upstream being called upstream, the argument controlling it is spelled --track
—another historical accident or mistake, really.
If you don't use --track
or --no-track
here, git branch
uses whatever you've chosen as a default when you configured branch.autoSetupMerge
. See below for what that means.
When you use git checkout
, you can use --track
or --no-track
, but if you don't use either option, it gets more complicated. The branch.autoSetupMerge
setting still matters but ... well:
git checkout -b newbranch
createsnewbranch
, but creates it fromHEAD
, so it doesn't set an upstream. That is, the new namenewbranch
now identifies the same commit thatHEAD
identified just before this.git checkout -b newbranch origin/name
createsnewbranch
and creates it usingorigin/name
. That is, the new namenewbranch
now identifies the same commit asorigin/name
. This obeys yourbranch.autoSetupMerge
configuration.git checkout -b newbranch existing-branch
createsnewbranch
, usingexisting-branch
as its starting point. That is, the new name and the existing name now identify the same commit. This also obeys yourbranch.autoSetupMerge
configuration, but see below for why I separated this out.git checkout name
either uses an existingname
, or creates a new branch namedname
usingorigin/name
. If it creates a new branch, it obeysbranch.autoSetupMerge
.Note that this will never create a new branch using a local branch name, i.e., it can't make a local branch that has another local branch as its upstream. If that were going to be the case, the two *
name
*s would be the same, so the branch by definition already exists, so it just checks out the local branch without creating anything.git checkout --track origin/name
creates a new branch namedname
and does set its upstream.git checkout --no-track origin/name
creates a new branch namedname
and makes sure it has no upstream.
So, as you can see, it's complicated! The exact method of creating the branch determines in part whether it has an upstream, with the rest of the determination being based on your branch.autoSetupMerge
configuration. This setting has three possible values:
true
: set the upstream when the starting-point is a remote-tracking name.false
: don't set the upstream.always
: set the upstream when the starting-point is either a remote-tracking name or a (local) branch name.
The default, if you did not set branch.autoSetupMerge
at all, is to pretend you set it to true
. So by default, all of these branch-creating options act like you said --track
if you give them an origin/*
name, or any other remote-tracking name, as their starting-point. That's true even if the starting point is merely implied, as in the:
git checkout develop
case where you don't have a local develop
but do have a remote-tracking origin/develop
: this creates your new local develop
from your remote-tracking origin/develop
, and if branch.autoSetupMerge
is true
—including if you haven't set it at all—now your develop
"tracks"—has as its upstream—your origin/develop
.
Summary
Some of your branches have upstreams set. Which branches have upstreams, and what those upstreams are, is up to you, controlled by your configuration and/or by your command-line options and/or by additional git branch --set-upstream-to
or git branch --unset-upstream
commands.
Those branches that do have upstreams set will get some useful features. If the upstream that is set "goes away" by whatever process, git branch -vv
will list your local branches and say that their upstream is "gone". Other Git commands will just pretend you unset their upstream. If the upstream comes back again, the upstream will resume its usefulness.
Deleting a local branch only because its upstream is gone is a mistake. Deleting it because you are done with it is fine. You don't need to have a local branch named develop
, or even one named master
, if you're done with them, even if they still exist as upstream origin/develop
or origin/master
names. You can just use origin/master
to refer to your Git's memory of the master
on origin
—you don't need to have your own master
at all.
Sometimes, you are done with a branch because of some underlying reason that also means that the upstream is going to be gone, or is already gone. In that case, deleting your local branch is fine—you're deleting it because you're done with it. The fact that it will be deleted on origin
, or already has been deleted on origin
, is irrelevant here!
answered 8 hours ago
torektorek
194k18242322
194k18242322
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55020899%2fgit-fetch-not-working-as-expected-gone-is-not-being-added-to-branch-descript%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I am also interested in this question. I have an unsatisfactory alias (to output local branches which lack a remote counterpart) that I'd love to replace one of these days (
git config --global alias.brloc '!git branch -vv | grep -v origin; git branch -vv | grep gone'
).– RomainValeri
15 hours ago