Advanced Git Commits

This article will outline some advanced usage patterns for git commit

Skip Staging

When I was first learning Git, I had difficulty grasping the purpose of Git’s staging area. It seemed, at the time, a pointless step in the process of performing a commit. Later in this article, we will see some good reasons for using the staging area. In this section, I will show you how to skip the staging area when committing.

The standard process when committing is to first stage, then commit.


$ # Use a text editor to modify index.html
$ git add index.html # Add to staging area
$ git status         # We see that index.html has been staged
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD ..." to unstage)

        modified:   index.html

$ # Commit with a message
$ git commit -m 'Added content to index.html' 
[master 1203be7] Added content to index.html
 1 file changed, 1 insertion(+)

In the above, we had to use git add index.html to stage the file. However, if we use the -a option of git-commit, we can eliminate this step:


$ git commit -am 'Added content to index.html' 

Here, the -a option causes all the modified files in the working directory to be added to staging automatically. This has the effect of putting the staging process behind the scenes.

Note that the -a is joined with the -m option for convenience. It could have been written like this:


$ git commit -a -m 'Added content to index.html' 

But that’s just more to type. Be sure, however, that the -m option is at the end since Git will expect the commit message to come directly after it.

Verbose Commits

Of course, to see what you are about to commit, you would use git diff. However, if you want to edit your commit message in a text editor (by omitting the -m option), it would be convenient to have the diff available to you in the editor for viewing.

Edit some files in your working tree and try this on for size.


$ git commit -av

As in the section Skip Staging, the -a option is used to add the files to staging automatically (of course, we could have used git add if we wanted to). The other option, -v, meaning verbose, appends a unified diff to the contents of the text editor. This is what I’m seeing in my text editor after I issued the above command.


Change content
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#   modified:   index.html
#
# ------------------------ >8 ------------------------
# Do not touch the line above.
# Everything below will be removed.
diff --git a/tmp/index.html b/tmp/index.html
index 4b8b391..0aca176 100644
--- a/tmp/index.html
+++ b/tmp/index.html
@@ -3,7 +3,6 @@
         <title>My Document</html>
     </head>
     <body>
-        <p>Some content.</p>
-        <p>Some more content.</p>
+        <p>Changed the content.</p>
     </body>
 <html>

This makes editing the commit message convenient because you can look at a the diff and comment on exactly what you’ve done. And, if you see something you don’t want committed, you can abandon the commit by leaving the commit message blank and exiting the editor.

Commit and Patch in One Command

One of my favorite features of Git is the ability to stage selected modifications in your working tree. I wrote an article about it here. Normally you would use git add -p to go through all your modifications selecting the ones you want to commit. This is good when you have more than a few modifications. However, when I have made a small modification, and I know there are modifications in my working tree that I don’t want to commit, I use this command:


$ git commit -p

This will first take you through the patch selection process, then put you in a your text editor to write a commit message. You can also add the -v option (which I always do) to get the diff in your editor.


$ git commit -pv

Amending Commits

If you noticed that you made a mistake with your last commit, it’s quite easy to correct it. You may have a typo in the commit message or you may have noticed there was something wrong with the code you committed. Either way, all you need to do is use the --amend option in your git commit.


$ git commit --amend

Your text editor will be launch and you can edit your commit message to whatever you like. When you leave your text editor, you will notice in git log that the last commit you did now has the updated commit message. (The commit’s SHA1 hash will also be different from the original since the commit’s timestamp and message have changed.) You can also modify the code changes in the commit simply by modifying and staging you code, as you normally would, and then committing them with the --amend option. The --amend options can also be conveniently mixed with the -a, -v and -p options described above.

Bought a Chromebook

Recently, I was in the market for a new, personal laptop. I wanted something that would run Linux well. For about a decade, I’ve been installing and using Linux on my laptops, but WiFi has always been a hassle. Typically, there has always been some sort of issue with the network card’s driver which caused it to only half support Linux. This would result in hours spent resolving the issue. Often, I would end up having to buy a USB-based WiFi network card that I knew was supported by Linux.

