Tag Archives: git

Using git offline – git disconnected

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 fork
  • upstream (it could be named different) pointing to the original repo so that you can sync up

My custom git prompt on Mac OS X – v1

Credits

This post is mainly a reminder for future me when I’ll make this configuration again, or share it with other people. I followed those two resources from Christophe Porteneuve, that I recommend you to read if you’re into git :

Why a custom git prompt ?

It’s been a few months since I’ve been using this custom git prompt and I would not leave it for anything :

  • It saves me time
  • It saves me mistakes
  • I get instant access to infos such as :
    • active branch
    • status (untracked files remaining / modified files / stashed files …)
  • git commands completion

Disclaimer

This is about my own current git prompt config on Mac OS X, please take a look at the links I put on the credits section, so that you could have it your own way and make your very own git config.

Configuration on Mac OS X

This post is only about Mac OS X, we’ll need to upgrade the git version shipped with the OS (git 1.9.3 on Yosemite) to the latest stable one : (currently git 2.2.1), to do that, I’ll use Homebrew (feel free to use your own package manager or any other means).

Upgrade git :

$ brew update
$ brew install git

Check the git version in an other prompt : git --version
If not correct (if brew doctor is ok, you shouldn’t have to), run the following command (you may be using an other file than ~/.bash_profile, according to your shell) :

$ echo 'export PATH="/usr/local/bin:/usr/local/sbin:~/bin:$PATH"' >> ~/.bash_profile

Install bash-completion :

$ brew install bash-completion

nano ~/.bash_profile , add the following inside the file :

#bash-completion
if [ -f $(brew --prefix)/etc/bash_completion ]; then
 . $(brew --prefix)/etc/bash_completion
fi
#git-config
#export PS1='\u@\h:\w$(__git_ps1) \$ '
export GIT_PS1_SHOWDIRTYSTATE=1 GIT_PS1_SHOWSTASHSTATE=1 GIT_PS1_SHOWUNTRACKEDFILES=1
export GIT_PS1_SHOWUPSTREAM=verbose GIT_PS1_DESCRIBE_STYLE=branch
export PROMPT_COMMAND='__git_ps1 "\u@\h:\w" " \\\$ "'
export GIT_PS1_SHOWCOLORHINTS=1

For better logs : nano ~/.gitconfig (it may already exist, if not create it) and add the following (source) :

[alias]
 st = status
 ci = commit
 lg = log --graph --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ar)%Creset'
 oops = commit --amend --no-edit

Go to a local git repo and see the magic of your new git prompt !

Other resources :

Update : My gitconfig and some more infos (like how to setup a visual diff/merge tool)