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.