acerfull

Chromebook

It was my wife who suggested I buy a Chromebook. Though not a proper Linux machine, the idea of a light-weight, thin-client running a browser and little else had an appeal for me. I actually don’t use my laptop for much else other than surfing the web and SSH’ing into other machines. So, since there is an SSH client plugin for Chrome, I thought I would give a Chromebook a try. Also, I reasoned, if it didn’t work out, I could always install Linux on it. After all, Chrome OS is based on the Linux kernel so there shouldn’t be any issues with running pure Linux on it.

The Hardware

I ended up purchasing the Acer Chromebook 15. I like this machine a lot. It’s got two speakers on either side as tall as a hand and the width of two fingers. This is great for watching movies or listening to music. (On my other laptops, I’ve always needed headphones in order to properly hear speech or music.) It’s also very light-weight — probably because of the SSD hard-drive. The battery-life is terrific and the laptop has a good look and feel to it. It’s also fast – much faster than my older machines which often made simple web browsing painfully slow.

acerkbd

I do have several complaints about the user interface aspects of the hardware. For one, the mousepad is positioned in a way that makes it inevitable that it will get tapped accidentally. As I write this in a WYSIWYG editor, I’m constantly noticing that the cursor has been moved which causes me to inadvertently type in different sections of the text. Another irritant is that there is no caps lock button — it has been replaced by a magnifying glass button — I guess so you can Google things faster. I don’t know what the consensus is on caps lock buttons, but I rely on them and I wish this laptop came with one. Also, the function keys have been replaced by special, Chromebook keys.

The Software

It’s pretty amazing to interact with this OS. It’s essentially on as soon as you hit the power button. Once on, you log in with your Google credentials. Then you set your keyboard layout, connect to your WiFi, and you’re ready to surf the web. I like Chrome, but prefer Firefox for its Awesome Bar. However, in this instance, I was willing to make a comprise.

My next task was to ensure I could get the aforementioned, browser-based, SSH client working. This is a good piece of software from what I was able to tell. However, after editing source code on a remote server, I became aware that there is a major issue with the concept of a browser-based SSH clients: key bindings. For example, I noticed that when I used CTRL-W to change windows in Vim, I was also telling Chrome I wanted to close the tab. I found a workaround for that, but then, when I tried CTRL-D to exit a shell, I realized I was telling Chrome I wanted to bookmark the current page. At that point, I realized that I was going down a rabbit’s hole of hacks I would have to make to Vim, Bash, etc., and that I should abandon Chrome OS and install Linux.

Installing Linux

Installing Linux on Chromebooks is interesting. You actually don’t defenestrate Chrome OS; you run both operating systems at the same time (glad I got 4GB of RAM instead of 2GB), and you can switch between them swiftly with a hotkey.

When I say you run both OS’s at the same time, I’m not talking about a virtual machine. Rather, you run Linux from Chrome OS using chroot. This is all made possible by a piece of software called Crouton.

Anyway, installing Linux on a Chrome OS is a fast, straightforward process. I recommend this tutorial. However, it is a little outdated since Crouton now supports Trusty Tahr, so once Ubuntu has been installed, remember to install it with this command.


sudo sh ~/Downloads/crouton -r trusty -t xfce

You have the option of installing Xfce or Unity. I choose Xfce and that consumes pretty much all of my 4GB of RAM. (I wanted to use Ratpoison but that doesn’t appear to be supported.)

After tweaking Xfce to meet my needs, I now have a beautiful installation of Linux and Chrome OS on a very capable laptop. I code and surf in Linux and I use Chrome OS for watching Hulu, Netflix, etc. Chrome OS is more convenient for this kind of thing because it doesn’t have the hangups about DRM and Flash that Firefox and Linux do. Chrome OS is also needed for managing WiFi connections which I have not had any issues with.

Conclusion

For the Linux lover, Chromebooks provide an solution which is both awkward and elegant. A complete Linux experience can be had with WiFi being competently managed by Chrome OS. For DRM media offerings like Netflix and Hulu, the user can easily switch to Chrome OS for hassle-free viewing. However, little RAM is left and the small SSD hard-drive is as fast as it is limited in its storage capacity.

