No Git repository is "special" or "more important" than any other Git repository, unless you deem it so yourself.
When you git clone ssh://main-PC/...
on Laptop A, the Git you run on Laptop A creates a Git repository that is a peer of the Git on main-PC
. You only think of the main PC version as "more master-y" because that's how you use it, and in fact, while traveling, the laptop version is "more master-y" because it gets new commits.
Your laptop-A repository uses the name origin
to remember the URL ssh://main-PC/...
. The name origin
is a remote. Your laptop-A Git uses remote-tracking names of the form origin/*
to remember branch names in that other Git.
On your laptop-B, you can either clone your laptop-A clone, or your main-PC clone. Your laptop-B will remember whatever URL you use here under its name origin
, and use remote-tracking names origin/*
to remember branch names copied during the clone process.
You can add more remote names to either laptop. For instance, assuming laptops A and B both ran git clone ssh://main-PC/...
, so that on both, origin
means ssh://main-PC/...
, you might want to do this on laptop A:
git remote add laptop-b ssh://ip.address/path/to/repo.git
for instance (of course the IP address may change over time, so you'll need to remove and re-add, or use git remote set-url
, to fix it sometimes). Now laptop-A has a name for laptop-B, and you can git fetch
or git push
from A to B to obtain (fetch) or send (push) any commits that are on B but not A (fetch) or A but not B (push). Similarly, on laptop B:
git remote add laptop-a ssh://ip.address/path/to/repo.git
will create the remote name laptop-a
on laptop B, and now you can git fetch
or git push
using this name.
Commits that are on one laptop are now easily transferred to the other. Note that the remote-tracking name, laptop-b/master
on laptop A for instance, is how the one laptop will remember that the other laptop has some commit—so now you'll want to have whichever laptop is "behind" update its local branch name(s) to remember the new commit(s).
The key notion is that all three repositories are peers. None has any special status, except insofar as you personally decide that one has some special status. Instead of making a static decision, you can decide based on commit hash IDs as recorded under remote-tracking names like origin/master
, laptop-a/master
, and laptop-b/master
.
The branch names in each of these peer repositories are different. The commit hash IDs are the same, as long as they all have the same commits. When one repository lacks some commit(s), git fetch
will yank them in (from the repository that needs the commits, to the one that has them), and/or git push
will send them (from the repository that has the commits, to the one that needs them).
These two operations—fetch and push—are almost symmetric, except for one other key difference. The fetch operation takes the source repository's branch names and renames them, so that they come in under remote-tracking names like origin/master
. The push operation sends the commits, then either:
- asks the target of the push to please set some of its branch names (this is the polite non-forced push), or
- commands the target of the push to set one some of its branch names (a forced push).
Because the target of a push is setting its branch names, several special conditions hold:
- The branch must not be checked out into a work-tree. This works well if the repository is one made with
--bare
as it never has any branch checked out into a work-tree.
- Or, the branch can be checked out into a work-tree, but then the Git receiving the push must be told to allow such push operations (and other conditions must hold). See, e.g., Git receive.denyCurrentBranch updateInstead fails [Edit: or your better link, What is this Git warning message when pushing changes to a remote repository?.
For these reasons, it's sometimes nicer to use an all-fetch work-flow; but then you have to use a second Git command to combine the new commits into the local repository. Many people like to use the git pull
command for this, as it actually runs git fetch
first, then runs a second Git command to incorporate the fetched commits.