6

I've repo with long history so I cloned using

git clone <url> --depth 1

Now I'm in the master branch.

How do I fetch remote branch called 'feature/my-feature' that was branched 3 commits earlier?

I've tried:

git checkout -b feature/my-feature  origin/feature/my-feature

and

git fetch -all

but I still only see master branch

Liero
  • 19,054
  • 16
  • 100
  • 195

2 Answers2

11

TL;DR

You need either:

git config --add remote.origin.fetch \
    +refs/heads/feature/my-feature:refs/remotes/origin/feature/myfeature

or:

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

followed by any of:

git fetch
git fetch --depth=<number>
git fetch --deepen=<number>
git fetch --unshallow

Explanation

When you make a shallow clone, Git adjusts two things:

  • the default set of branch(es) or tag(s) to be obtained and updated; and
  • the depth of the initial fetch.

(Running git clone is shorthand, as it were, for doing a long sequence of Git commands that include git init, git remote add, git fetch, and git checkout. There may be a few more commands used, if necessary.)

In particular, --depth 1 has the side effect of adding --single-branch. If you do not also cancel this out with an explicit --no-single-branch, Git will configure the fetch line, as if you had run:

git config remote.origin.fetch +refs/heads/<branch>:refs/remotes/origin/<branch>

This tells later git fetch commands to bring over only the given <branch>, and no others.

A normal (non-shallow, non-single-branch) git clone uses the default setting:

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

which tells each later git fetch to bring over every branch.

Then, having configured all future fetch operations to be single-branch, the shallow clone fetches with your specified --depth, which inserts special "graft" entries using a file named .git/shallow. These affect the overall usefulness of the clone (but much less so in any modern Git); for more about this, see Is it safe to shallow clone with --depth 1, create commits, and pull updates again? For here, though, note that this does not retain the --depth number. It just cuts short the initial fetch. The behavior of future fetches, especially those that use a different reference name, is hard to predict from this.

If you run git fetch --unshallow, Git will find all the shallow cut-offs and re-fetch all history from before this point. Then Git removes the special .git/shallow file entirely, and the clone behaves like a non-shallow clone. This is independent of whether the clone behaves like a --single-branch clone.

If you run git fetch with a specific --depth or --deepen, Git will make new shallow cut-offs as needed to set the depth to the number you specify, or increase any existing shallow cut-offs by the number you specify. If there are multiple branches, the relationship between their various cut-offs is complicated (it's all based on graph walking and without code inspection or testing, it's hard to say how Git will behave in some tricky cases).

torek
  • 330,127
  • 43
  • 437
  • 552
  • 1
    Finally, an answer! This should have been the accepted one. Thank you for explaining it much better than other discussions regarding this. – ParkerD May 21 '20 at 20:56
2

You could try forcing it by specifying the fetch depth:

git fetch --depth=999999

depth is the number of commits. Note that when you did a clone, clone actually does a fetch and the depth is the same depth specified in your fetch.

Make that number bigger if needed!

EDIT

Also you can/probably-should use deepen - same syntax, but it effectively fetches from where your last specified depth left off.. so its a bit more efficient:

git fetch --deepen=999999

It might depend on your git version though for that last one....

EDIT 2

Just saw this while I was reviewing the git docs....

git fetch --unshallow

This would be the best way (not sure which version it is implemented in and I have not tried it.... but looks like the thing to use).

code_fodder
  • 12,697
  • 10
  • 68
  • 123