Staying Sane with Git

You might have seen it – @kkuchta christmas tree gif making the rounds these past holidays:

tumblr_inline_o0mdgf6twL1t74qoj_540

For a short story, that sums up my early experience with git with surprisingly accuracy!

For anyone else who’s struggled with git over the years, here’s a bit of my process to help me stay sane with git:

1. Where I am in the tree?

I use the command line to make new commits, pull, merge, etc, and when I first started using git I had a lot of trouble understanding just where I was in the repo at any given time. After branching a few times, I’d get lost pretty easily and forget which branch I was currently on. A simple fix – I added the following line to my .bash_profile:

# Put Git branch name it shell prompt
parse_git_branch() {
  git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}
PS1='\W$(parse_git_branch)\$ '
`

This simply printed out my current location in my console’s prompt:

loose-leaf(master)$ 

Now I felt comfortable git add branch-nameing and git checkouting as often as I needed without getting lost.

2. Where I can go in the tree?

Now that I knew my location in the tree, I still wasn’t sure where I was in relation to all the other branches. For that, I use a GUI app called GitX-dev: http://rowanj.github.io/gitx/. It’s a fantasic and incredibly simple app that shows the history, branches, and tags of your git repo.

tumblr_inline_o0mf1nh6NQ1t74qoj_500

It’s incredibly handy for easily seeing at a glance where I am in relation to all of the other branches, both local and remote.

3. Where is the remote?

Speaking of remote – there’s been more than once where I’ve worked on a fork of a primary repo, so I needed to keep track of both the upstream original repo and the fork repo.

For that, two simple commands help me know what’s what:

# lists all of the remote repositories and their names
get remote -v

and

# fetches all of the branches and tags from the remote repo
git fetch remote-name

These two commands let me know if my local branch was still simply an ancestor of the remote branch – if so, I could git pull without fear of an ugly surprise merge.

If my local branch had diverged from the remote, then I could look through the recent commits for that remote branch in GitX, and then decide if I wanted to a) git rebase my local changes onto the end of the remote branch with:

git rebase branch-name remote-name/branch-name

or b) have more context going into the merge with git pull.

4. Where is my bug?!

Here’s the real gem that I’ve only recently started using to great effect: git bisect.

Git bisect is a magical tool that will help you find the exact commit that caused the bug you’re trying to track down. Simply put, it does a binary search through your commit history until it finds the exact commit where the bug appeared.

Assuming your repo is currently showing your bug, you can start the process with a simple:

$ git bisect bad
You need to start by "git bisect start"
Do you want me to do it for you [Y/n]? Y

Confirm the prompt with a Y, and then checkout a commit that you know doesn’t show the bug:

$ git checkout old-branch-that-worked

And then mark that bugless commit as the 'good' commit.

$ git bisect good

And now the magic has started! You’ll see something like the following show up soon:

Bisecting: 4 revisions left to test after this (roughly 2 steps)    
[uglycommithash] an old commit message

Git has started its binary search through your tree, and all you have to do now is tell it whether the bug shows up in this commit with git bisect bad or if it’s bug free with git bisect good. Just keep going until you finally see:

ugly-commit-hash is the first bad commit
commit ugly-commit-hash
Author: Definitely Not You

And that’s it! Now you know precisely where the bug is and have a much better chance of tracking down its root cause. At this point I usually git branch bug-name just so I have a temporary flag in GitX for me to look for.

Note: At the end of your git bisect, even though it’s shown you the offending commit, you’ll technically still be in bisect mode.

# at this point after a bisect, your git status will be:
$ git status
HEAD detached at 4751a0a
You are currently bisecting, started from branch 'master'.
  (use "git bisect reset" to get back to the original branch)

To return to git business as usual, simply:

$ git bisect reset

And Tada! You’re back at your original branch as if nothing had happened. It’s an absolutely invaluable way to quickly narrow down a tricky to find bug.

A little goes a long way

This writeup barely scratches the surface of what’s possible in git – there’s huge swaths of functionality I haven’t even touched on – but if I had to choose only a handful of features, this would be it. This is 99% of what I use when I’m interacting with a repo, and I wish I’d seen it when I first made the switch to git many years ago.

Procrastination as a tool for Productivity

There’s a thousand small things that I need to get done during the week. They’re only mildly the same week-to-week, and they all individually don’t take up much time at all, but together they eat far more of my productivity than they deserve. I’d found that I was spending far too much time context-switching between my real work – programming – and these other smaller tasks. Programming is the sort of job that doesn’t work well with small distractions, and these small tasks were eating far more of my time than they deserved. Something needed to change.

So a few months ago I started an experiment – instead of doing these little things as they came up during the week, I would procrastinate- er, schedule- all of that time for a single day: Tuesday.

It’s a simple change – an obvious one in hindsight – Instead of replying to emails immediately or scheduling calls for that afternoon, I decided to completely ignore them until just 1 day a week. If I needed to meet with someone, “how’s next Tuesday work for you?” If I needed to write a new blog post – Tuesday’s the day. Replying to emails? Tuesday. Generi-task that’s not crucial to building great products? Tuesday.

It’s a simple change, but it’s let me push all of my context-switching tasks into just 1 time block, which has freed up the rest of my week for dramatically fewer distractions as I program. I’ve dubbed Tuesday as my “fake productivity” day – it’s for all the things that I genuinely do need to do, but at the same time don’t help me build better software. My “real productivity” – building stuff – has been much more efficient now that I’ve moved most of my distractions out of the way.

It’s a habit I highly recommend.

Automatically Include Open Source Licenses into the Settings Bundle

There’s a good chance your iOS app includes a number of open source frameworks in its codebase, and Loose Leaf is no different. Many of these dependencies’ licenses require that you include a copy of the license in your final build. Some require other attribution or URLs. Even with just a few includes, it can quickly become difficult to manage.

Last week, we talked through how to automate our app’s version numbers, and this post will automate our bundled licenses.

The Goal: Include Licenses in our Settings

This walkthrough will help us create a custom sub-menu in our Settings.bundle to show all of our bundled licenses.

Licenses in Settings

Step 1: Prepare The Settings Bundle

First, we need to define where that section will appear in our settings bundle.

In your Settings bundle, create a new file Acknowledgements.plist:

Acknowledgements

Since the contents of this file will be auto-generated each time we compile, there’s no reason to add it into your git repo. I suggest adding the path to Acknowledgements.plist into your .gitignore file.

Next,you’ll need to add 2 items to your Root.plist – a Title and a Child Pane:

License Root.plist item

The first item creates the “LICENSE” title item, and the second creates the link to our sub-menu. It references the file “Acknowledgements.plist” to use as the child pane.

Step 2: Add License Files

Next, we need to define the licenses that we’ll be wanting to include in our Settings bundle. For each license, create a separate .license file in your project contents. No need to copy these into your built product, we only need them in our source tree – we’ll be using them to generate the contents of our Acknowledgements.plist file in the next step.

Each license file should be in the following format:

Name of the Licensed Object, eg. "JRSwizzle License" etc

contents of the license here - copy of the MPL or MIT License, etc

Step 3: The Build Script

I’m using the perl script from this Stack Overflow answer, and I’ve saved it as “generateLicenses.pl”. This perl script will be modifying the Acknowledgements.plist file for us.

#!/usr/bin/perl -w

use strict;

my $out = "Settings.bundle/en.lproj/Acknowledgements.strings";
my $plistout =  "Settings.bundle/Acknowledgements.plist";

system("rm -f $out");

open(my $outfh, '>', $out) or die $!;
open(my $plistfh, '>', $plistout) or die $!;

print $plistfh <<'EOD';

StringsTable
Acknowledgements
PreferenceSpecifiers

EOD
for my $i (sort glob("*.license"))
{
    my $value=`cat $i`;
    $value =~ s/\r//g;
    $value =~ s/\n/\r/g;
    $value =~ s/[\t]+\r/\r/g;
    $value =~ s/\"/\\\"/g;
    my $key=$i;
    $key =~ s/\.license$//;
    
    my $fullstr = "";
    
    my $cnt = 1;
    my $keynum = $key;
    for my $str (split /\r\r/, $value)
    {
        if ($cnt == 1){
            print $plistfh "\n";
            print $plistfh "Type\n";
            print $plistfh "PSGroupSpecifier\n";
            print $plistfh "Title\n";
            print $plistfh "$keynum\n";
            print $outfh "\"$keynum\" = \"$str\";\n";
        }else{
            $fullstr .= "\n\n" . $str;
        }
        
        $keynum = $key.(++$cnt);
    }
    
    $fullstr =~ s/^\s+//;
    $fullstr =~ s/\s+$//;
    
    print $outfh "\"$keynum\" = \"$fullstr\";\n";
    print $plistfh "FooterText\n";
    print $plistfh "$keynum\n";
    print $plistfh "\n";


    print $plistfh "\n";
    print $plistfh "Type\n";
    print $plistfh "PSGroupSpecifier\n";
    print $plistfh "FooterText\n";
    print $plistfh "\n\n";
    print $plistfh "\n";
}

print $plistfh <<'EOD';



EOD
close($outfh);
close($plistfh);

Important!

Make sure the Run Script is near the top of your Phases section. This script needs to be run before the Copy Resources item in your Build Phases.

Step 4: Build your project!

That’s it! Just build and run your project, and all of the licenses should be auto-included into your Settings bundle automatically. If you ever need to add or remove more licenses, just add or remove more .license files from your project’s directory and they’ll be automatically included in the next build. Easy!

This process has been incredibly helpful for Loose Leaf‘s build process. I include a number of different open source frameworks, and this has been incredibly helpful for organizing those licenses into the build. Of course, I also have a number of frameworks that I’ve open sourced as a part of Loose Leaf’s development, so maybe I’ll see one of those included in your project too!

Google Author link
Page 1 of 4912345...102030...Last »