Doing git Dev on Two Machines

I prefer coding on my workstation (30” + 24” LCDs, fast, great chair, etc…).  But sometimes want to hack on my laptop (an MBA) when I’m out and about.

Here’s my workflow.

I am posting this for two reasons:

  1. It’s complex enough that regularly forget how to do it. I rely on random Bing searches to remind myself. This way it’s all in one place.
  2. I’m not sure I’m doing it right, or if there’s an easier way. If people provide comments I’ll update the workflow accordingly.

Machine Setup

The first thing is to get the machine setup. These days, because MileLogr is an ASP.NET app, I do most dev within Windows.  Once you are past machine setup the rest of the workflow is basically the same.  I use the tools outlined in my post on Coping with the OSS Command Line on Windows to make my Windows workflow work.

In short I install & configure Chocolatey (a Windows tool/package manager that makes installing all this stuff super-easy) and then, in turn use that for installing all the stuff below, from a elevated cmd.exe.  Someday I’ll write a cmd script that does this, but for now I just type each command, e.g.:

C:\>cinst sublimetext2

I do this for each of the following:

Of course, I also have Visual Studio and ReSharper installed.

Once all of the above is installed and configured I use a PowerShell prompt within ConEmu; I rarely ever go back to cmd.exe.

A big part of setting up a machine is dealing with SSH keys. I use both github and bitbucket.org so I need multiple SSH keys.

Generate a RSA SSH key

C:\Users\charlie\.ssh> ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/charlie/.ssh/id_rsa): foo
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in foo.
Your public key has been saved in foo.pub.
The key fingerprint is:
...

Take the resulting foo.pub file and add its contents to your SSH keys on github or bitbucket.

Add each key to ssh

C:\Users\charlie\.ssh> ssh-add .\foo
Enter passphrase for .\foo:
Identity added: .\foo (.\foo)

Setup .ssh/config

To support multiple git hosts (e.g. github, bitbucket, Azure Web Sites, etc…) on a single machine use a .ssh/config file. Here’s mine (with foo added):

C:\Users\charlie\.ssh> type .\config
Host          tig
HostName       github.com
User           git
PreferredAuthentications publickey
IdentityFile ~/.ssh/tig_rsa
Host          heroku
HostName       heroku.com
User           git
PreferredAuthentications publickey
IdentityFile ~/.ssh/heroku_rsa
Host          sourceforge
HostName       sourceforge.net
User           git
PreferredAuthentications publickey
IdentityFile ~/.ssh/sourceforge_rsa
Host          wpengine
HostName       git.wpengine.com
User           git
PreferredAuthentications publickey
IdentityFile ~/.ssh/wpengine
Host          bitbucket
HostName       bitbucket.org
User           git
PreferredAuthentications publickey
IdentityFile ~/.ssh/bizlogr
Host          foo
HostName       github.com
User           git
PreferredAuthentications publickey
IdentityFile ~/.ssh/foo

Cloning a repo

To use git with the foo credentials, replace the hostname in the git clone command with the Host from the .ssh/config file above.

E.g. Assuming I added the foo.pub public key to my github account (tig), to create a clone of tig/jOdometer, github says to use this:

git clone git@github.com:foo/jOdometer.git

Instead, to ensure the right SSH connection is used, I would do this:

git clone git@foo:tig/jOdometer.git

Don’t forget to setup ssh-agent so you don’t have to enter your pass phrases each time you run a command.

Syncing a Branch

The general workflow Stefan and I are using for MileLogr is derived from this post:

http://nvie.com/posts/a-successful-git-branching-model/

This means that whenever I’m doing any work on MileLogr I’m doing it in a feature branch.  Our model is feature branches are generally per-developer and, if I weren’t trying to sync between two machines I probably would just keep them local (although making them remote has the benefit of providing backup).

Note we follow this guys advice and have two categories of branches: public & private. Public branches have a history that is “immutable, atomic, and easy to follow.”  Private branches are disposable and malleable. Master and develop are public.  Feature branches are private.

http://sandofsky.com/blog/git-workflow.html

Here’s the workflow.  Desktop is my desktop that I’m currently working on, where I’ve made changes I want available on my laptop.

