aRtoo
aRtoo

Reputation: 1892

I can't checkout a branch in Git after fetching branch from remote

When I do a git fetch --all I can see my branch being fetch from remote it display like this * [new branch] <branch name> -> origin/<branch name> and then when I checkout with the command git checkout <new branch> nothing happens.

So I checkout from the origin with the command git checkout origin/<branch name> but the weird things is that it's saying I'm on different head but I'm still on master branch with a different head? (I'm confused here too).

Actual message:

HIT-STORE on master <----- see this im still on master
gco origin/588-feature-message-project-list-view  <---- this is the branch I want to checkout
HEAD is now at 11faa7cb fixes #584: member sign up <-- instead I got this!

I used to just do this.

  1. create a branch in gitlab
  2. run command git fetch --all
  3. git checkout <new branch>

That's it, then I have my branch in my local.


I also tried doing git branch --track <new branch> origin/<new branch> and it will fix it. But next time if I create a new branch I will still get the same problem.

I also tried following this link

git config --get remote.origin.fetch
+refs/heads/*:refs/remotes/origin/*

I also tried removing and adding the remote but nothing happens.


Here's my Git config too:

enter image description here

I just added that ff = only because I had some warning from Git. if remove that I'll get a warning about pulling.


Update

FYI this is not the same as checking out a branch. I have an issue when checking out a branch.

So when I create a branch on Gitlab I have to do these steps before:

  1. git fetch --all
  2. then git will update my remote origin to have the new branch
  3. then I have to simply checkout a branch like what I used to before with the command `git checkout <the_new_brach>
  4. and I can work on that branch.

This is the problem. I create a branch on Gitlab.

  1. do git fetch --all
  2. then git will update my remote origin to have the new branch
  3. Here's the problem part when I do a git checkout git checkout new_branch. I will get this error. error: pathspec 'new_branch' did not match any file(s) known to git. So I said ok. maybe I need to checkout from origin. so I run the command. git checkout origin/new_branch then I get this HEAD error/warning:
Note: switching to 'origin/new_branch'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 8a55a856 Merge branch 'hotfix-branch-base1'

After getting that error/warning when I run cat .git/HEAD I get a SHA:

$ cat .git/HEAD                                    
8a55a856dd9a9756533158bf28a98bab81b0d0c6

I never encountered that before. now to solve this I have to run the command. git branch --track new_branch origin/new_branch every time I checkout the a branch. this is annoying running every time i checkout a branch.

Upvotes: 0

Views: 2518

Answers (2)

torek
torek

Reputation: 487755

It's not clear to me from your question (before or after the edits) exactly what's causing the problem, but here is some background and my own guess:

  1. Git has multiple kinds of names: branch names, tag names, remote-tracking names, and so on. These names live in name spaces, with branch names technically being names that start with refs/heads/. You can always spell out one of these names completely: refs/heads/X means branch X and refs/tags/X means tag X, even if you have a branch and tag that share shortened names. The name X will mean whichever of those it matches: branch X if you only have a branch named X, or tag X if you only have a tag named X.

    In general, humans like to shorten their names. We see that with names like Robert turning into Bob, Christine becoming Chris, and so on, so this isn't peculiar to Git or computers, even. But if it's ambiguous—as at a party where everyone is named Bruce—we can always use some longer, unambiguous name, such as Bruce Bruceson.

  2. git checkout (and, since Git 2.23, git switch) will create a new branch name under certain circumstances. This creation is referred to as "DWIM mode" internally, with DWIM standing for Do What I Mean (as opposed to what I say). There is a --no-guess option to git checkout and git switch to disable DWIM mode, though it's only properly documented in git switch at the moment. An upcoming Git update will improve the documentation and add a configuration setting to enable or disable guessing by default.

The certain circumstances part of item 2 is probably where things are going wrong. Before Git version 2.23, you must pay very close attention to this part because it can destroy work you've done. This is fixed in Git version 2.23, where Git detects one of these ambiguous cases and warns you to use a more specific command.

(I recommend upgrading if possible. The work-destroying case is pretty rare, for multiple reasons, but it's also pretty nasty when it happens. Having Git stop and make you disambiguate your intent is a very good thing.)

The circumstances

Suppose you run:

git checkout abcd

The string abcd can be interpreted in four different ways:

  • It could be a short version of a branch name, especially if you have a refs/heads/abcd, which is a branch name by definition as it starts with refs/heads.

  • It could be a shortened hash ID. The hash ID is the true name of any Git commit, or any internal Git object. Hash IDs are currently1 expressed as 40-character hexadecimal strings like e1cfff676549cdcd702cbac105468723ef2722f4. They can be shortened to as few as the first four characters, provided this is unambiguous (and you simply use more characters if it is ambiguous). Note that branch names that have any character not in the [0-9a-fA-F] set cannot be misinterpreted this way: the name beef might be a shortened hash, but beer is definitely not a shortened hash.

  • If there's no existing refs/heads/abcd and it's not a shortened hash ID, it could be a request to create abcd, using the guessing DWIM mode.

  • Finally, it could be the name of a file or directory, or more formally, a pathspec, that you wish git checkout to extract. In my opinion, this is the dangerous case. This was one of the reasons that git checkout was copied out into two separate commands, git switch and git restore, in Git version 2.23: git switch will never interpret abcd as this last case; only git restore will interpret it this way.

In the DWIM case (and only in this case), Git will search through your remote-tracking names.2 These are the names that begin with refs/remotes/. Your Git copies some other Git's branch names to your own remote-tracking names, so that if they have master and develop, you wind up with origin/master and origin/develop, providing that you call the other Git origin.3 If your Git finds exactly one suitable remote-tracking name, your Git pretends that you wrote:

git checkout -b <branch> --track <remote>/<branch>

and thus does what you meant, rather than what you said to do: DWIM!


1Git is moving towards using SHA-256 instead of SHA-1, and then hash IDs will be 64 characters instead of 40, so as to hold 256-bit numbers instead of 160-bit numbers.

2Git calls these remote-tracking branch names. They are copied from branch names but they do not act as branch names, and I find it less confusing to just drop the word branch from the name.

3Since origin is the default name for "the other Git" when there's just one other Git, and most people work with one other Git, that's usually the only remote and hence the only set of remote-tracking names: origin/*.


When they don't apply

Your Git won't use DWIM mode when:

  • There is no remote-tracking name that matches. There are simply no candidates, so this just can't happen.

  • There are two or more remote-tracking names that match. This can only occur if you have two or more remotes, such as both origin and upstream.

    Suppose you do not have a branch named beer and you run git checkout beer. Your Git can't interpret that as a branch name because you don't have a branch name beer; your Git can't interpret that as a shortened hash ID because r is not a valid hexadecimal character. But you have an origin/beer. That's a candidate for creating your own beer. But if you also have an upstream/beer, well, now you have two candidates. That's too many, and DWIM won't apply.

  • One of the other cases matches first.

I think this last one is the case that is biting you here. Let's say you are running git checkout dev. You clearly don't have an existing branch named dev, because if you did, you'd get the message about switching to it, and you would no longer be on master. So it's not that case. The letter v is not a valid hexadecimal character, and three letters—dev—is too short for Git to try it as a shortened hash name, so it's probably not that case either.

But what if you have a file, or directory, named dev? Then:

git checkout dev

is being interpreted as:

git checkout -- dev

which means replace the work-tree contents of dev (or dev/*, if dev is a directory) with the contents from the Git's index. This functionality is not present in git switch as it was put in git restore instead, to avoid accidentally clobbering un-git add-ed work.

The reason I think it's this case is that if you don't have a branch to check out and there's no other match so that Git tries to do the DWIM trick and that fails for any reason, then, after DWIM failure, you get an error message. You are not getting an error message, so the checkout must be succeeding.

What to do about it

You have two options:

  • Don't use that name. Branch names are not actually important to Git, so you can use some other spelling. You might have some sort of outside-of-Git constraint that makes you really want to use that spelling, though.

  • Make the command unambiguous.

To make the command unambiguous, you have multiple options:

  • Use -b, as in Christoph's answer. You can add an explicit --track, as in my cut-and-paste from the git checkout documentation, or --no-track if you want the new branch not to have an upstream set.4

  • Use git checkout --track origin/dev. Without the -b option, Git will, in effect, reverse the DWIM logic and turn the remote-tracking name origin/dev (i.e., refs/remotes/origin/dev) into the branch name dev (i.e., refs/heads/dev).

  • Use git checkout name --. The -- tells Git that this is definitely not a pathspec. (I'm not a big fan of this particular method, but that's more a matter of taste than anything else.)

  • Use git branch to create the branch name, then a separate git checkout. This sidesteps all the DWIM stuff as git branch always knows that it is dealing with a branch name. The drawback, of course, is that it takes extra steps.

  • Use any other multistep method, such as checking out the commit as a detached HEAD followed by git checkout -b to create a branch name.


4I don't know why you might want that, but it's available as an option.


Why git checkout origin/dev results in a "detached HEAD"

Git has two normal operating modes, which Git calls attached HEAD and detached HEAD. The detached HEAD mode is really meant for special purpose operations, such as while you're in the middle of an ongoing rebase that requires resolving conflicts manually. You can use it for ordinary everyday work, but that requires a certain minimum of masochism.

In attached HEAD mode, Git's special name HEAD—which should always be written in all-uppercase like this5—is attached to a branch name. Git uses this to enable HEAD to find the current commit through that branch name, and to automatically update that branch name when making new commits. But because only branch names are supposed to automatically update like this, Git won't attach the special name HEAD to anything that is not a branch name.

The name refs/remotes/origin/dev, or any similar remote-tracking name, is simply not a branch name. (See footnote 2 again.) Git won't attach HEAD to one of these names. So git checkout origin/dev, or anything similar, results in the same detached HEAD mode as git checkout hash-ID. In Git 2.23 and later, the git switch command requires that you add the --detach option when doing this sort of thing, to make sure you know what you're doing, but git checkout just blithely assumes that you know what detached HEAD mode is about, and at most, prints a warning.


5All-lowercase or mixed spellings like head and HeAd sometimes work on some file systems, typically on Windows and macOS, at the moment, but there are two problems: a future release of Git may make this stop working at all, and it sometimes doesn't work anyway. If you are in an added work-tree, the lowercase spellings refer to the main worktree's HEAD, rather than the added work-tree's HEAD. In any case it's a bad habit to get into. If you find the name HEAD too long, consider using its synonym: the single character @. Since a late Git 1.8 or so version, @ works everywhere that HEAD does.

Upvotes: 1

Christoph
Christoph

Reputation: 7063

I guess you are looking for git checkout -b new_branch origin/new_branch. To ckeck, run

git fetch --all
git branch -av

Before a checkout, new_branch does not exist locally...

Upvotes: 0

Related Questions