I have all of my data on my home PC in a git repo.
This home PC (and therefore the git repo) is only accessible from the local LAN that the home PC is on. Putting the git repo on an internet-accessible cloud server, or making my home PC accessible on the internet, is not an option. This limitation is important to mention for the below question, because otherwise the obvious solution is "just put your git repo on a cloud server".
I have a laptop I travel with called Laptop-A. Before I leave on my travels, I check out the repo on Laptop-A from my home PC by using: git clone ssh://main-PC/... During my travels, I make local changes on Laptop-A, and I commit them locally, but I can't push them since the main repo on my home PC is not accessible via the internet. My changes can only be pushed when return home and am on the same LAN as my home PC.
This all works, but now I'm adding Laptop-B while traveling. This laptop also has the main repo cloned, just like Laptop-A does. In fact, the two laptops are basically identical but they are two separate machines. I'm doing this in case one of the laptops has a hardware malfunction. In that case, I still have the other laptop.
However, now I run into the problem that for the two laptops to truly be redundant, when I make a local change to a git repo on one laptop, that change is not reflected in the git repo on the other laptop, since they are obviously two separately checked out copies of the repo. I could commit each change manually and separately on each laptop, but that is awkward and I could make a mistake leading to inconsistencies. Even if I do it correctly, it's annoying and will lead to merge conflicts once I want to push from both laptops when I return home.
What I want to do is commit a change on Laptop-A and then have this synced to Laptop-B, so that I can push (to the main repo) from either laptop once I return home.
How can I accomplish this?
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:
Because the target of a push is setting its branch names, several special conditions hold:
--bare
as it never has any branch checked out into a work-tree.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.