Git is a distributed version control system
Imagine two colleagues, one of them has lost access to his git account (or even more, doesn’t have any Internet access), lets see how a distributed version control system can help us …
Constraints
- Alice and Bob work at the same “Acme” company on the project “awesome-project”, they use github (it could be any other solution that supports git remote repos)
- Alice has a working workstation (access to Internet, she can push/pull to github with her account)
- Bob:
- He doesn’t have access to Internet (lost it)
- His account to push/pull to github has been disabled for some reason
- They have a usb drive they can share
Goal
In the next sections, we’ll see how, with the help of Alice, Bob will manage to:
- Retrieve the latest changes from github
- Continue to work in local
- Push his changes to github
Init
Create a bare repository
Alice will insert the usb drive in her computer. She will create a “bare” repo that will act as a remote repository (you will be able to push/pull on it).
Alice:~ $ cd /Volumes/USB_DRIVE
Alice:USB_DRIVE $ mkdir awesome-project.git && cd $_
Alice:awesome-project.git $ git --bare init
Initialized empty Git repository in /Volumes/USB_DRIVE/awesome-project.git/
Point a remote from your working repo to the bare repo
Alice will go to the working directory of “awesome-project” and point a git remote to the bare repo she just created on the usb drive.
Alice:~ $ cd awesome-project Alice:awesome-project $ git remote add usbdrive /Volumes/USB_DRIVE/awesome-project.git/
When listing remotes, she will see a new remote like this:
Alice:awesome-project $ git remote -vv origin https://github.com/acme/awesome-project.git (fetch) origin https://github.com/acme/awesome-project.git (push) usbdrive /Volumes/USB_DRIVE/awesome-project.git/ (fetch) usbdrive /Volumes/USB_DRIVE/awesome-project.git/ (push)
Retrieving latest changes
We’ll now see how Bob will retrieve the latest changes without needing:
- direct access to the remote repo via Internet (nor Internet access)
- neither having his git account active
Push local repo to usb drive remote repo
Say Bob has not been able to synchronize for a few hours (or a few days), Alice who has access to the remote repo will now:
- retrieve the latest changes of the repo from the remote
origin
- push those changes to the bare repo, on the remote
usbdrive
- then, in the next step, Bob will retrieve those changes
Alice:awesome-project $ git pull origin Already up to date. Alice:awesome-project $ git push usbdrive --all Enumerating objects: 6, done. Counting objects: 100% (6/6), done. Delta compression using up to 8 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (6/6), 492 bytes | 492.00 KiB/s, done. Total 6 (delta 0), reused 0 (delta 0) To /Volumes/USB_DRIVE/awesome-project.git/ * [new branch] develop -> develop * [new branch] master -> master
Note: Instead of git push usbdrive --all
, you can specify a specific branch to avoid pushing all your branches.
Pull from usb drive remote repo to local repo
Now, Alice can give the usb drive to Bob who will be able to pull the new changes from the remote repo on the usb drive, without needing any Internet access nor git account:
- Bob will plug the usb drive
- he will point his working directory to the local remote on the usb drive he just plugged, just like above
- then pull the changes on a specific branch
Bob:~ $ cd awesome-project
Bob:awesome-project $ git remote add usbdrive /Volumes/USB_DRIVE/awesome-project.git/
Bob:awesome-project $ git pull usbdrive master
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Volumes/USB_DRIVE/awesome-project
* branch master -> FETCH_HEAD
* [new branch] master -> usbdrive/master
Updating 3aff4f8..741ac86
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
Note: You need to specify the branch you want to pull so that git will track it (see at the end about tracking branches).
Pushing latest changes
We’ll now see how Bob will push the changes he just made in his local working directory to the remote repo on Internet via Alice, without needing:
- direct access to the remote repo via Internet (nor Internet access)
- neither having his git account active
Bob worked on his local working directory and made a few commits on a local branch called feature/from-bob
.
In order to make his work available to everyone, Bob needs to:
- push his local branch
feature/from-bob
to the usb drive remote repo - give the usb drive to Alice
- Alice will pull the
feature/from-bob
branch on usb drive remote repo to her local repo - She will then push the
feature/from-bob
branch from her local working directory to the remote repo - Then everybody will have access to the changes Bob made
Pushing from local working directory to usb drive remote repo
Bob pushes his local branch feature/from-bob
to the usb drive remote repo.
Bob:awesome-project $ git push -u usbdrive feature/from-bob
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 264 bytes | 264.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /Volumes/USB_DRIVE/awesome-project.git/
* [new branch] feature/from-bob -> feature/from-bob
Branch 'feature/from-bob' set up to track remote branch 'feature/from-bob' from 'usbdrive'.
Retrieving Bob’s changes from the remote repo on the usb drive
Bob gives the usb drive to Alice, she plugs it on her computer in order to pull the branch feature/from-bob
from the usbdrive
remote repo (on the usb drive, same as the first step when she pushed but this time, she is pulling from it to retrieve Bob’s work).
Alice:awesome-project $ git fetch usbdrive
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /Volumes/USB_DRIVE/awesome-project
* [new branch] feature/from-bob -> usbdrive/feature/from-bob
Pushing Bob’s work to github via Alice’s workstation
Alice has retrieved Bob’s branch feature/from-bob
on her local working directory, she will now push it to github (the remote origin
), making it available to everyone.
Alice:awesome-project $ git checkout feature/from-bob Branch 'feature/from-bob' set up to track remote branch 'feature/from-bob' from 'usbdrive'. Switched to a new branch 'feature/from-bob' Alice:awesome-project $ git push -u origin feature/from-bob Enumerating objects: 8, done. Counting objects: 100% (8/8), done. Delta compression using up to 8 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (6/6), 487 bytes | 487.00 KiB/s, done. Total 6 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'feature/from-bob' on GitHub by visiting: remote: https://github.com/acme/awesome-project/pull/new/feature/from-bob remote: To https://github.com/acme/awesome-project.git * [new branch] feature/from-bob -> feature/from-bob Branch 'feature/from-bob' set up to track remote branch 'feature/from-bob' from 'origin'.
Conclusion
This might appear as a twisted use case, the goal was to make you better understand distributed version control and remote/tracking branches.
- you create a tracking branch when you use
-u
which is short for--set-upstream
- tracking branches are local branches that have a direct relationship to a remote branch
- If you’re on a tracking branch and type git pull, Git automatically knows which server to fetch from and branch to merge into
You may be used to have only one remote (usually origin
), you have seen in the example we used two. You can have multiple remotes, so you can push to multiple remote servers.
A case you will come across often is when you fork a repo, you will have:
origin
as your main remote pointing to your forkupstream
(it could be named different) pointing to the original repo so that you can sync up