Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get contents of a file from another branch without checking it out

I am trying to get the content of a file from another branch.
I am doing the following:

git checkout branchB some_specific_file.cc  

The current branch stays unaffected. I suspect the reason is that even though I have done a git pull I have never checked out branchB locally.
Is there a way for this command to work if I have just done a pull in the repo but never actually checked out the other branch?
Is there something equilalent to:

git pull  
git checkout branchB  
git checkout branchA  
git checkout branchB some_specific_file.cc 

without actually having to switch to branches so the last command works?

like image 304
Jim Avatar asked Apr 06 '18 15:04

Jim


People also ask

How do I pull the contents of a git branch?

git pull = git fetch + git merge Their Git lists their branch names, and which commit hash IDs go with those branch names. Your Git then makes sure you have those commits, and once you do, sets up your origin/* names to remember those hash IDs.

How do I pull changes from another branch without merging?

You could use git checkout from the branch you want to transfer the changes to: git checkout <branch name> . That will change all files to match the version in the desired branch. Then you can commit, change, discard whatever you want.


1 Answers

TL;DR

I think what you want is:

$ git fetch
$ git checkout origin/branchB -- some_specific_file.cc

Background

The fundamental error you are making here is thinking that branches mean something. :-) Or rather, that a branch name has some sort of global meaning—but it doesn't!

The git pull command is meant as a convenience operation: it first runs git fetch, then it runs a second Git command. The default second command is normally git merge, but (a) you can change this and (b) there are some corner cases. The reason this is supposedly (but not actually) convenient is that git fetch only obtains new commits. It does not affect any of your branches (which are yours, they do not belong to some remote like origin!).

Typically, after you have obtained new commits from some remote like origin, you will want to incorporate (some of) those commits into (some of) your own branches. To do that, you need a second Git command, such as git merge or git rebase. There are many small issues here though, such as:

  • How do you know whether to merge, rebase, or do something else entirely?
  • What if you want to operate on more than one of your branches?

The git pull convenience command casts all of these aside and assures you that, no matter what git fetch did, you're 100% sure that an immediate git xxx—you fill in the xxx part before you fetch—is the right answer! If it's not—which actually turns out to be very often, in my experience—then git pull is the wrong command.

What you're doing above

Your sequence of these two commands above:

git pull
git checkout branchB

will, if you do not yet have a branchB, create for yourself a new (local) branch name branchB, pointing to the same commit as your existing remote-tracking name origin/branchB. Then:

git checkout branchA

gets you back on your (presumably existing) branchA, about which we have more to say in a moment. The final command:

git checkout branchB -- some_specific_file.cc

then extracts that specific file from the commit identified by the name branchB. (I added the -- here—it's a good idea to use it by reflex, in case a file name resembles a git checkout option or branch name; some_specific_file.cc won't, so it's safe either way.)

git pull = git fetch + git merge

The git fetch step has your Git call up another Git, typically at the URL you have stored under the name origin. Their Git lists their branch names, and which commit hash IDs go with those branch names. Your Git then makes sure you have those commits, and once you do, sets up your origin/* names to remember those hash IDs.

The origin/* names are what I call remote-tracking names; Git calls them remote-tracking branch names. They remember, for you, in your own Git repository, where the remote's branch names were, the last time your Git talked with their Git.

Hence, since git pull runs git fetch, this has the side effect of updating your remote-tracking names. But there's a problem: git pull, in its effort to be convenient, limits the set of names that git fetch fetches.

Normally git fetch fetches all their branch names, updating all of your corresponding remote-tracking names. When run from git pull, though, Git looks at your current branch's so-called upstream setting. Typically the upstream of master is origin/master, the upstream of branchA is origin/branchA, and so on. These are your names for their branches. When git pull runs git fetch, it says: only update this one remote-tracking name.

What this means in the end is that git pull while not on branchB won't update your origin/branchB, and that's (eventually) a big problem. You will need to reverse the order of the commands: check out branchB first, then pull. (Or, better, avoid git pull, but hold on a moment.)

Creating a (local) branch

The git checkout command will switch you to some existing branch that you already have:

git checkout master

for instance will switch you to master, which you probably already have. But if you don't have it yet, git checkout will scan your origin/* names—the remote-tracking (not-exactly-a-branch) names—to see if there's one that matches after taking away the origin/ part.

If so, your Git will create a new local branch name that has the origin/ version of itself as its "upstream".

git merge operates always and only on the current branch

The last step of git pull is normally to run git merge. If you run:

git checkout branchB
git pull

this means that Git should:

  • check out your existing branchB, or create it if necessary from origin/branchB;
  • fetch and update origin/branchB (only);
  • run git merge to update your current branch—branchB—using its upstream, origin/branchB, which git fetch just updated.

The merge step will then make your local branch branchB get updated.

So in general, you have to git checkout branchB before you git pull so as to make sure that origin/branchB is the (single) remote-tracking name that the fetch updates, and then your own local branchB is also updated.

You don't need that

But you don't need any of that for your task. If you just run git fetch, which updates all your origin/* names, and then use origin/branchB to identify the commit that contains the version of some_specific_file.cc that you want, you're good. Hence the final set of commands I suggest at the top.

like image 139
torek Avatar answered Sep 21 '22 02:09

torek