Broken Rules Pattern with Epiphany

broken rulesThe Broken Rules pattern* provides an encapsulated way for objects to indicate whether or not their internal state is valid. Here, “valid” usually means that the data contained in the object is ready to be persisted to a database. An object following this pattern will have two properties: isvalid and brokenrules. isvalid returns True or False to indicate whether or not the object has any broken rules. brokenrules will return a collection of validation problems if there are any.

Broken Rules for Entity Classes

To demonstrate, let’s create some entity classes.


class deities(entities):
    pass

class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __str__(self):
        return self.name

pantheon = deities()
zeus = deity('Zeus', 'male')

assert pantheon.isvalid
assert pantheon.brokenrules.count == 0

assert zeus.isvalid
assert zeus.brokenrules.count == 0

Here, we define an entities collection class called deities and an entity class called deity. We instantiate one of each and we can immediately assert that each has an isvalid proprety that is True and a brokenrules collection property which is empty. These properties are available as a consequence of the classes inheriting from entities and entity. Both asserts mean the same thing since by definition, an entity with no broken rules is valid.

Now let’s redefine the deity class to contain validation logic so we can ensure it knows when it is invalid.


class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    
    @property
    def brokenrules(self):
        brs = brokenrules()
        if self.gender not in ('female', 'male'):
            brs += '{} is an invalid gender.'.format(self.gender)

        if type(self.name) != str:
            brs += 'Deity names must be strings.'

        return brs
    
    def __str__(self):
        return self.name

zeus = deity(42, None)

assert not zeus.isvalid

assert str(zeus.brokenrules) == 'None is an invalid gender.\nDeity names must be strings.'

The first step is to override the brokenrules property. It creates and return its own brokenrules collection class each time it is called. We establish two validation rules: the first is that a deity must have a gender that is either “female” or “male”, and the second is that the deity’s name must be a string. Later, we instantiate a deity called zeus and deliberately brake both of these rules upon instantiation by making its name 42 and giving it a gender of None. Now, we can assert that zeus is not valid. We can also convert the brokenrules property to a string to get a \n delimited list of human readable broken rules.

Persistence

Epiphany entities don’t currently perform persistence routines (this will likely change in the future), but if we wanted to ensure that our invalid zeus is never saved to the database, we could write our save (INSERT/UPDATE) method like so.


class deity(entity):
    ⋮ 
    save(self):
        if self.isvalid:
            # INSERT/UPDATE state date into database
            pass
         else:
              raise Exception("Can't save to database. Object is in an invalid state.")
    ⋮ 

Here, isvalid is tested to ensure that invalid data is never persisted. However, if this were for a typical data entry application, the UI code should first test if the object is valid, and if is not, inform the user so the data can be corrected.

Broken Rules for Collections

Let’s see what happens when we add our invalid zeus to a deities collection.


pantheon = deities()
pantheon += zeus

assert not pantheon.isvalid
assert str(pantheon.brokenrules) == 'None is an invalid gender.\nDeity names must be strings.'

If an entities collection object contains one or more invalid entity objects, it will become invalid. Thus, having added zeus to the pantheon collection, we have now made pantheon invalid. Converting the entities collection’s brokenrules property to a string will list each of the broken rules in each of the invalid entity objects within the collection.

Recursive Broken Rules

When an entity has a collection of child entities, it is easy to create a validation rule that states that the entity is only valid if all of it’s child objects are valid.

Let’s say that our deity object has a collection called children(appropriately enough):


class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        self.children = deities()

    @property
    def brokenrules(self):
        brs = brokenrules()
        if self.gender not in ('female', 'male'):
            brs += '{} is an invalid gender for "{}".'.format(self.gender, self.name)

        if type(self.name) != str:
            brs += 'Deity names must be strings.'

        brs += self.children.brokenrules

        return brs

    def __str__(self):
        return self.name


