2013-12-23

 



Corey Quinn | Senior Technical Consultant at Taos

by Corey Quinn | Senior Technical Consultant at Taos

Earlier last week, the fine folks from SaltStack conducted an on-site training for us at Taos. For those who are unaware, Salt is a remote execution / configuration management system written in Python, and a project to which I’ve been a contributor for several years.

As often happens during such trainings, someone asked a question about how to achieve a certain goal. The answer was “that’s a good idea; we can’t do it yet, but patches are welcome!”

Fast-forward twelve hours.

At 3AM the following morning I received an email from a class attendee with a patch that neatly solved the problem. “The code was easy– but how do I get this accepted into Salt?”

So without further ado, allow me to present a crash course in git workflow for submission to projects on Github. Note that every project is slightly different, so these steps may not map exactly to a given project’s workflow. I’ll endeavor to point out these edge cases as they arise.

It starts with forking the upstream repository. Visit https://github.com/saltstack/salt, sign in (taking care to create an account if you don’t have one already) and hit the “Fork” button in the upper right.

At this point, you’ll want to set up your system to use git; details on this are available at https://help.github.com/articles/set-up-git. Be sure to set up an ssh keypair as well.

At this point, you’ll want to check out your fork of Salt onto your local system. Since my username on github is KB1JWQ, I would simply:

cd ~/src
git clone git@github.com:KB1JWQ/salt
cd salt

When working on a new feature, it makes the most sense to work in a feature branch. This way, you can sanely work on multiple features concurrently if inspiration strikes. In this example, I’ll update a bit of the documentation to reflect a change I made to the Salt installation process on OS X.

git branch docfix
git checkout docfix

At this point, I’m working on the “docfix” branch, as can be shown:

[cquinn@princessquinndolyn salt]$ git branch
develop
* docfix

(Note that Salt’s upstream branch is called “develop”, as opposed to the more frequently encountered “master” for most projects.)

At this point I’ll go ahead and make some changes, committing after each one. Soon, I’m pretty happy with my changes, and “git log” looks like this:

[cquinn@princessquinndolyn salt]$ git log
commit c42d182a8bff8cf61d91bad5a1b8033dc91fb5bb
Author: Corey Quinn <github@sequestered.net>
Date: Wed Dec 18 06:32:51 2013 -0800

Grizzly bear defeated and has fled back into the woods; back to documentation fixes

commit 740ba96ea4fdfa7a2e1a8f74e300df3b465c738d
Author: Corey Quinn <github@sequestered.net>
Date: Wed Dec 18 06:29:49 2013 -0800

Missed a bug when I had to pause to wrestle a grizzly bear

commit fe603e9f91b2a8d4c80b34281c3bac91d1052ea4
Author: Corey Quinn <github@sequestered.net>
Date: Wed Dec 18 06:27:12 2013 -0800

Included homebrew installation information

 

I’m pretty happy with this, but as far as the upstream project is concerned, I’m only fixing one issue; they don’t need to see the “grizzly” details. At this point I’ll squash my three commits into one:

[cquinn@princessquinndolyn salt]$ git rebase -i HEAD~3

This uses git’s rebase command. The -i flag indicates I want to do it interactively, and HEAD~3 states I want it to operate from the current latest commit (called HEAD) to the three most recent commits. This drops me into my editor, where I see:

pick fe603e9 Included homebrew installation information
pick 740ba96 Missed a spot when I had to pause to wrestle a grizzly bear
pick c42d182 Grizzly bear defeated and has fled back into the woods; back to documentation fixes

# Rebase b9aeb5a..c42d182 onto b9aeb5a
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like “squash”, but discard this commit’s log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

I want to pick the first commit, and squash the remaining two into it, like so:

pick fe603e9 Included homebrew installation information
squash 740ba96 Missed a spot when I had to pause to wrestle a grizzly bear
squash c42d182 Grizzly bear defeated and has fled back into the woods; back to documentation fixes

 

I save and quit, at which point git fires up a new editor session:

# This is a combination of 3 commits.
# The first commit’s message is:
Included homebrew installation information

# This is the 2nd commit message:

Missed a spot when I had to pause to wrestle a grizzly bear

# This is the 3rd commit message:

Grizzly bear defeated and has fled back into the woods; back to documentation fixes

# Please enter the commit message for your changes. Lines starting
# with ‘#’ will be ignored, and an empty message aborts the commit.
# Not currently on any branch.
# Changes to be committed:
# (use “git reset HEAD <file>…” to unstage)
#
# modified: HACKING.rst
# modified: doc/topics/installation/osx.rst
# modified: doc/topics/tutorials/walkthrough_macosx.rst
#

 

I edit this file to simply contain the first commit message, and save it.

Now git log shows:

commit 8d9ec84465e46875891a30251c85d11ba2184743
Author: Corey Quinn <github@sequestered.net>
Date: Wed Dec 18 06:27:12 2013 -0800

Included homebrew installation information

 

At this point, I can “git push origin docfix”, and git will push my local changes back to my fork on Github.

At this point, if I visit my fork on Github in my web browser, it will let me know that I can submit a pull request. I include a descriptive title, and then hit Send Pull Request.

At this point, the clock is ticking. If more than 45 seconds elapse without the pull request being accepted upstream, this means that the upstream developers are being lazy. The correct solution here is to join #salt on irc.freenode.net and loudly berate them for this. What’s the fun in contributing to open source projects if you can’t be belligerent about it?

Going forward, you can continue to edit on your fork, but updating it requires a slightly different workflow than a simple “git pull.”

[cquinn@princessquinndolyn salt]$ git remote add upstream https://github.com/saltstack/salt
[cquinn@princessquinndolyn salt]$ git fetch upstream
[cquinn@princessquinndolyn salt]$ git rebase upstream/develop
First, rewinding head to replay your work on top of it…
Fast-forwarded develop to upstream/develop.

At this point, your fork is updated locally (git push to your fork will update it on Github)

Ideally this has been somewhat educational. Feel free to reach out to me either here in the comments or on irc.freenode.net (my username is Corey).

Show more