Create feature branch

Let’s say I’m making some changes for a feature called demo. Below I create the feature branch and made a change to an existing file:

On my Desktop:

E:\code\milelogr [master]> git checkout -b demo
Switched to a new branch 'demo'
E:\code\milelogr [demo]> subl .\MileLogr\Views\_ViewStart.cshtml
E:\code\milelogr [demo]>
E:\code\milelogr [demo +0 ~1 -0]>

(Note, see how my prompt shows my git status? That’s posh-git at work. Love it!)

Committing and pushing work in progress

Now, let’s say I’m in the middle of hacking away and I need to run. But I want to be able to pick up where I left off on my laptop. I need to commit these changes and push them to a remote so I can pull them down to my laptop:

E:\code\milelogr [demo +0 ~1 -0]> git add MileLogr/Views/_ViewStart.cshtml
E:\code\milelogr [demo +0 ~1 -0]> git commit -m "work in progress"
[demo 5d1fcd1] work in progress
1 file changed, 1 insertion(+)
E:\code\milelogr [demo]> git push origin demo
Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 442 bytes, done.
Total 5 (delta 3), reused 0 (delta 0)
remote: bb/acl: bogus is allowed. accepted payload.
To git@bitbucket.org:bogus/milelogr.git
* [new branch]      demo –> demo
E:\code\milelogr [demo]>

Now, if you look on the remote (in my case on bitbucket.org) you’ll see the repo now has a new branch named demo.

Pulling a private branch

On the laptop, it’s now easy to pull this branch down.

On Laptop (note different path):

C:\Users\Charlie\code\milelogr [master]> git pull
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 5 (delta 3), reused 0 (delta 0)
Unpacking objects: 100% (5/5), done.
From bitbucket:bogus/milelogr
* [new branch]      demo       -> origin/demo
Already up-to-date. C:\Users\Charlie\code\milelogr [master]> git checkout demo Branch demo set up to track remote branch demo from origin. Switched to a new branch 'demo' C:\Users\Charlie\code\milelogr [demo]>

I can make changes on the laptop and when I’m done I can commit and push them by specifying the remote origin with the branch:

C:\Users\Charlie\code\milelogr [demo +0 ~1 -0]> git add MileLogr/Views/_ViewStart.cshtml
C:\Users\Charlie\code\milelogr [demo +0 ~1 -0]> git commit -m "more work in progress" 
C:\Users\Charlie\code\milelogr [demo]> git push

I could setup tracking for a branch with

git branch --set-upstream demo origin/demo

and then subsequent git pull operations would just work. Or I can just

On Desktop:

E:\code\milelogr [demo]> git pull origin demo 

Integrating feature into develop

When it’s time to integrate this feature branch (demo) into our develop branch I’ll do this:

E:\code\milelogr [demo]> git checkout develop
Branch develop set up to track remote branch develop from origin.
Switched to a new branch 'develop'
E:\code\milelogr [develop]> git merge demo
…
Merge made by the 'recursive' strategy.

Note that I did NOT use  git merge –no-ff demo in the above example. Since we treat our public branches (develop and master) as ‘pure’, we do NOT want to fast-forward history when merging.  Instead we will always ‘clean up’ a feature branch’s history before merging (see this post for why).

I can then delete the feature branch (locally):

E:\code\milelogr [develop]> git branch -d demo
Deleted branch demo (was 303c53f).

If we’re really done with that demo feature branch we need to delete it from the remote too:

E:\code\milelogr [develop]> git push origin :demo
remote: bb/acl: bogus is allowed. accepted payload.
To git@bitbucket.org:bogus/milelogr.git
- [deleted]         demo

Finally, we push the develop branch to the remote:

E:\code\milelogr [develop]> git push origin develop
Counting objects: 1, done.
Writing objects: 100% (1/1), 231 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
remote: bb/acl: bogus is allowed. accepted payload.
To git@bitbucket.org:bogus/milelogr.git
    46a5f06..298561f  develop –> develop

Hope this helps others.

Comments and suggestions on improving this workflow welcome below.

© Charlie Kindel. All Rights Reserved.