zeus = deity('Zeus', 'male')
zeus.children += deity('Aeacus', 'm')
zeus.children += deity('Angelos', 'female')
zeus.children += deity('Aphrodite', 'female')
zeus.children += deity('Apollo', 'male')
zeus.children += deity('Ares', 'male')
zeus.children += deity('Artemis', 'f')
zeus.children += deity('Athena', 'female')


assert not zeus.isvalid
assert str(zeus.brokenrules) == 'm is an invalid gender for "Aeacus".\nf is an invalid gender for "Artemis".'

Here we’ve redefined the deity entity to have a children property to contain all of the deity’s children. (Note that it’s a coincidence that this property is called children. Obviously, an object can (and usually would) have child collections for things other then literal children. deity and children have a one-to-many relationship with each other and that is the concept that is being illustrated here.)

We’ve also added a line in the brokenrules property to collect all the broken rules of the deity’s children objects. We’ve additionally made a minor adjustment to the broken rule text to indicate which deity the broken rule pertains to.

Now we use the new class by re-instantiating zeus and adding his children to his children collection. (We will stick to those of his children whose name started with “A” for brevity.)

Note that for Aeacus and Artemis, we’ve abbreviated the gender. This breaks a rule because gender must be either of the exact strings ‘male’ or ‘female’. Thus, zeus becomes invalid – not because the zeus itself is invalid, but because two of his children are in an invalid state. The brokenrule collection tells us which deity objects are invalid.

Conclusion

The Broken Rule pattern is a powerful way to centralize the validation rules of the object, and ensure that bad data is never persisted to the data store. Additionally, it is easy to capture any broken rules in an object graph by adding a single line of code in the brokenrule property.

Ideally, all validation could could happen in this property since it’s centralized and has the full power and expressiveness of the Python language to test values for validity. However, for reasons of optimization, it will still be necessary to duplicate many of these rules on the browser using JavaScript to improve the user’s experience. Also, some validation rules are so data-intensive that they should be implemented in the database itself (e.g., using check constraints, unique constraints, etc.). Regardless, I’ve found this pattern so useful that I use it for virtually all of the code I author.

* The Broken Rules pattern was likely first described in Rocky Lhotka’s book Professional Visual Basic 5.0 Business Objects published in 1997

Zachary’s First Program

I’m feeling pretty proud today. Last night, while working with my son on his reading and writing skills, he asked if we could do some “vi” instead. Of course, I said okay to that. So we got a command prompt going and I told him to enter vi. I said, “Why don’t we write a program?”. He got excited and I walked him through a simple conditional branch. This is what he typed.


if 2==1 :
    print 'uiioo9kooio;iplko[fp;-[k;kjifkfjkjfjjjhgffjvvhjvjv8vhvhvhhhvv bh  h h fyvr7ryzachary'

After explaining what it all meant, he thought it would print the text. He didn’t quite have the concept of equality down. Once I explained it to him, he change the 2 to a 1 and was happy to finally have it all done.

Epiphany Queries

Python Epiphany collection classes can be queried in a way that is reminiscent of SQL WHERE clauses, except with the full expressive power of Python. Let’s create a deities collection class and a corresponding deity entity class to set up a demonstration.


class deities(entities):
    pass

class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

Here, deities inherits from entities making it a collection class. deity objects will obviously be appended to this collection class. Each deity object will store the deities name and gender.

Let’s start by using our classes to build up a pantheon of the major Greek gods and goddesses.


pantheon = deities()
pantheon  +=  deity('Aphrodite',   'female')
pantheon  +=  deity('Apollo',      'male')
pantheon  +=  deity('Ares',        'male')
pantheon  +=  deity('Artemis',     'female')
pantheon  +=  deity('Athena',      'female')
pantheon  +=  deity('Demeter',     'female')
pantheon  +=  deity('Hades',       'male')
pantheon  +=  deity('Hephaestus',  'male')
pantheon  +=  deity('Hera',        'female')
pantheon  +=  deity('Hermes',      'male')
pantheon  +=  deity('Hestia',      'female')
pantheon  +=  deity('Poseidon',    'male')
pantheon  +=  deity('Zeus',        'male')

