Home > Software engineering >  Get branch on Origin not on local
Get branch on Origin not on local

Time:02-10

I ran in to a situation where I was working on two separate machines and ended up with the following:

Laptop A:

  • main
  • branch_a
  • branch_b

Laptop B:

  • main
  • branch_c

Origin:

  • main
  • branch_a
  • branch_b
  • branch_c

I am now done working on Laptop B and will continue working on Laptop A. How do I go about getting just branch_c locally on to my Laptop A and then working from there only?

CodePudding user response:

Not entirely sure I understand the question, but if you simply want to access branch_c on laptop A, here are the instructions.

First you must fetch, to get the latest on the remote server. Then checkout branch_c.

run the following:

git fetch
git checkout branch_c

that is it

CodePudding user response:

Solving this "properly" (for whatever definition you want to use for "properly") requires understanding Git's names. Branch names are just a specific subset of Git's names, which also include tag names, remote-tracking names, replacement names, internal names for bisection, and many others.

These names exist—or mostly exist, depending on whose definition you use—as one or another form of reference or ref. A ref is, or mostly is, a name that starts with refs/ and goes on to include another component, such as stash or heads/ or tags/ and so on. Each slash separates a component, much as with a Unix/Linux style path name: the only thing that's special here is that all of these refs start with refs/.

The exception, if you choose to define it this way—some parts of Git do and some don't—is that there are some special refs like HEAD, MERGE_HEAD, CHERRY_PICK_HEAD, and so on, that don't start with refs/. Some Git documentation considers these ordinary refs, and some doesn't, but the important thing is that they do exist yet don't start with refs/. Only the ones that do start with refs/, plus the special name HEAD, are allowed to "escape" from your Git repository to be seen by some other Git repository.

In other words, every Git repository has its own set of refs. But, when you connect one Git repository to another Git repository—which you do with git fetch or git push—you may allow your names to be seen by the other Git repository, and the other Git repository may allow its names to be seen by your Git repository.

Meanwhile, each name stores one hash ID. That's what they're good for: storing one hash ID. Git needs the hash IDs to find commits. Git does not need the names! It only needs the hash IDs. If you can supply the hash ID, without the name, that's sufficient.

You should thus hold the following picture in your head

Each Git repository has:

  • commits, which have hash IDs;
  • some names—i.e., refs—that let you, a human, tell Git a hash ID without you having to memorize the hash IDs yourself.

When you use git push or git fetch, or git pull which starts by running git fetch, you are telling your Git to connect to another Git repository. That other Git repository also has commits (which have hash IDs) and names. Your Git may look at their names and get commits from them—that's the git fetch operation—or may send commits to them and ask them to set some of their names: that's the git push operation.

Those names that start with refs/heads/ are branch names, and these are the names you use most often. Git allows you (the human) to leave off the refs/heads/ part, and will stick that part on automatically as needed to find the commit locally. You can explicitly write out refs/heads/ to force Git to use something as a branch name, if needed, but usually we just let Git do its thing and are lazy and type in main and not refs/heads/main.

Fetch and push are different

When you use git push, you will run it like this at least once:

git push -u origin somebranch

The origin here is the name you are using to store a URL, by which your Git can call up some other Git repository over the Internet. The somebranch part is short for refs/heads/somebranch: we let Git fill in the refs/heads/ for us.

Your Git uses this to find your latest commit for your somebranch branch. Your Git then makes sure their Git repository has this commit and all the commits needed to lead up to that commit too, and then—once your Git is sure they have them, first sending over those commits and anything else needed—your Git will ask their Git to create or update their refs/heads/somebranch name. That way, your Git and their Git will both have branches named somebranch, and both branch names will refer to the same commit.

It's the commits that matter to Git—not the branch names, just the commits! The branch names only matter to humans. But we want Git to serve us, not the other way around, so we want the branch names to match too.

Once you've done one git push -u like this, you can just run:

git push

Your Git will figure out that your current branch is somebranch, and use its upstream setting—that's what the -u is for with git push -u origin somebranch, to set the upstream setting—to do git push origin somebranch for you. Sometimes, creating a branch name in your repository automatically sets its upstream and then you can use git push without the -u. Sometimes it doesn't, and you need the git push -u, or git branch --set-upstream-to, or some other method to set the branch's upstream.

But git fetch is different! When you run git fetch you should usually just run it by itself, or with the name of the remote origin:

git fetch origin

This has your Git call up the other Git repository, using the URL stored under the name origin as before. But this time your Git is getting stuff from them. Your Git asks them to list out all their branch names. They do that, and then your Git takes each of their names and changes them. Your Git turns their main—which is, strictly speaking, refs/heads/main—and turns that into your origin/main, which is short for refs/remotes/origin/main. Your Git turns their branch_a, if they have one, into origin/branch_a. This repeats for every branch name they have: every one of their refs/heads/* names becomes a refs/remotes/origin/* name.

Having made these changes, your Git also gets from them any commits they have, that you don't, that you'll need to create-or-update all your refs/remotes/origin/* names. And, once you have all these commits and supporting objects, your Git will then create or update all these names.

These refs/remotes/origin/* names—which your Git lets you abbreviate as origin/main and so on—are your remote-tracking names for the remote named origin. Because they include the word origin in their name, they're separate from any other remote-tracking names.

Adding more remotes

This separation means you can add more remotes. Running:

git remote add computer1 <url>

allows you to run:

git fetch computer1

(assuming there's some URL by which your current computer—computer0?—can reach computer1). That will let your Git get, from "their" (your-on-computer1) Git repository, all of their commits and all of their branch names. Your Git will now create-or-update refs/remotes/computer1/* in your repository.

What if you can't log in from one of your computers to another?

What if you can't actually log in to your laptop-A to your laptop-B? For instance, if they're both running Windows, they probably have no server-like capabilities.1 In this case, you'll need to use git push from the laptop that has the branch name, to send the branch to some hosting server that both laptops can reach. This will also send any commits needed.

Then, once the commits and branch name exist where you can get them, you run git fetch to there. That gets the commits and creates a remote-tracking name, such as origin/branch_c or laptopB/branch_c, locally. Now you have a remote-tracking name on laptop A that finds the right commit, which you also have on laptop A. So now you can create a branch name on laptop A:

git branch branch_c origin/branch_c

for instance.

If you were able to start sshd directly on laptopB and created a laptopB remote and ran git fetch laptopB to get laptopB/branch_c on laptop A, you can run:

git branch branch_a laptopB/branch_c

for instance.

Note that these git branch commands create branch_c locally, here on laptop A, with an upstream set. The upstream that is set is origin/branch_c or laptopB/branch_c, whatever your second argument was. If you want to change the upstream, just remember to run git push -u later as needed. The -u option tells git push: If this push works, please run git branch --set-upstream-to for me, with the arguments to the git branch --set-upstream-to command setting the upstream of the current branch to whatever branch you just created-or-updated on whichever remote you just pushed-to.


1If they're both running Linux, or even just one of them is, you can start up an sshd on the Linux laptops and use ssh access, which will usually be the way to do what you want.

  • Related