Now, using the pantheon collection object, lets create gods and goddesses collections by querying the pantheon collection.


gods = pantheon.where(lambda x: x.gender == 'male')
goddesses = pantheon.where(lambda x: x.gender == 'female')

assert isinstance(gods, entities)
assert isinstance(goddesses, entities)

assert gods.count == 7
assert goddesses.count == 6

The first line uses the where method to collect all the deities that are male. The second line does the same for female deities. A lambda object is used as the query, though any Python callable, such as a function, could be used. Lambdas have the advantage of being terse.

Later we can assert that the where method returns a generic entities class . Ideally, where would have created and returned a deities class in this example. However, that feature hasn’t been implemented yet.

Finally, we make use of the queries to observe that there are 7 male deities and 6 female deities in our original pantheon collection.

In the above queries, only one property, gender, was tested for equality. However, we can make the test as expressive as any Python expression can be. Let’s say we want to see how many male deities are in our pantheon collection whose name starts with the letter ‘A’.


gods = pantheon.where(lambda x: x.name[0] == 'A' and x.gender == 'male')
assert gods.count == 2
assert gods[0].name == 'Apollo'
assert gods[1].name == 'Ares'

Here we see it’s no problem to use Python’s string indexing as well as the and operator to create a slightly more complex query. Of course, very complicated queries with complex nested Boolean expressions can be used as well. We can see that two deities are returned: the first being ‘Apollo’ and the second being ‘Ares’.

Disabled submit buttons in Chrome

There is an interesting difference in the way Chrome handles disabled submit buttons that is worth keeping in mind when developing for different browsers.

disabled submit

When the user clicks a button — particularly a submit button — it is often useful to disable the button so the user can’t accidentally click it multiple times. Multiple clicks would lead to undesirable results, such as a blog post being submitted twice or, worse, a sales order being executed more than once.

Let’s consider an HTML form called my-form with a button named submit-button.


<form id="my-form">
    <!--Form elements would go here-->
    <button id="submit-button" type="submit">Submit</button>
</form>

The following JavaScript (which relies on jQuery) will work on non-Chrome browsers.

     
$('#submit-button').on('click', function (e) {
    $(this).attr('disabled', 'disabled');
});

This event is triggered when the submit-button is clicked. It sets the button’s disabled attribute. Afterwards, the browser will submit the form.

To get the above code to work in all browsers, make the following changes.

     
$('#submit-button').on('click', function (e) {
    e.preventDefault();
    $(this).attr('disabled', 'disabled');
    $('#my-form').submit();
});

The first line will disable the browsers default behavior (e.preventDefault();) — which is to submit the form. Then we disable the button like we did before. Finally, the form is submitted imperatively by the JavaScript.

Fast development in Pantheon

Developing on Pantheon can be slow

Pantheon provides a convenient environment for developing and hosting WordPress and Drupal sites. For each Pantheon site, you are provided with a development, test and live (production) environment. The development environment offers a convenient method for testing code changes.

pantheon

However, on first glance, deploying code changes to the development environment may seem terribly inconvenient. So much so, I imagine most developers set up a local development environment for developing on. Of course, this relegates Pantheon’s development environment to being a mere gateway for getting your code into the test and live environments.

The reason developing in Pantheon environment is inconvenient is because it uses SFTP and Git as the means to upload your code modifications to the development environment. That means, every time you make code modifications which you would like to test, you must save them in your IDE locally, then use an SFTP or Git client to upload your (likely small) changes to the development environment. Using Git for this seems out of the question, since it would mean a commit for each minor modification you make during development, but a conventional SFTP client would be to slow because every time you save your changes, you would have to switch to your SFTP client, select and upload the changed files, wait for the transmission to complete, then test the changes.

Luckily, there is a faster way.

SSHFS allows you to mount an SFTP site just like you would mount any other drive. You can install it using the following.


sudo apt-get install sshfs

Instruction for installing on Mac and Windows can be found at here.

Mounting

Get you SFTP connection information by going to your development site’s Pantheon Dashboard and click on the Connection Info button.

Create a directory for the mount.


mkdir ~/mnt/dev-jesse-hogan.com

Now mount the SFTP site,


sshfs <user-name-from-dashboard>@<host-name-from-dashboard>: ~/mnt/dev-jesse-hogan.com -p 

sshfs will request the SFTP password which is also obtainable from the Pantheon Dashboard.

If all goes well, you should be able to enter that mount directory and begin editing your source files.


$ sshfs <username>@<hostname>: ~/mnt/jesse-hogan.com/ -p 2222 
<username>@<hostname>'s password:
$ cd ~/mnt/jesse-hogan.com/
$ ls
bin  cache  certs  chef.stamp  code  files  fusedav.conf  htpasswd  includes  logs  metadata.json  mime.types  nginx.conf  php  php-fpm.conf  php.ini  run  stunnel  tmp
$ cd code
$ ls
index.php    readme.html  wp-activate.php  wp-blog-header.php    wp-config.php         wp-content   wp-includes        wp-load.php   wp-mail.php      wp-signup.php     xmlrpc.php
license.txt  README.md    wp-admin         wp-comments-post.php  wp-config-sample.php  wp-cron.php  wp-links-opml.php  wp-login.php  wp-settings.php  wp-trackback.php

As you can see above, when you first enter the mount, there are no WordPress file presented – only config files, logs for the web server’s environment. You must go into the code/ directory to view the WordPress files (not sure about Drupal). From here, you can edit your WordPress themes and plugins as if you were on the development site itself.

How to paste in Vim from the outside

When using Vim in a dummy terminal, pasting text into Vim can be result in indentations which get progressively longer as the screen fills up. This vexed me for quite some time until I discovered that there is a simple fix for it. When you want to paste into Vim, just type following into Vim’s command-line.


set paste

Now pasting will work as expected.

When you’re done pasting, be sure to turn pasting off by issuing the following command. Otherwise, you’ll notice strange behavior when editing – such as autoindent not working.


set nopaste

Submitting a form using XHR and jQuery

jQuery’s serialize() method offers a convenient way to turn an HTML form’s data into a text string in standard URL-encoded notation. Consider the following code


$('form').on('submit', function(e) {
    $.ajax({
        type: 'POST',
        url:  '/my/endpoint',
        data: $('form').serialize(),
        dataType: 'json',
        success: function(data){
            // Success Code
        },
        error: function(jqXHR, strStatus, strErrorThrown){
            // Error Code
        }
    });
});

This serializes the form data in $(‘form’) and sends it to /my/endpoint.

Note that the JSON will contain keys for all the input elements regardless of whether or not they contain data except for inputs of type “radio” or “checkbox”. They must be clicked, then their key and value will be present in the JSON.

The following PHP code will take the serialized form data and convert it into a standard PHP array.


$qry = file_get_contents("php://input");
parse_str($qry, $qry_array);

$qry_array will contain the form’s data.

Pretty printing JSON in Vim

Command Line

First, your going to want to get a good command line tool to pretty print JSON. Unfortunately, you can’t just do this.


apt-get install ppjson

However, to create `ppjson`, you can just do the following.


sudo sh -c 'echo python -mjson.tool  > /usr/local/bin/ppjson'
sudo chmod 755 /usr/local/bin/ppjson

Now you have a JSON pretty printer called `ppjson`. Just pipe some JSON into it and watch it go to work.


jhogan@abe:$ echo '{"b":2, "a":1}' | ppjson
{
    "a": 1,
    "b": 2
}

Doing it in Vim

Vim has a very nice feature that lets you pipe text from a Vim buffer into a filter (i.e., command). Lets say your Vim buffer is filled with unformatted JSON text – but nothing more. Just issue the following command-line in Vim’s command mode.

%!ppjson

The buffer will now be filled with nicely formatted JSON.

On the other hand, if only a portion of the Vim buffer contains JSON, use CTRL-V to select the JSON, then, while still in normal mode, type !ppjson. In Vim’s command line, you will see:


:'<,'>!json

Hit ENTER and the unformatted JSON will be replaced by formatted JSON.