unethical blogger - Git
http://unethicalblogger.com/taxonomy/term/16/0
Git-related postsenA rebase-based workflow
http://unethicalblogger.com/posts/2010/04/rebasebased_workflow
<p><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/branch_madness.jpeg" target="_blank"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/branch_madness.jpeg"width="200" align="right"/></a>When I first started working with Git in <a href="http://unethicalblogger.com/posts/2008/07/experimenting_with_git_slide_part_13">mid 2008</a> I was blissfully oblivious to the concept of a "rebase" and why somebody might ever use it. While at Slide we were <strong>crazy</strong> for merging (<em>see diagram to the right</em>), everything pretty much revolved around merges between branches. To add insult to injury, development revolved around a single central repository which <em>everyone</em> had the ability to push to. Merges compounded upon merges led to a frustratingly complex merge history.</p>
<p>When I first arrived at Apture, we were still using Subversion, similar to Slide when I arrived (I have a Git-effect on companies). In order to work effectively, I <em>had</em> to use git-svn(1) in order to commit changes that weren't quite finished on a day-to-day basis. Rebasing is fundamental to the git-svn(1) workflow, as Subversion requires a linear revision history; I would typically work in the <code>master</code> branch and execute <code>git svn rebase</code> prior to <code>git svn dcommit</code> to ensure that my changes could be properly committed at the head of trunk.</p>
<p>When we finally switched from Subversion to Git we adopted an "integration-manager workflow" which is far more conducive to rebase being useful than the purely centralized repository workflow I had previously used at Slide.</p>
<p><center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/integration_manager_workflow.png"/></center>
<center><small>From the <a href="http://progit.org/book/ch5-1.html">Pro Git</a> site</small></center></p>
<p>In addition to the publicly readable repositories for each developer, we use Gerrit religiously which I'll cover in a later post.</p>
<p>We use rebase heavily in this workflow to accomplish three main goals:</p>
<ul>
<li>Linear revision history</li>
<li>Concise commits covering a logical change</li>
<li>Reduction of merge conflicts</li>
</ul>
<p>Creating a solid linear revision history, while not immediately important, is nicer in the longer term allowing developers (or new hires) to walk the history of a particular file or module and see a clear progression of changes.</p>
<p><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/qgit_apture_graph.png" align="right" hspace="4" vspace="4"/>Creating concise commits is probably the <strong>most</strong> important reason to use rebase, when working in a topic branch I will typically commit every 20-40 minutes. In order to not break my flow, the commit messages will typically be brief and cover only a few lines of changes, atomic commits are great when writing code but they're lousy at informing other developers about the changes. To do this, an "interactive rebase" can be used, for example, collapsing the commits in a topic branch <code>ticket-1234</code> would look like:</p>
<ul>
<li><code>git checkout ticket-1234</code></li>
<li><code>git rebase -i master</code></li>
</ul>
<p>This will bring up an editor with a list of commits, where you can "squash" commits together and re-write the final commit message to be more informative.</p>
<h3>The Workflow</h3>
<p>For the purposes of the example, let's use the topic branch from above (<code>ticket-1234</code>) which we'll assume has 3 commits unique to it.</p>
<ol>
<li>Fetch the latest changes from the upstream "master" branch
<ul>
<li><code>git fetch origin</code></li>
</ul></li>
<li>Rebase the topic branch, effectively piling the 3 commits on top of the latest tip of the upstream "master" branch
<ul>
<li><code>git rebase origin/master</code></li>
</ul></li>
<li>Collapse the 3 commits in the topic branch down into one commit
<ul>
<li><code>git rebase -i origin/master</code></li>
</ul></li>
<li>(<em>Later</em>) Bringing those commits down into the "master" branch
<ul>
<li><code>git checkout master && git rebase ticket-1234</code></li>
</ul></li>
</ol>
<p>With an interactive rebase, you can chop commits up, re-order them, squash them, etc, with the non-interactive rebase you can pile your commits on top of an upstream head making your changes apply cleanly to the latest code in the upstream repository.</p>
<p><a href="http://www.gitready.com/">git ready</a> has a few nice articles on the subject as well, such as an <a href="http://www.gitready.com/intermediate/2009/01/31/intro-to-rebase.html">intro to rebase</a> and an article on <a href="http://www.gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html">squashing commits with rebase</a></p>
http://unethicalblogger.com/posts/2010/04/rebasebased_workflow#commentsGitSoftware DevelopmentFri, 02 Apr 2010 13:00:00 +0000R. Tyler Croy276 at http://unethicalblogger.comPre-tested commits with Hudson and Git
http://unethicalblogger.com/posts/2009/12/pretested_commits_hudson_and_git
<p>A few months ago <a id="aptureLink_yMRaEAQt6P" href="http://twitter.com/kohsukekawa">Kohsuke</a>, author of the <a id="aptureLink_gay9zt4yuf" href="http://twitter.com/hudsonci">Hudson continuous integration server</a>,
introduced me to the concept of the "pre-tested commit", a feature of the <a id="aptureLink_h8ICO1PttT" href="http://en.wikipedia.org/wiki/TeamCity">TeamCity</a>
build management and continuous integration system. The concept is simple, the build
system stands as a roadblock between your commit entering trunk and only after the
build system determines that your commit doesn't break things does it allow the commit
to be introduced into version control, where other developers will sync and integrate
that change into their local working copies. The reasoning and workflow put forth by
TeamCity for "pre-tested commits" is very dependent on a centralized version control
system, it is solving an issue <a id="aptureLink_IXcu5r11no" href="http://en.wikipedia.org/wiki/Git%20%28software%29">Git</a> or <a id="aptureLink_cPtvZ5XxiP" href="http://en.wikipedia.org/wiki/Mercurial%20%28software%29">Mercurial</a> users don't really run into. Those using
Git can commit their hearts out all day long and it won't affect their colleagues until they
<strong>merge</strong> their commits with others.</p>
<p>In some cases, allowing buggy or broken code to be <em>merged</em> in from another developer's Git
repository can be worse than in a central version control system, since the recipient of the
broken code might perform a knee-jerk <a id="aptureLink_N7GE0Q9soz" href="http://www.kernel.org/pub/software/scm/git/docs/git-revert.html">git-revert(1)</a> command on the merge! When you revert
a merge commit in Git, what happens is you not only revert the merge, you revert the commits
associated with that merge commit; in essence, you're reverting <em>everything</em> you just merged in
when you likely just wanted to get the broken code out of your local tree so you could continue
working without interruption. To solve for this problem-case, I utilize a "pre-tested commit" or
"pre-tested merge" workflow with Hudson.</p>
<p>My workflow with Hudson for pre-tested commits involves three separate Git repositories: my local
repo (local), the canonical/central repo (origin) and my "world-readable" (inside the firewall) repo (public).
For pre-tested commits, I utilize a constantly changing branch called "pu" (potential updates) on the
world-readable repo. Inside of Hudson I created a job that polls the world-readable repo (public)
for changes in the "pu" branch and will kick off builds when updates are pushed. Since the content of
<code>public/pu</code> is constantly changing, the <a id="aptureLink_O9LMHblU7c" href="http://www.kernel.org/pub/software/scm/git/docs/git-push.html">git-push(1)</a> commands to it must be "forced-updates" since I am
effectively rewriting history every time I push to <code>public/pu</code>.</p>
<p>To help forcefully pushing updates from my current local branch to <code>public/pu</code> I use the following <a id="aptureLink_jO9JAsy1Sm" href="http://git.or.cz/gitwiki/Aliases">git alias</a>:</p>
<pre><code>% git config alias.pup "\!f() { branch=\$(git symbolic-ref HEAD | sed 's/refs\\/heads\\///g');\
git push -f \$1 +\${branch}:pu;}; f"
</code></pre>
<p>While a little obfuscated, thie <code>pup</code> alias forcefully pushes the contents of the current branch to the specified
remote repository's <code>pu</code> branch. I find this is easier than constantly typing out: <code>git push -f public +topic:pu</code></p>
<p>In list form, my workflow for taking a change from inception to <code>origin</code> is:</p>
<ul>
<li><em>hack, hack, hack</em></li>
<li>commit to <code>local/topic</code></li>
<li><code>git pup public</code></li>
<li>Hudson polls <code>public/pu</code> </li>
<li>Hudson runs potential-updates job</li>
<li>Tests fail?
<ul>
<li><strong>Yes</strong>: Rework commit, try again</li>
<li><strong>No</strong>: Continue</li>
</ul></li>
<li>Rebase onto <code>local/master</code></li>
<li>Push to <code>origin/master</code></li>
</ul>
<p>Using this pre-tested commit workflow I can offload the majority of my testing requirements to the build system's cluster of machines instead of running them locally, meaning I can spend the <strong>majority</strong> of my time writing code instead of waiting for tests to complete on my own machine in between coding iterations.</p>
http://unethicalblogger.com/posts/2009/12/pretested_commits_hudson_and_git#commentsGitHudsonSoftware DevelopmentThu, 31 Dec 2009 23:22:16 +0000R. Tyler Croy254 at http://unethicalblogger.comCode Review with Gerrit, a mostly visual guide
http://unethicalblogger.com/posts/2009/12/code_review_gerrit_mostly_visual_guide
<p>A while ago, when <a id="aptureLink_DCQGFvVLOq" href="http://twitter.com/pjthiel">Paul</a>, <a id="aptureLink_BbwdfFjMPz" href="http://twitter.com/jasonrubenstein">Jason</a> and I worked together, I became a big fan of code reviews before merging code. It was no surprise really, we were the first to adopt <a id="aptureLink_ySC1aL45rF" href="http://en.wikipedia.org/wiki/Git%20%28software%29">Git</a> at the company and our workflow was quite ad-hoc, the need to federate knowledge within the group meant code reviews were a pretty big deal. At the time, we mostly did code reviews in person by way of "hey, what's this you're doing here?" or by literally sending patch emails with <a id="aptureLink_NlYWR6qaQY" href="http://www.kernel.org/pub/software/scm/git/docs/git-format-patch.html">git-format-patch(1)</a> to the team mailing list so all could participate in the discussion about what merits "good code" exhibited versus "less good code." Now that I've left that company and joined another one, I've found myself in another small-team situation, where my teammates place high value on code review. Fortunately this time around better tools exist, namely: <a id="aptureLink_suzQh0OgeJ" href="http://code.google.com/p/gerrit/">Gerrit</a>.</p>
<p>The history behind Gerrit I'm a bit hazy on, what I do know is that it's primary developer Shawn Pearce (<a id="aptureLink_ZO1gp7ghRJ" href="http://www.linkedin.com/pub/shawn-pearce/0/a93/61">spearce</a>) is one of the Git "inner circle" who contributes heavily to Git itself as well as <a id="aptureLink_ORrreTOiql" href="http://www.jgit.org/">JGit</a>, a Git implementation in Java which sits underneath Gerrit's internals. What makes Gerrit unique in the land of code review systems is how tightly coupled Gerrit is with Git itself, so much so that you submit changes by <strong>pushing</strong> as if the Gerrit server were "just another Git repo."</p>
<p>I recommend building Gerrit from source for now, spearce is planning a proper release of the recent Gerrit developments shortly before Christmas, but who has that kind of patience! To build Gerrit you will need <a id="aptureLink_za0iMCBpFC" href="http://en.wikipedia.org/wiki/Apache%20Maven">Maven</a> and the Sun <a id="aptureLink_V99Bh9QLC8" href="http://en.wikipedia.org/wiki/Java%20Development%20Kit">JDK</a> 1.6.</p>
<h2>Setting up the Gerrit daemon</h2>
<p>First you should clone one of Gerrit's dependencies, followed by Gerrit itself:</p>
<pre><code>banana% git clone git://android.git.kernel.org/tools/gwtexpui.git
banana% git clone git://android.git.kernel.org/tools/gerrit.git
</code></pre>
<p>Once both clones are complete, you can start by building one and then the other (which might take a while, go grab yourself a coffee, you've earned it):</p>
<pre><code>banana% (cd gwtexpui && mvn install)
banana% cd gerrit && mvn clean package
</code></pre>
<p>After Gerrit has finished building, you'll have a <code>.war</code> file ready to run Gerrit with (<em>note:</em> depending on when you read this article, your path to gerrit.war might have changed). First we'll initialize the directory "/srv/gerrit" as the location where the executing Gerrit daemon will store its logs, data, etc:</p>
<pre><code>banana% java -jar gerrit-war/target/gerrit-2.0.25-SNAPSHOT.war init -d /srv/gerrit
*** Gerrit Code Review v2.0.24.2-72-g4c37167
***
Initialize '/srv/gerrit' [y/n]? y
*** Git Repositories
***
Location of Git repositories [git]:
*** SQL Database
***
Database server type [H2/?]:
*** User Authentication
***
Authentication method [OPENID/?]:
*** Email Delivery
***
SMTP server hostname [localhost]:
SMTP server port [(default)]:
SMTP encryption [NONE/?]:
SMTP username :
*** SSH Daemon
***
Gerrit SSH listens on address [*]:
Gerrit SSH listens on port [29418]:
Gerrit Code Review is not shipped with Bouncy Castle Crypto v144
If available, Gerrit can take advantage of features
in the library, but will also function without it.
Download and install it now [y/n]? y
Downloading http://www.bouncycastle.org/download/bcprov-jdk16-144.jar ... OK
Checksum bcprov-jdk16-144.jar OK
Generating SSH host key ... rsa... dsa... done
*** HTTP Daemon
***
Behind reverse HTTP proxy (e.g. Apache mod_proxy) [y/n]? n
Use https:// (SSL) [y/n]? n
Gerrit HTTP listens on address [*]:
Gerrit HTTP listens on port [8080]:
Initialized /srv/gerrit
</code></pre>
<p>After running through Gerrit's brief wizard, you'll be ready to start Gerrit itself (<em>note:</em> this command will not detach from the terminal, so you might want to start it within screen for now):</p>
<pre><code>banana% java -jar gerrit-war/target/gerrit-2.0.25-SNAPSHOT.war daemon -d /srv/gerrit
</code></pre>
<p>Now that you've reached this point you'll have Gerrit running a web application on port 8080, and listening for SSH connections on port 29418, congratulations! You're most of the way there :)</p>
<h2>Creating users and groups</h2>
<p>Welcome to Gerrit
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_start.png" rel="lightbox"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_start.png" width="550"/></a></center>
First thing you should do after starting Gerrit up is log in to make sure your user is the administrator, you can do so by clicking the "Register" link in the top right corner which should present you with an openID login dialog
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_openid.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_openid.png"/></a></center>
After logging in with your favorite openID provider, Gerrit will allow you to enter in information about you (SSH key, email address, etc). It's worth noting that the email address is <strong>very</strong> important as Gerrit uses the email address to match your commits to your Gerrit account
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_account_create.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_account_create.png"/></a></center>
When you create your SSH key for Gerrit, it's recommended that you give it a custom entry in <code>~/.ssh/config</code> along the lines of:</p>
<pre><code>Host gerrithost
User <you>
Port 29418
Hostname <gerrithost>
IdentityFile <path/to/private/key>
</code></pre>
<p>After you click "Continue" at the bottom of the user information page, you will be taken to your dashboard which is where your changes waiting to be reviewed as well as changes waiting to be reviewed <em>by</em> you will be waiting
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_mydashboard.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_mydashboard.png"/></a></center></p>
<p>Now that your account is all set up, let's create a group for "integrators", integrators in Git parlance are those that are responsible for reviewing code and integrating it into the "official" repository (typically integrators are project maintainers or core developers). Be sure to add yourself to the "Integrators" group, we'll use this "Integrators" group later to create more granular permissions on a particular project:
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_creategroup.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_creategroup.png"/></a></center></p>
<h2>Projects in Gerrit</h2>
<p>Creating a new project in Gerrit is fairly easy but a little <em>different</em> insofar that there isn't a web UI for doing so but there is a command line one:</p>
<pre><code>banana% ssh gerrithost gerrit create-project -n <project-name>
</code></pre>
<p>For the purposes of my examples moving forward, we'll use a project created in Gerrit for one of the Python modules I maintain, <a id="aptureLink_B0WQyZCJVK" href="http://search.twitter.com/search?q=py-yajl">py-yajl</a>. After creating the "py-yajl" project with the command line, I can visit Admin > Projects and select "py-yajl" and edited some of its permissions. Here we'll give "Integrators" the ability to <strong>Verify</strong> changes as well as <strong>Push Branch</strong>.
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_integratoraccess.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_integratoraccess.png"/></a></center></p>
<p>With the py-yajl project all set up in Gerrit, I can return to my Git repository and add a "remote" for Gerrit, and push my master branch to it</p>
<pre><code>banana% git checkout master
banana% git remote add gerritrhost ssh://gerrithost/py-yajl.git
banana% git push gerrithost master
</code></pre>
<p>This will give Gerrit a baseline for reviewing changes against and allow it to determine when a change has been merged down. Before getting down to business and starting to commit changes, it's recommended that you install the <a href="http://gerrit.googlecode.com/svn/documentation/2.0/user-changeid.html#creation" target="_blank"><strong>Gerrit Change-Id commit-msg hook documented here</strong></a> which will help Gerrit track changes through rebasing; once that's taken care of, have at it!</p>
<pre><code>banana% git checkout -b topic-branch
banana% <work>
banana% git commit
banana% git push gerrithost HEAD:refs/for/master
</code></pre>
<p>The last command will push my commit to Gerrit, the command is kind of weird looking so feel free to put it behind a <a id="aptureLink_4QD4sdoRxy" href="http://git.or.cz/gitwiki/Aliases">git-alias(1)</a>. After the push is complete however, my changes will be awaiting review in Gerrit
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_openchanges.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_openchanges.png"/></a></center></p>
<p><center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_changeoverview.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_changeoverview.png"/></a></center></p>
<p>At this point, you'd likely wait for another reviewer to come along and either comment your code inline in the side-by-side viewer or otherwise approve the commit bu clicking "Publish Comments"
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_publishcomments.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_publishcomments.png"/></a></center></p>
<p>After comments have been published, the view in My Dashboard has changed to indicate that the change has not only been reviewed but also verified:
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_mydashboard_changesreviewed.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_mydashboard_changesreviewed.png"/></a></center></p>
<p>Upon seeing this, I can return back to my Git repository and feel comfortable merging my code to the master branch:</p>
<pre><code>banana% git checkout master
banana% git merge topic-branch
banana% git push origin master
banana% git push gerrithost master
</code></pre>
<p>The last command is significant again, by pushing the updated master branch to Gerrit, we indicate that the change has been merged, which is also reflected in My Dashboard
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_mydashboard_changesmerged.png" rel="lightbox"><img width="550" src="http://agentdero.cachefly.net/unethicalblogger.com/images/gerrit_mydashboard_changesmerged.png"/></a></center></p>
<p>Tada! You've just had your code reviewed and subsequently integrated into the upstream tree, pat yourself on the back. It's worth noting that while Gerrit is under steady development it <em>is</em> being used by the likes of the Android team, JGit/EGit team and countless others. Gerrit contains a number of nice subtle features, like double-clicking a line inside the side-by-side diff to add a comment to that line specifically, the ability to "star" changes (similar to bookmarking) and a too many others to go into detail in this post.</p>
<p>While it may seem like this was a fair amount of set-up to get code reviews going, the payoff can be tremendous, Gerrit facilitates a solid Git-oriented code review process that scales very well with the number of committers and changes. I hope you enjoy it :)</p>
http://unethicalblogger.com/posts/2009/12/code_review_gerrit_mostly_visual_guide#commentsGitSoftware DevelopmentTue, 08 Dec 2009 06:45:25 +0000R. Tyler Croy241 at http://unethicalblogger.comOn GitHub and how I came to write the fastest Python JSON module in town
http://unethicalblogger.com/posts/2009/12/github_and_how_i_came_write_fastest_python_json_module_town
<p>Perhaps the title is a bit too much ego stroking, yes, I did write the fastest Python module for decoding JSON strings and encoding Python objects to JSON. I didn't however write the parser behind the scenes.</p>
<p>Over the summer I discovered "<a id="aptureLink_n24z7kSMi1" href="http://lloyd.github.com/yajl/">Yet Another JSON Library</a>" on <a id="aptureLink_u0eQz9GMNI" href="http://www.crunchbase.com/company/github">GitHub</a>, written by <a id="aptureLink_YqaYOvz7FP" href="http://twitter.com/lloydhilaiel">Lloyd Hilaiel</a>, jonesing for a Saturday afternoon project I started the "<a id="aptureLink_iih8O9gONv" href="http://search.twitter.com/search?q=py-yajl">py-yajl</a>" project to see if I could implement a Python C module atop Lloyd's marvelous parsing library. After tinkering with the project for a while I got a working prototype building (learning how to define custom types in Python along the way) and let the project stagnate as my weekend ended and the workweek resumed.</p>
<p>A little over a week ago "<a id="aptureLink_S2nwrzEgQp" href="http://github.com/autodata">autodata</a>", another GitHub user, sent me a "Pull Request" with some minor changes to make py-yajl build cleaner on amd64; my interest in the project was suddenly reignited, amazing what a little interest can do for motivation. Over the 10 days following autodata's pull request I discovered that a former colleague of mine and fellow GitHub user "<a id="aptureLink_mY3NgqZfrq" href="http://twitter.com/teepark">teepark</a>" had forked the project as well, working on Python 3 support. Going from zero to <strong>two</strong> people interested in the project, I quickly converted the code from a stagnant, borderline embarrassing, dump of C code into a leak-free, swift JSON library
for Python. Not one to miss out on the fun, I pinged Lloyd who quickly became as enamored with making py-yajl the best Python JSON module available, he forked the project and almost immediately sent a number of pull requests my way with further optimizations to py-yajl such as:</p>
<ul>
<li>Swapping out the use of Python lists to a custom pointer stack for maintaining internal state</li>
<li>Accelerating parsing and handling of Number objects</li>
<li>Pruning a few memory leaks here and there</li>
</ul>
<p>Thanks to <a id="aptureLink_CZHm3Z4vyV" href="http://twitter.com/mikeal">mikeal</a>'s <a id="aptureLink_2E75jRgjq1" href="http://www.mikealrogers.com/archives/695">JSON post</a> and <a href="http://gist.github.com/239887">jsonperf.py</a> script, Lloyd and I could both see how py-yajl was stacking up against <a id="aptureLink_kofLpe0ikl" href="http://pypi.python.org/pypi/python-cjson">cjson</a>, jsonlib, <a id="aptureLink_V0T79aEWbu" href="http://code.google.com/p/jsonlib2/">jsonlib2</a> and <a id="aptureLink_bZhlC8WgRE" href="http://code.google.com/p/simplejson/">simplejson</a>; things got competitive. Below are the most recent <code>jsonperf.py</code> results with py-yajl v0.1.1:</p>
<pre><code>json.loads: 6470.22037ms
simplejson.loads: 202.21063ms
yajl.loads: 145.32621ms
cjson.decode: 102.44788ms
json.dumps: 2309.15286ms
cjson.encode: 276.49586ms
simplejson.dumps: 201.59785ms
yajl.dumps: 161.00153ms
</code></pre>
<p>Over the coming days or weeks (as time permits) I'm planning on adding JSON stream parsing support, i.e. parsing a stream of data as it's coming in off a socket or file object, as well as a few other miscellaneous tasks.</p>
<p>Given the nature of GitHub's social coding dynamic, py-yajl got off the ground as a project but Yajl itself gained an IRC channel (#yajl on Freenode) and a mailing list ([email protected]). To date I have over 20 unique repositories on GitHub (i.e. authored by me) but the experience around Yajl has been the most exciting and finally proved the "social coding" concept beneficial to me.</p>
http://unethicalblogger.com/posts/2009/12/github_and_how_i_came_write_fastest_python_json_module_town#commentsGitPythonSoftware DevelopmentFri, 04 Dec 2009 09:30:09 +0000R. Tyler Croy239 at http://unethicalblogger.comDo you love Git too?
http://unethicalblogger.com/posts/2009/11/do_you_love_git_too
<p>In addition to RSS feeds, one of my favorite sources of reading material is the <a id="aptureLink_kVnWAHwnNd" href="http://git-scm.org">Git mailing list</a>; I'm not really active, I simply enjoy reading the discussions around code and the best solutions for certain problems. If you read the list long enough, you'll start to appreciate the time and attention the Git core developers (<a id="aptureLink_xnlR489xfT" href="http://www.linkedin.com/pub/shawn-pearce/0/a93/61">spearce</a>, <a id="aptureLink_m0cWtPFy7a" href="http://peff.net/peff/">peff</a> and <a id="aptureLink_GNv5qRpV4O" href="http://gitster.livejournal.com">junio</a> (a.k.a. gitster)) put into cultivating the code and in cultivating new contributors. Of all the open source projects I watch to one extent or another, Git is very effective at bringing in new contributors and getting their contributions vetted for inclusion.</p>
<p>If you're a heavy Git user (like me) you can certainly see the results of their tireless efforts, Junio's (git.git's maintainer) in particular. I highly recommend checking out his Amazon <a href="http://www.amazon.com/gp/registry/wishlist/1513KNZE30W63">wishlist</a> to thank him for his efforts.</p>
http://unethicalblogger.com/posts/2009/11/do_you_love_git_too#commentsGitWed, 04 Nov 2009 06:46:08 +0000R. Tyler Croy237 at http://unethicalblogger.comJython, JGit and co. in Hudson
http://unethicalblogger.com/posts/2009/07/jython_jgit_and_co_hudson
<p>At the <a href="http://wiki.hudson-ci.org/display/HUDSON/BayAreaMeetup">Hudson Bay Area Meetup/Hackathon</a>
that <a href="http://slideinc.github.com">Slide, Inc.</a> hosted last weekend, I worked on the
<a href="http://wiki.hudson-ci.org/display/HUDSON/Jython+Plugin">Jython plugin</a>
and released it just days after releasing a strikingly similar plugin, the
<a href="http://wiki.hudson-ci.org/display/HUDSON/Python+Plugin">Python plugin</a>. I felt
that an explanation might be warranted as to why I would do such a thing.</p>
<p>For those that don't know, <a href="http://hudson-ci.org">Hudson</a> is a Java-based continuous
integration server, one of the best CI servers developed (in my humblest of opinions).
What makes Hudson so great is a <strong>very</strong> solid <a href="http://wiki.hudson-ci.org/display/HUDSON/Extend+Hudson">plugin architecture</a>
allowing developers to extend Hudson to support a wide variety of scripting languages
as well as notifiers, source control systems, and so on (<a href="http://weblogs.java.net/blog/kohsuke/archive/2009/06/growth_of_hudso.html">related post</a>
on the growth of Hudson's plugin ecosystem). Additionally, Hudson supports <em>slaves</em> on
any operating system that Java supports, allowing you to have a central manager (the
"master" Hudson server/node) and a vast network of different machines performing tasks
and executing jobs. Now that you're up to speed, back to the topic at hand.</p>
<p><strong>Jython</strong> versus <strong>Python</strong> plugin. Why bother with either, as <a href="http://twitter.com/gboissinot">@gboissinot</a>
pointed out in <a href="http://twitter.com/gboissinot/status/2619505521">this tweet</a>? The
<em>interesting</em> thing about the Jython plugin, particularly when you use a large number
of slaves is that with the installation of the Jython plugin, suddenly you have the
ability to execute Python script on <strong>every</strong> single slave, regardless of whether or
not they actually have Python installed. The more "third party" that can be moved into
Hudson by way of the plugin system means reduced dependencies and difficulty setting
up slaves to help handle load.</p>
<p>Take the "git" versus the "git2" plugin, the git plugin was recently criticized on the
<a href="irc://irc.freenode.net/hudson">#hudson channel</a> because of it's use of the <a href="http://www.jgit.org/">JGit</a>
library, versus "git2" which invokes <a href="http://git-scm.org">git(1)</a> on the command line.
The latter approach is flawed for a number of reasons, particularly the reliance on the git
command line executables and scripts to return consistent formatting is specious at best
even if you aren't relying on "porcelain" (git community terminology for front-end-ish
script and code sitting on top of the "plumbing", the breakdown is detailed <a href="http://www.kernel.org/pub/software/scm/git/docs/">here</a>).
The command-line approach also means you now have to ensure every one of your slaves
that are likely to be executing builds have the appropriate packages installed.
One the flipside however, with the JGit-based approach, the Hudson slave
agent can transfer the
appropriate bytecode to the machine in question and execute that without relying on
system-dependencies.</p>
<p>The Hudson Subversion plugin takes a similar approach, being based on <a href="http://svnkit.com/">SVNKit</a>.</p>
<p>Being a Python developer by trade, I am certainly not in the "Java Fanboy" camp, but
the efficiencies gained by incorporating Java-based libraries in Hudson plugins and
extensions is a no brainer, the reduction of dependencies on the systems incorporated
in your build farm will save you plenty of time in maintenance and version woes alone.
In my opinion, the benefits of JGit, Jython, SVNKit, and the other Java-based libraries
that are running some of the most highly used plugins in the Hudson ecosystem continue
to outweigh the costs, especially as <a href="http://slideinc.github.com">we</a> find ourselves bringing more and more slaves
online.
<!--break--></p>
http://unethicalblogger.com/posts/2009/07/jython_jgit_and_co_hudson#commentsGitHudsonMiscellaneousOpinionSoftware DevelopmentWed, 22 Jul 2009 06:16:38 +0000R. Tyler Croy219 at http://unethicalblogger.comGit Protip: Split it in half, understanding the anatomy of a bug (git bisect)
http://unethicalblogger.com/posts/2009/03/git_protip_split_it_half_understanding_anatomy_bug_git_bisect
<p>I've been sending "Protip" emails about Git to the rest of engineering here at <a href="http://slide.com">Slide</a> for a while now, using the "Protips" as a means of introducing more interesting and complex features Git offers.</p>
<hr/>
There are those among us who can look at a reproduction case for a bug and <strong><em>just know</em></strong> what the bug is. For the rest of us mere mortals, finding out what change or set of changes actually introduced a bug is extremely useful for figuring out why a particular bug exists. This is even more true for the more elusive bugs or the cases where code "looks" correct and you're stumped as to why the bug exists now, when it didn't yesterday/last week/last month. The options in most classical version control systems you have available to you are to sift through diffs or wade through log message after log message trying to spot the particular change that introduced the regression you're now tasked with resolving. <br/><br />
Fortunately (of course) Git offers a handy feature to assist you in tracking down regressions as they're introduced, <strong>git bisect</strong>. Take the following scenario:<br />
<blockquote>Roger has been working on some lower level changes in a project branch lately. When he left work last night, he ran his unit tests (everything passed), committed his code and went home for the day. When he came in the next morning, per his typical routine, he synchronized his project branch with the master branch to ensure his code wasn't stomping on released changes. For some reason however, after synchronizing his branch, his unit tests started to fail indicating that a bug was introduced in one of the changes that was integrated into Roger's project branch. </p></blockquote>
<p>Before switching to Git, Roger might have spent an hour looking over changes trying to pinpoint what went wrong, but now Roger can use <strong>git bisect</strong> to figure out exactly where the issue is. Taking the commit hash from his last good commit, Roger can walk through changes and pinpoint the issue as follows:</p>
<pre>
## Format for use is: git bisect start [<bad> [<good>...]] [--] [<paths>...]
xdev4% git bisect start HEAD 324d2f2235c93769dd97680d80173388dc5c8253
Bisecting: 10 revisions left to test after this
[064443d3164112554600f6da39a36ffb639787d7] Changed the name of an a/b test.
xdev4%
</pre><p>This will start the bisect process, which is interactive, and start you halfway between the two revisions specified above (see the image below). Following the scenario above, Roger would then run his unit tests. Upon their success, he'd execute "git bisect good" which would move the tree halfway between that "good" revision and the "bad" revision. Roger will continue doing this until he lands on the commit that is responsible for the regression. Knowing this, Roger can either revert that change, or make a subsequent revision that corrects the regression introduced.<br />
<center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/git_bisect.png"/></center><br />
A sample of what this sort of transcript might look like is below:</p>
<pre>
xdev4% git bisect good
Bisecting: -1 revisions left to test after this
[bcf020a6c4ac7cc5df064c66b182b2500470000a] Merge branch 'cjssp' into master
xdev4% git bisect bad
bcf020a6c4ac7cc5df064c66b182b2500470000a is first bad commit
xdev4% git show bcf020a6c4ac7cc5df064c66b182b2500470000a
commit bcf020a6c4ac7cc5df064c66b182b2500470000a
Merge: 62153e2... 064443d...
Author: Chris <chris@foo>
Date: Tue Jan 27 12:57:45 2009 -0800
Merge branch 'cjssp' into master
xdev4% git bisect log
# bad: [7a5d4f3c90b022cb66fd8ea1635c5de6768882d7] Merge branch 'foo' into master
# good: [d1014fd52bebd3c56db37362548e588165b7f299] Merge branch 'bar'
git bisect start 'HEAD' 'd1014fd52bebd3c56db37362548e588165b7f299' '--' 'apps'
# good: [064443d3164112554600f6da39a36ffb639787d7] Changed the name of an a/b test. PLEASE PICK ME UP WITH NEXT PUSH. thx
git bisect good 064443d3164112554600f6da39a36ffb639787d7
# bad: [bcf020a6c4ac7cc5df064c66b182b2500470000a] Merge branch 'cjssp' into master
git bisect bad bcf020a6c4ac7cc5df064c66b182b2500470000a
xdev4% git bisect reset
xdev4%
</pre><p>Instead of spending an hour looking at changes, Roger was able to quickly walk a few revisions and run the unit tests he has to figure out which commit was the one causing trouble, and then get back to work squashing those bugs.</p>
<p>Roger is, like most developers, inherently lazy, and running through a series of revisions running unit tests sounds like "work" that doesn't need to be done. Fortunately for Roger, git-bisect(1) supports the subcommand "<strong>run</strong>" which goes hand in hand with unit tests or other tests. In the example above, let's pretend that Roger had a test case exhibiting the bug he was noticing. What he could actually do is let <strong>git bisect run</strong> automatically run a test script to run his unit tests to find the offending revision i.e.:</p>
<pre>
xdev4% git bisect start HEAD 324d2f2235c93769dd97680d80173388dc5c8253
Bisecting: 10 revisions left to test after this
[064443d3164112554600f6da39a36ffb639787d7] Changed the name of an a/b test.
xdev4% git bisect run ./mytest.sh
</pre><p>After executing the <strong>run</strong> command, git-bisect(1) will binary search the revisions between GOOD and BAD testing whether or not "mytest.sh" returns a zero (success) or non-zero (failure) return code until it finds the commit that causes the test to fail. The end result should be the exact commit the regression was introduced into the tree, after finding this Roger can either grab his rubber chicken and go slap his fellow developer around or fix the issue and get back to playing Nethack.<br/><br />
All in all git-bisect(1) is extraordinarily useful for pinning down bugs and diagnosing issues as they're introduced into the code base.<br/></p>
<p>For more specific usage of `git bisect` refer to it's man page here: <a href="http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html" target="_top">git-bisect(1) man page</a></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2009/03/git_protip_split_it_half_understanding_anatomy_bug_git_bisect#commentsGitSlideSoftware DevelopmentSat, 07 Mar 2009 06:07:13 +0000R. Tyler Croy212 at http://unethicalblogger.comGit Protip: A picture is worth a thousand words (git tag)
http://unethicalblogger.com/posts/2009/01/git_protip_a_picture_worth_a_thousand_words_git_tag
<p>I've been sending weekly "Protip" emails about Git to the rest of engineering here at <a href="http://slide.com">Slide</a> for a while now, using the "Protips" as a means of introducing more interesting and complex features Git offers. Below is the fourth Protip written to date.</p>
<hr/>
<br/><br />
While the concept of "tagging" or "labeling" code is not a new, or original idea that was introduced with Git, our use of tags in a regular workflow does not predate the migration to Git however. At it's most basic level, a "tag" in any version control system is to take a "picture" of how the tree looks at a certain point in time such that it can be re-created later. This can be extremely helpful for both local and team development, take the following scenario for local development using tags:</p>
<div style="margin: 10px; padding: 7px; border: 1px solid #cecece;">
<p>Tim is extremely busy, most of his days working at an exciting, fast-paced start-up seem to fly by. With one particular project Tim is working on, a lot of code is changing at a very fast pace and the branch he's currently working in is stable one minute and destabilized the next. Tim has two basic options for leaving himself "bread-crumbs" to step back in time to a stable or an unstable state. The first, complicated option, is to mark his commit messages with something like "STABLE", etc so he can <span class="geshifilter"><code class="python geshifilter-python">git diff</code></span> or <span class="geshifilter"><code class="python geshifilter-python">git reset --hard</code></span> from the current HEAD to the last stable point of the branch. </p>
<p>
The second option is to make use of tags. Whenever Tim reaches a stable point in his turmultuous development, he can simply run:<br />
<span class="geshifilter"><code class="python geshifilter-python">git tag wip-protips_<span style="color: #66cc66;">`</span>date <span style="color: #483d8b;">"+%s"</span></code></span><br />
(or something similar, `date` added to ensure the tag is unique). If Tim finds himself too far down the wrong path, he can rollback his branch to the latest tag (<span class="geshifilter"><code class="python geshifilter-python">git reset --hard protiptag</code></span>), create a new stable branch based on that tag (<span class="geshifilter"><code class="python geshifilter-python">git checkout -b wip-protip<span style="color: #ff4500;">-2</span> protiptag</code></span>), or diff his current HEAD to the tag to see what all he's changed since his branch was stable (<span class="geshifilter"><code class="python geshifilter-python">git diff protiptag...<span style="color: black;">HEAD</span></code></span>)</p>
</div>
<p>This local development scenario can become a team development scenario involving tags, if for example, Tim needed QA to start testing portions of his branch (his changes are just that important). Since the current HEAD of Tim's branch is incredibly unstable, he can push his tag to the central repository so QA can push a stage using the tag to the last stable point in the branch's history with the command: <span class="geshifilter"><code class="python geshifilter-python">git push origin tag protiptag</code></span></p>
<p>Tags are similar to most other "refs" in Git insofar that they are distributable, if I execute <span class="geshifilter"><code class="python geshifilter-python">git fetch your-repo --tags</code></span>, I can pull the tags you've set in "your-repo" and apply them locally aid development. The distributed nature is primarily how tags differ in Git from Subversion, nearly the rest of the concept is the exact same.</p>
<p>Currently at Slide, tag usage is dominated by the <span class="geshifilter"><code class="python geshifilter-python">post-receive</code></span> hook in the central repository, where <strong>every</strong> push into the central repository ("origin") in the branch release branch is tagged. This allows us to quickly "revert" bad live pushes temporarily, by simply pushing the last "good" tagged release, to ensure minimal site destabilization (while we correct live issues outside of the release branch). </p>
<p>For more specific usage of `git tag` refer to the <a href="http://www.kernel.org/pub/software/scm/git/docs/git-tag.html" target="_top">git-tag(1) man page</a></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a><br />
<!--break--></p>
http://unethicalblogger.com/posts/2009/01/git_protip_a_picture_worth_a_thousand_words_git_tag#commentsGitSlideSoftware DevelopmentFri, 16 Jan 2009 06:14:10 +0000R. Tyler Croy207 at http://unethicalblogger.comFind me on github (rtyler)
http://unethicalblogger.com/posts/2009/01/find_me_github_rtyler
<p>Rod reminded me with <a href="http://unethicalblogger.com/posts/2009/01/im_using_git_because_it_makes_me_feel_cool#comment-715" target="_blank">his comment</a> in one of <a href="http://unethicalblogger.com/posts/2009/01/im_using_git_because_it_makes_me_feel_cool" target="_blank">my other posts</a> that I've not yet mentioned <a href="http://github.com" target="_blank">github</a>.</p>
<p>I've got a bunch of my nonsense thrown up on <a href="https://github.com/rtyler" target="_blank">github.com/rtyler</a>, it's awesome (no really, github rocks my socks, those guys are good people).</p>
http://unethicalblogger.com/posts/2009/01/find_me_github_rtyler#commentsGitMiscellaneousSlideSoftware DevelopmentMon, 05 Jan 2009 10:04:26 +0000R. Tyler Croy204 at http://unethicalblogger.comI'm using Git because it makes me feel cool
http://unethicalblogger.com/posts/2009/01/im_using_git_because_it_makes_me_feel_cool
<p>Let's be honest for a second, anybody who knows me knows that I'm clearly an insecure person; I spend the majority of my time trying my best to appear cool. I've owned a lot of Macs in my life, not because they're solid machines with a fantastic operating system, but because I felt so damn smug and cool whenever I was doing anything on my Macs. I also developed Mac software for a while, not because it was my passion or Objective-C and Cocoa are practically God's gift to software, but because Mac developers are so cool, what with the <a href="http://iamthewalr.us/2009/01/04/my-new-company/about" target="_blank">black-rimmed glasses</a> and fancy coffees. Hell, I remember when I finally traded my MacBook Pro for a Thinkpad running Linux; it had nothing to do with an ideological stance against Apple's treatment of developers or frustrations with Leopard, it was all about the new <em>geek-chic</em> that was Linux. Thus far, my life has basically been one big quest for more leet-points.</p>
<p><big>Then came Git.</big></p>
<p>When I started out in the software world, I was using CVS, which was a notch less cool than a slim IBM salesman's tie. The constant moaning and groaning of fellow developers using CVS, combined with the shame that I felt when I finally told my parents about my use of CVS was too much to bear. I had to switch. </p>
<p>I remember the first time I tried <a href="http://subversion.tigris.org/" target="_blank">Subversion</a>, I remember talking to <a href="http://redterror.net/" target="_blank">Dave</a> and saying "Meh, I'll stick with CVS!" Soon enough, just like the Macarena, Subversion swept the nation up. Subversion was the newest, coolest thing ever, developers rushed into the streets exclaiming "it sucks less than CVS! It sucks less than CVS!" I switched over to Subversion and all of a sudden I was cool again. One by one, open source projects I knew about switched over to Subversion, then <a href="http://sf.net" target="_blank">Source Forge</a> switched over to Subversion and in an instant, Subversion replaced CVS and became the mainstream version control system. Subversion had grown up, gotten married, a 401k and health insurance, how uncool.</p>
<p>After joining <a href="http://www.slide.com" target="_blank">Slide</a>, which used Subversion, I found myself burning up inside. Here I was at this hip start-up, really feeling cool, but still using the same version control system that uncool companies like, <a href="http://developers.yahoo.com" target="_blank">Yahoo!</a> and <a href="http://www.sun.com" target="_blank">Sun</a> use. I would not stand for this. As 2007 became 2008 the writing was on the wall, Git was <a href="http://blogs.chicagotribune.com/news_columnists_ezorn/2008/02/my-new-bicycle.html" target="_blank">our new bicycle</a>. It had been blessed by Saint Torvalds and clearly we needed to get in on the ground floor of the new cool before it became mainstream.</p>
<p>We needed to switch to Git immediately. Who cares if Git is extremely fast, it's not like time is money or something ridiculous like that. What do I care if Git handles branches and merge histories unlike CVS or Subversion? With its immense coolness-factor, I didn't even consider that Git will allow us to work in a decentralized workflow <strong>or</strong> a centralized workflow, nope, didn't even cross my mind. If one were to make a list of Pros and Cons of Git versus whichever other version control system, you could just put "Pro: <strong><u>Cool</u></strong>" at the top of the list, underlined, in bold, and the rest would be moot as far as I'm concerned. </p>
<p>Unlike Subversion or <a href="http://perforce.com" target="_blank">Perforce</a>, Git doesn't have corporate backing, Git is distributed, like a guerilla-force sweeping through the jungle ready to pownce on an unsuspecting platoon; that's freakin' cool. Git rides a motorcycle, wears a leather jacket, makes women swoon and kicks ass and/or jukeboxes. </p>
<p><a href="http://whygitisbetterthanx.com/" target="_blank">Git</a> is the Fonz. Cool.</p>
<p>Don't make any false assumptions about my feelings towards Git, it's not like it's a clearly superior version control system or anything, I'm using it only because I want to be cool too.<br />
<!--break--></p>
http://unethicalblogger.com/posts/2009/01/im_using_git_because_it_makes_me_feel_cool#commentsGitLinuxMiscellaneousOpinionSoftware DevelopmentSun, 04 Jan 2009 14:42:02 +0000R. Tyler Croy202 at http://unethicalblogger.comGit Protip: By commiting that revision, you fucked us (git revert)
http://unethicalblogger.com/posts/2008/12/git_protip_by_commiting_revision_you_fucked_us_git_revert
<p>I've been sending weekly "Protip" emails about Git to the rest of engineering here at <a href="http://slide.com">Slide</a> for a while now, using the "Protips" as a means of introducing more interesting and complex features Git offers. Below is the third Protip written to date.</p>
<hr/>
<br/></p>
<p>The concept of "revert" in Git versus Subversion is an interesting, albeit subtle, change, mostly in part due to the subtle differences between Git and Subversion's means of tracking commits. Just as with Subversion, you can only revert a committed change, unlike Subversion there is a 1-to-1 mapping of a "commit" to a "revert". The basic syntax of revert is quite easy: <span class="geshifilter"><code class="python geshifilter-python">git revert 0xdeadbeef</code></span>, and just like a regular commit, you will need to push your changes after you revert if you want others to receive the revert as well. <br />
In the following example of a revert of a commit, I also use the "-s" argument on the command line to denote that I'm signing-off on this revert (i.e. I've properly reviewed it).</p>
<pre>
xdev3% git revert -s c20054ea390046bd3a54693f2927192b2a7097c2
----------------[Vim]----------------
Revert "merge-to-release unhide 10000 coin habitat"
This reverts commit c20054ea390046bd3a54693f2927192b2a7097c2.
Signed-off-by: R. Tyler Ballance <tyler@slide.com>
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch wip-protips
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: bt/apps/pet/data.py
</pre><pre>
+ python bt/qa/git/post-commit.py -m svn@slide.com
Sending a commit mail to svn@slide.com
Created commit a6e93b8: Revert "merge-to-release unhide 10000 coin habitat"
1 files changed, 4 insertions(+), 3 deletions(-)
</pre><p><br/><br />
<!--break--></p>
<h2><a name="Reverting_multiple_commits"></a> Reverting multiple commits </h2>
<p>Since <span class="geshifilter"><code class="python geshifilter-python">git revert</code></span> will generate a new commit for you every time you revert a previous commit, reverting multiple commits is not as obvious (side note: I'm aware of the ability to squash commits, or --no-commit for git-revert(1), I dislike compressing revision history when I don't believe there shouldn't be compression). If you want to revert a specific merge from one branch into the other, you can revert the merge commit (provided one was generated when the changes were merged). Take the following example:</p>
<pre>
commit 81a94bb976dfaaaae42ae2600b7e9e88645ebd81
Merge: 8134d17... d227dd8...
Author: R. Tyler Ballance <tyler@slide.com>
Date: Thu Nov 20 10:15:16 2008 -0800
Merge branch 'master' into wip-protips
</pre><p><br/><br />
I want to revert this merge since it refreshed my <strong>wip-protips</strong> branch from master, and brought in a lot changes tat have destablized my branch. In the case of reverting a merge commit, you need to specify <strong>-m</strong> and a number to denote where the mainline branch for Git to pivot off of is, <strong>-m 1</strong> usually suffices. So the revert of the commit above will look something like this:</p>
<pre>
git revert 81a94bb976dfaaaae42ae2600b7e9e88645ebd81 -m 1
</pre><p><br/><br />
Then my revert commit will be committed after I review the change in Vim:</p>
<pre>
commit 8cae4924c4c05dadaaeccb3851cfc9ec1b8efd0f
Author: R. Tyler Ballance <tyler@slide.com>
Date: Thu Nov 20 10:20:44 2008 -0800
Revert "Merge branch 'master' into wip-protips"
This reverts commit 81a94bb976dfaaaae42ae2600b7e9e88645ebd81.
</pre><p><br/><br />
Let's take the extreme case where I don't have a merge commit to pivot off of, or I have a particular <strong>set</strong> of bare revisions that I need to revert in one pass, you can start to tie Git subcommands together like <strong>git-rev-list(1)</strong> to accomplish this. This hypothetical situation might occur if some swath of changes have been applied to a team-master that need to be backed out. Without a merge commit to key off of, you have to revert the commits one by one, but that doesn't mean you have to revert each one by hand:<br />
<span class="geshifilter"><code class="bash geshifilter-bash"><span style="color: #000000; font-weight: bold;">for</span> r <span style="color: #000000; font-weight: bold;">in</span> `git rev-list master...master-fubar --<span style="color: #007800;">since=</span><span style="color: #ff0000;">"8:00"</span> --<span style="color: #007800;">before=</span><span style="color: #ff0000;">"12:00"</span> --no-merges`; <span style="color: #000000; font-weight: bold;">do</span> git revert --no-edit -s <span style="color: #007800;">$r</span>; <span style="color: #000000; font-weight: bold;">done</span></code></span><br />
In the above example, I can use <strong>git-rev-list(1)</strong> to give me a list of revisions that have occurred on "master-fubar" that have not occurred on "master" between the times of 8 a.m. and 12 p.m., excluding merge commits. Since <strong>git-rev-list(1)</strong> will return a list of commit hashes by default, I can loop through those commit hashes in chronological order and revert each one. The inner part of the loop signs-off on the revert (-s) and then tells <strong>git-revert(1)</strong> to auto-commit it without opening the commit message in Vim (--no-edit). What this then outputs is the following:</p>
<pre>
xdev% for r in `git rev-list master...master-fubar --since="8:00" --before="12:00" --no-merges`; do git revert --no-edit -s $r; done
Finished one revert.
Created commit b6810d7: Revert "a test, for you"
1 files changed, 1 insertions(+), 1 deletions(-)
Finished one revert.
Created commit 83156bd: Revert "These are not the droids you are looking for
1 files changed, 2 insertions(+), 0 deletions(-)
Finished one revert.
Created commit 782f328: Revert "commented out stuff"
1 files changed, 0 insertions(+), 3 deletions(-)
Finished one revert.
Created commit 2b8d664: Revert "back on again"
1 files changed, 1 insertions(+), 1 deletions(-)
xdev%
</pre><p><br/><br />
For specific usage of "git-revert" or "git-rev-list" refer to the <a href="http://www.kernel.org/pub/software/scm/git/docs/git-revert.html" target="_top">git-revert(1) man page</a> or the <a href="http://www.kernel.org/pub/software/scm/git/docs/git-rev-list.html" target="_top">git-rev-list(1) man page</a></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/12/git_protip_by_commiting_revision_you_fucked_us_git_revert#commentsGitSlideSoftware DevelopmentWed, 10 Dec 2008 10:35:59 +0000R. Tyler Croy201 at http://unethicalblogger.comGit Protip: Learning from your history (git log)
http://unethicalblogger.com/posts/2008/12/git_protip_learning_your_history_git_log
<p>I've been sending weekly "Protip" emails about Git to the rest of engineering here at <a href="http://slide.com">Slide</a> for a while now, using the "Protips" as a means of introducing more interesting and complex features Git offers. Below is the second Protip written to date.</p>
<hr/>
<br/></p>
<p>
One of the major benefits to using Git is the entirety of the repository being entirely local and easily searched/queried. For this, Git has a very useful command called <span class="geshifilter"><code class="python geshifilter-python">git log</code></span> which allows you to inspect revision histories in numerous different ways between file paths, branches, etc. There are a couple basic scenarios where <span class="geshifilter"><code class="python geshifilter-python">git log</code></span> has become invaluable, for me at least, in order to properly review code but also to track changes effectively from point A to point B.</p>
<ul>
<li> <strong>What's Dave been working on lately?</strong> (<strong><em>with diffs</em></strong>)
<ul>
<li><span class="geshifilter"><code class="python geshifilter-python"> git log -p --no-merges --author=dave</code></span></li>
</ul>
<p>The <strong>--no-merges</strong> option will prevent <strong>git log</strong> from displaying merge commits which are automatically generated whenever you pull from one Git branch to another</p>
</li>
<li> <strong>Before I merge this branch down to my team master, I want to know what files have been changed and what revisions</strong>
<ul>
<li><span class="geshifilter"><code class="python geshifilter-python">git log --name-status master-topfriends...<span style="color: black;">proj</span>-topfriends-thing</code></span></li>
</ul>
<p>
Git supports the ability with <strong>git log</strong> and with <strong>git diff</strong> to provide unidirectional branch lookups or bidirectional branch lookups. For example, say the left branch has commits "A, B" and the right branch has commits "A, C". The <strong>...</strong> syntax will output "C", whereas <strong>..</strong> will output "B, C"
</p>
</li>
<li> <strong>I just got back from a vacation, I wonder what's changed?</strong>
<ul>
<li><span class="geshifilter"><code class="python geshifilter-python">git log --since=<span style="color: #483d8b;">"2 weeks ago"</span> --name-status -- templates</code></span></li>
</ul>
<p>At the tail end of a <strong>git log</strong> command you can specify particular paths to look up the histories for with the <strong>--</strong> operator, in this case, I will be looking at the changes that have occured in the templates directory over the past two weeks</p>
</li>
<li> <strong>Most recent X number of commits?</strong> (<strong><em>with diffs</em></strong>)
<ul>
<li> <span class="geshifilter"><code class="python geshifilter-python">git log -n <span style="color: #ff4500;">10</span> --no-merges -p</code></span>
</li>
</ul>
</li>
</ul>
<p>All <span class="geshifilter"><code class="python geshifilter-python">git log</code></span> commands automatically filter into <span class="geshifilter"><code class="python geshifilter-python">less<span style="color: black;">(</span><span style="color: #ff4500;">1</span><span style="color: black;">)</span></code></span> so you can page through the output like you would normally if you executed a <span class="geshifilter"><code class="python geshifilter-python">svn log | less</code></span>. Because <span class="geshifilter"><code class="python geshifilter-python">git log</code></span> is simply reading from the locally stored revision history you can quickly grep the history by any number of different search criteria to gain a better understanding of how the code base is changing and where.</p>
<p>For more specific usage of `git log` refer to the <a href="http://www.kernel.org/pub/software/scm/git/docs/git-log.html" target="_blank">git log man page</a></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/12/git_protip_learning_your_history_git_log#commentsGitSlideSoftware DevelopmentTue, 02 Dec 2008 22:03:40 +0000R. Tyler Croy200 at http://unethicalblogger.comGit Protip: Stash the goods yo (git stash)
http://unethicalblogger.com/posts/2008/11/git_protip_stash_goods_yo_git_stash
<p>For about a month now I've been sending weekly "Protip" emails about Git to the rest of engineering here at <a href="http://slide.com">Slide</a>. I've been using them to slowly and casually introduce some of the more "interesting" features Git has to offer as we move away from Subversion entirely. Below is the first Protip I sent around, I'll be sure to send the rest in good time.</p>
<hr/>
<br/></p>
<p>
Given the nature of how Git is structured, in that your "working copy" is also your "repository" you might find yourself switching branches relatively often. While you can actually switch branches with uncommited changes outstanding in your Git directory, it's not advised (as you might forget you're commiting changes in the wrong branch, etc). You have two options if you are halfway through some work, one is to commit a checkpoint revision, but the other is to make use of the <span class="geshifilter"><code class="python geshifilter-python">git stash</code></span> command.
</p>
<p>A scenario where this becomes especially useful would be: Bill is working in his local branch "wip-funtime" on replacing large swaths of unnecessary useless code and Ted accidentally pushes some of Bill's other changes from another branch live and things <em>break</em>. Bill <em>could</em> commit his code and write a fairly uninformative log message like "checkpoint" which cheapens the value of the revision history of his changes <strong>or</strong> Bill can use <span class="geshifilter"><code class="python geshifilter-python">git stash</code></span> to snapshot his currently working state and context switch. In this case Bill would execute the following commands:
<ul>
<li> <span class="geshifilter"><code class="python geshifilter-python">git stash</code></span>
</li>
<li> <span class="geshifilter"><code class="python geshifilter-python">git checkout master-media</code></span>
</li>
<li> <em>perform hotfixes</em>
</li>
<li> <span class="geshifilter"><code class="python geshifilter-python">git checkout wip-funtime</code></span>
</li>
<li> <span class="geshifilter"><code class="python geshifilter-python">git stash pop</code></span>
</li>
</ul>
<p>After performing the <span class="geshifilter"><code class="python geshifilter-python">git stash pop</code></span> command, Bill's Git repository will be in the exact same state, all his uncommitted changes in tact, as it was when he originally stashed and context-switched.</p>
<p />
For specific usage of `git stash` refer to the <a href="http://www.kernel.org/pub/software/scm/git/docs/git-stash.html" target="_top">git stash man page</a></p>
<p />
<p />
<h3><a name="Example_usage_of_git_stash"></a><a name="Example_usage_of_git_stash_"></a> Example usage of `git stash` </h3>
<p><strong>Stashing changes away</strong><br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> git stash</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">Saved working directory and index state <span style="color: #ff0000;">"WIP on master-topfriends: 7b1ce9e... TOS copy fix"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">(</span>To restore them <span style="color: #7a0874; font-weight: bold;">type</span> <span style="color: #ff0000;">"git stash apply"</span><span style="color: #7a0874; font-weight: bold;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">HEAD is now at 7b1ce9e TOS copy fix</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> </div></li></ol></pre></div><strong>Looking at the stash</strong><br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> git stash list</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">}</span>: WIP on master-topfriends: 7b1ce9e... TOS copy fix</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">}</span>: On master-topfriends: starfruit <span style="color: #7a0874; font-weight: bold;">complete</span> patchset</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">2</span><span style="color: #7a0874; font-weight: bold;">}</span>: On wip-classmethod: starfruit patches</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> </div></li></ol></pre></div><strong>Grabbing the latest from the stash</strong><br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> git stash pop</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">Dropped refs<span style="color: #000000; font-weight: bold;">/</span>stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">}</span> <span style="color: #7a0874; font-weight: bold;">(</span>94b9722b5a999c32c4361d795ee8f368d8412f9a<span style="color: #7a0874; font-weight: bold;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> </div></li></ol></pre></div><strong>Grabbing a specific stash</strong><br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> git stash list</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">0</span><span style="color: #7a0874; font-weight: bold;">}</span>: WIP on master-topfriends: 7b1ce9e... TOS copy fix</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">}</span>: On master-topfriends: starfruit <span style="color: #7a0874; font-weight: bold;">complete</span> patchset</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">stash<span style="color: #000000; font-weight: bold;">@</span><span style="color: #7a0874; font-weight: bold;">{</span><span style="color: #000000;">2</span><span style="color: #7a0874; font-weight: bold;">}</span>: On wip-classmethod: starfruit patches</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span> git stash apply <span style="color: #000000;">2</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># On branch master-topfriends</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># Changed but not updated:</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># (use "git add <file>..." to update what will be committed)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: db/dbroot.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: gogreen/coro.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: py/bin/_makepyrelease.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: py/initpkg.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: py/misc/_dist.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: py/misc/testing/test_initpkg.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: py/path/local/local.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;"># modified: py/test/terminal/terminal.py</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">tyler<span style="color: #000000; font-weight: bold;">@</span>starfruit:~<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">source</span><span style="color: #000000; font-weight: bold;">/</span>git<span style="color: #000000; font-weight: bold;">/</span>main<span style="color: #000000; font-weight: bold;">/</span>bt<span style="color: #000000; font-weight: bold;">></span></div></li></ol></pre></div></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/11/git_protip_stash_goods_yo_git_stash#commentsGitSlideTue, 25 Nov 2008 07:50:06 +0000R. Tyler Croy199 at http://unethicalblogger.comWhy we chose Git, a rebuttal.
http://unethicalblogger.com/posts/2008/11/why_we_chose_git_a_rebuttal
<p>One thing I learned early on in the internet, when is was more of a cobbling instead of a series, of tubes, was not to feed trolls. <img src="http://agentdero.cachefly.net/unethicalblogger.com/images/xkcd_duty_calls.png" height="250" align="right"/>That said, I found that my post "<a href="http://www.unethicalblogger.com/posts/2008/11/delightfully_wrong_about_git" target="_blank">Delightfully Wrong About Git</a>" had found it's way into such silly news aggregation machines as <a href="http://www.dzone.com/links/delightfully_wrong_about_git.html" target="_blank">DZone</a>, <a href="http://www.reddit.com/r/git/comments/7fdbd/delightfully_wrong_about_git/" target="_blank">Reddit</a> and <a href="http://news.ycombinator.com/item?id=374970" target="_blank">Hacker News</a>. Some of the points raised in the comments were valid and warrant a response, while the majority of them were the standard responses to <strong>any</strong> discussion about version control "psh, dumb. should have used [Bazaar/Mercurial/Darcs/Subversion/Team Foundation System]"</p>
<p><strong><big>Why not another (D)VCS?</big></strong><br />
One of the most resounding criticisms/questions was this one, why not Bazaar? Why not Mercurial! My favorite, albeit childish, retort is "why?" But I can say that I have tried a variety of other version control systems, Git, Bazaar, CVS, Subversion, Perforce and some other proprietary VCSes at previous employers. While both Darcs and Mercurial seem to be very solid DVCSes, they suffer from a problem of momentum, Darcs in particular. They both appear to be victims of Git's success, while there is inherently nothing wrong with either of them, they are competing with Linus' love-child, Git. When chosing to move to a new VCS in a company that is well over 50+ employees, the staying power of the technology you chose is important. I feel confident that Git will not only be supported, but actively developed and improved for years to come.</p>
<p>More importantly than that though, <strong>I like Git</strong>. Is that not enough right there? <a href="http://slide.com" target="_blank">Slide</a> makes excessive use of branches, tags and other "complex" VCS concepts that centralized systems like CVS and Subversion have trouble with.<img src="http://agentdero.cachefly.net/unethicalblogger.com/images/branch_madness.jpeg" width="300" align="left" hspace="4" alt="Git Branch Madness!" title="Git Branch Madness!"/> With Subversion creating branches in the volume in which we create branches spiralled out of control with branches becoming "stale" quickly, meaning that if we didn't refresh the branch regularly with updates from trunk it would be nearly impossible to cleanly merge back down into trunk. With my current Git clone of our primary repository, I have 23 branches (roughly 6 personal local branches, 5 old branches, and 12 active branches). Our primary Git repository has been online for about 6 months and currently has 68 branches in it, rougly 55 are active.<br />
<blockquote>Why all the love for Git, but nobody every talks about Bazaar, Mercurial, Darcs, etc? Sure Git is faster, but unless you've got a enormous code base (like the linux kernel), it seems like Bazaar or Mercurial would be a better choice than Git.</p></blockquote>
<p>One of the better known selling points of Git is that <strong>it's fast</strong>. My cloned repository of the primary Slide Git repository weighs in at a hefty <strong>7.1GB</strong>. The latest revision number in our Subversion repository is in the 103,000 range, tacked onto that our tree is just over 2GB in size, and you've got a lot of history to keep track of. Git handles this without a sweat. despite hitting the disk extremely hard when switching to a very out of date branch. With <a href="http://kerneltrap.org/mailarchive/git/2008/10/29/3859494" target="_blank">this fix</a> from Nico, the last of the mmap(2) allocation issues we were experiencing vanished as well.</p>
<p><strong><big>Stop re-inventing the wheel!</big></strong><br />
One of the more interesting sentiments I noticed perusing the various comments made regarding my previous post were that we are "re-inventing the wheel" by writing scripts, hooks and other wrappers to use a product like Git. The notion that having scripts and hooks for something you use in daily development is re-inventing the wheel, or gratuituous strikes me as laughable at best. We're developers. We write scripts. Why didn't I ever write a myriad of scripts when I was an avid Subversion user? <a href="http://www.unethicalblogger.com/posts/r_tyler_ballance/subversion_branching_with_less_pain" target="_blank"><strong>I did.</strong></a>. There's an enormous different between writing scripts to compensate for a poorly performing product, and writing scripts to further enhance you or your colleagues' workflows, <a href="http://www.kernel.org/pub/software/scm/git/docs/githooks.html" target="_blank">Git's hook support</a> falls into the latter category.</p>
<p>The "religion" aspect of the whole version control debate was never considered in our transition to Git, nor was the buzz. I'm far more interested in what makes other VCSes better or worse than Git, so that Git can be improved instead of a justification to ditch Git for yet-another-dvcs. I like to think of the various tools like version control that we developers use as something more relatable: work pants. A good pair of work pants should be flexible enough to allow you to get your work done, modest enough to stay out of the way and most importantly, a good pair of work pants should keep your junk safe ;)</p>
<p>I'm still happy to answer more specific questions about Git and how/why it works for us as well as it has, but I think most of the questions I've seen thus far have been answered above.<br />
<br/></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/11/why_we_chose_git_a_rebuttal#commentsGitOpinionSlideSoftware DevelopmentTue, 25 Nov 2008 04:24:38 +0000R. Tyler Croy198 at http://unethicalblogger.comGit integration with Hudson and Trac.
http://unethicalblogger.com/posts/2008/11/git_integration_with_hudson_and_trac
<p>As I mentioned in my <a href="http://www.unethicalblogger.com/posts/2008/11/delightfully_wrong_about_git" target="_blank">previous post about Git</a> at <a href="http://slide.com">Slide</a>, I wanted to answer some questions that we had to answer to migrate to Git for our development workflow. One of the major questions that had to be answered, especially for our QA department to sign off on the idea was:<br />
<blockquote>How will Git integrate with Hudson, Trac and our other pieces of development infrastructure?</p></blockquote>
<p>For us to use any version control system, centralized or decentralized, there had to be a "central" point for changes to integrate into in order for us to properly test releases and then ship them to the live site. With this requirement, we oriented our use of Git around a centralized repository which developers pull from, and push to on a regular basis.</p>
<p>In order for Git to integrate into Trac and Hudson, we opted for baking the functionality we needed into the post-receive hook on the centralized repository instead of relying on <a href="http://trac-hacks.org/wiki/GitPlugin" target="_blank">GitTrac</a>, or the <a href="http://hudson.gotdns.com/wiki/display/HUDSON/Git+Plugin" target="_blank">Hudson Git plugin</a> to do what we needed it do to.</p>
<p>You can find the script below, or <a href="https://github.com/rtyler/slide-git-scripts/tree" target="_blank">in this GitHub repository</a>. The script requires the <a href="http://trac-hacks.org/wiki/XmlRpcPlugin" target="_blank">Trac XML-RPC</a> plugin to be installed in order to properly annotate tickets when changes are pushed into the central repository. The notation syntaxes that the post-receive.py script supports in commit messages are:<br />
<blockquote>re #12345<br />
qa #12345<br />
attn bbum,fspeirs</p></blockquote>
<p><!--break--><br />
As one might expect, the first notation: "<strong>re #12345</strong>" will simply annotate a ticket with the commit message and the branch in which the commit was pushed into. The "<strong>qa #12345</strong>" notation part of an internal notation of marking tickets in Trac as "Ready for QA", which let's our QA engineers know when tickets are ready to be verified; a "qa" note in a commit message will reference the commit and change the status of the ticket in question. The final notation that the script supports: "<strong>attn bbum,fspeirs</strong>" is purely for calling attention to a code change, or to ask for a code review. When a commit is pushed to the central repository with "attn" in the commit message, an email with the commit message and diff will be emailed to the specified recipients.</p>
<p>In addition to updating Trac tickets, pushes into any branch that have a <a href="https://hudson.dev.java.net/" target="_blank">Hudson</a> job affiliated will use the Hudson External API to queue a build for that branch. In effect, it you "git push origin master", the post-receive.py script will ping Hudson and ask it to queue a build of the "master" job.</p>
<p>I have included the script inline below for those weary of clicking links like <a href="https://github.com/rtyler/slide-git-scripts/tree" target="_blank">this one to the GitHub repository containing the script</a>. Enjoy :)</p>
<div style="height: 500px; overflow: scroll;"><div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">''</span><span style="color: #483d8b;">'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">Copyright (c) 2008 Slide, Inc</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">Permission is hereby granted, free of charge, to any person obtaining a copy</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">of this software and associated documentation files (the "Software"), to deal</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">in the Software without restriction, including without limitation the rights</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">to use, copy, modify, merge, publish, distribute, sublicense, and/or sell</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">copies of the Software, and to permit persons to whom the Software is</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">furnished to do so, subject to the following conditions:</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">The above copyright notice and this permission notice shall be included in</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">all copies or substantial portions of the Software.</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">THE SOFTWARE.</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">'</span><span style="color: #483d8b;">''</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">''</span><span style="color: #483d8b;">'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;"> For questions, patches, etc contact R. Tyler Ballance <[email protected]></span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">'</span><span style="color: #483d8b;">''</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">getpass</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">re</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">socket</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">smtplib</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">time</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">xmlrpclib</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">optparse</span> <span style="color: #ff7700;font-weight:bold;">import</span> OptionParser</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">MAIL_SERVER = <span style="color: #483d8b;">'your_mail_server.com'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">MAIL_SUFFIX = <span style="color: #483d8b;">'@mycompany.com'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">BUILD_HUDSON = <span style="color: #008000;">True</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">HUDSON_URL = <span style="color: #483d8b;">'http://hudson'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">TRAC_XMLRPC_URL = <span style="color: #483d8b;">'URL_TO_TRAC/projects/MYPROJECT/login/xmlrpc'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> rpcProxy<span style="color: black;">(</span><span style="color: #dc143c;">user</span>=<span style="color: #483d8b;">'qatracbot'</span>, password=<span style="color: #008000;">None</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> password = password <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">getenv</span><span style="color: black;">(</span><span style="color: #483d8b;">'TRAC_PASS'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">xmlrpclib</span>.<span style="color: black;">ServerProxy</span><span style="color: black;">(</span><span style="color: #483d8b;">'https://%s:%s@%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span><span style="color: #dc143c;">user</span>, password, TRAC_XMLRPC_URL<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> _send_commit_mail<span style="color: black;">(</span><span style="color: #dc143c;">user</span>, address, subject, branch, commits, files, diff<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Sending a GITRECEIVE mail to %s'</span> <span style="color: #66cc66;">%</span> address</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> message = <span style="color: #483d8b;">'Commits pushed to %s:<span style="color: #000099; font-weight: bold;">\n</span>--------------------------------------<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>%s<span style="color: #000099; font-weight: bold;">\n</span>--------------------------------------<span style="color: #000099; font-weight: bold;">\n</span>%s<span style="color: #000099; font-weight: bold;">\n</span>--------------------------------------<span style="color: #000099; font-weight: bold;">\n</span>%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>branch, commits, files, diff<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> _send_mail<span style="color: black;">(</span><span style="color: #dc143c;">user</span>, address, subject, message<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> _send_attn_mail<span style="color: black;">(</span><span style="color: #dc143c;">user</span>, destuser, diff<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Sending a "please review" mail to %s'</span> <span style="color: #66cc66;">%</span> destuser</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> message = <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'Good day my most generous colleague! I would hold you in the highest esteem and toast you over my finest wines if you would kindly review this for me<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\t</span> - %(user)s<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>Diff:<span style="color: #000099; font-weight: bold;">\n</span>------------------------------------------------<span style="color: #000099; font-weight: bold;">\n</span>%(diff)s'</span><span style="color: #483d8b;">''</span> <span style="color: #66cc66;">%</span> <span style="color: black;">{</span><span style="color: #483d8b;">'diff'</span> : diff, <span style="color: #483d8b;">'user'</span> : <span style="color: #dc143c;">user</span><span style="color: black;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> addresses = <span style="color: black;">[</span><span style="color: black;">]</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">for</span> d <span style="color: #ff7700;font-weight:bold;">in</span> destuser.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #483d8b;">','</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> addresses.<span style="color: black;">append</span><span style="color: black;">(</span><span style="color: #483d8b;">'%s%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>d, EMAIL_SUFFIX<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> _send_mail<span style="color: black;">(</span><span style="color: #dc143c;">user</span>, addresses, <span style="color: #483d8b;">'Please review this change'</span>, message<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> _send_mail<span style="color: black;">(</span><span style="color: #dc143c;">user</span>, address, subject, contents<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">try</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">isinstance</span><span style="color: black;">(</span>address, <span style="color: #008000;">list</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> address = <span style="color: black;">[</span>address<span style="color: black;">]</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> s = <span style="color: #dc143c;">smtplib</span>.<span style="color: black;">SMTP</span><span style="color: black;">(</span>MAIL_SERVER<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> message = <span style="color: #483d8b;">'From: %s%s<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>To: %s<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>Subject: %s<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>%s<span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span><span style="color: #dc143c;">user</span>, MAIL_SUFFIX, <span style="color: #483d8b;">', '</span>.<span style="color: black;">join</span><span style="color: black;">(</span>address<span style="color: black;">)</span>, subject, contents<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> s.<span style="color: black;">sendmail</span><span style="color: black;">(</span><span style="color: #483d8b;">'%s%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span><span style="color: #dc143c;">user</span>, MAIL_SUFFIX<span style="color: black;">)</span>, address, message<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> s.<span style="color: black;">quit</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">except</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Failed to send the email :('</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> _update_ticket<span style="color: black;">(</span>ticket, message, options=<span style="color: black;">{</span><span style="color: black;">}</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> rpc = rpcProxy<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> rpc.<span style="color: black;">ticket</span>.<span style="color: black;">update</span><span style="color: black;">(</span>ticket, message, options<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> rpc</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> find_re<span style="color: black;">(</span>commit<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">map</span><span style="color: black;">(</span><span style="color: #008000;">int</span>, <span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">(</span>r<span style="color: #483d8b;">'(?i)<span style="color: #000099; font-weight: bold;">\s</span>+re<span style="color: #000099; font-weight: bold;">\s</span>*#([0-9]+)'</span>, commit<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> handle_re<span style="color: black;">(</span>branch, commit, ticket<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Annotating ticket #%s'</span> <span style="color: #66cc66;">%</span> ticket</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> message = <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'The following was committed in "%(branch)s":</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;"> {{{ </span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">%(commit)s }}}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;"> '</span><span style="color: #483d8b;">''</span> <span style="color: #66cc66;">%</span> <span style="color: black;">{</span><span style="color: #483d8b;">'branch'</span> : branch, <span style="color: #483d8b;">'commit'</span> : commit<span style="color: black;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> _update_ticket<span style="color: black;">(</span>ticket, message<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> find_qa<span style="color: black;">(</span>commit<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">map</span><span style="color: black;">(</span><span style="color: #008000;">int</span>, <span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">(</span>r<span style="color: #483d8b;">'(?i)<span style="color: #000099; font-weight: bold;">\s</span>+qa<span style="color: #000099; font-weight: bold;">\s</span>*#([0-9]+)'</span>, commit<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> handle_qa<span style="color: black;">(</span>branch, commit, ticket<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Marking ticket #%s as "ready for QA"'</span> <span style="color: #66cc66;">%</span> ticket</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> message = <span style="color: #483d8b;">''</span><span style="color: #483d8b;">'The following was committed in "%(branch)s":</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;"> {{{ </span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;">%(commit)s }}}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #483d8b;"> '</span><span style="color: #483d8b;">''</span> <span style="color: #66cc66;">%</span> <span style="color: black;">{</span><span style="color: #483d8b;">'branch'</span> : branch, <span style="color: #483d8b;">'commit'</span> : commit<span style="color: black;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> rpc = _update_ticket<span style="color: black;">(</span>ticket, message, options=<span style="color: black;">{</span><span style="color: #483d8b;">'status'</span> : <span style="color: #483d8b;">'qa'</span><span style="color: black;">}</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> find_attn<span style="color: black;">(</span>commit<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">(</span>r<span style="color: #483d8b;">'(?i)<span style="color: #000099; font-weight: bold;">\s</span>+attn<span style="color: #000099; font-weight: bold;">\s</span>*([A-Za-z,]+)'</span>, commit<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> handle_attn<span style="color: black;">(</span>branch, commit, attn<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;"># Unpack commit from this: "commit 5f4c31f3c31347c62d68ecb5f2c9afa3333f4ad0\nAuthor: R. Tyler Ballance <[email protected]>\nDate: Wed Nov 12 16:57:32 2008 -0800 \n\n Merge commit 'git-svn' \n\n \n \n"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">try</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> commit_hash = commit.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #483d8b;">' '</span><span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">1</span><span style="color: black;">]</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">except</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #808080; font-style: italic;"># fuk it</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> diff = <span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">'git show --no-color %s --pretty=format:"Author: %%cn <%%ce>%%n%%s%%n%%n%%b%%n%%n%%H"'</span> <span style="color: #66cc66;">%</span> commit_hash<span style="color: black;">)</span>.<span style="color: black;">read</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">rstrip</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> _send_attn_mail<span style="color: black;">(</span><span style="color: #dc143c;">getpass</span>.<span style="color: black;">getuser</span><span style="color: black;">(</span><span style="color: black;">)</span>, attn, diff<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">def</span> mail_push<span style="color: black;">(</span>address, oldrev, newrev, refname<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">user</span> = <span style="color: #dc143c;">getpass</span>.<span style="color: black;">getuser</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> machine = <span style="color: #dc143c;">socket</span>.<span style="color: black;">gethostname</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> base_git_diff = <span style="color: #483d8b;">'git diff %s %s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>oldrev, newrev<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> files_diffed = <span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">'%s --name-status'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>base_git_diff<span style="color: black;">)</span><span style="color: black;">)</span>.<span style="color: black;">read</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">rstrip</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> full_diff = <span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">'%s -p --no-color'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>base_git_diff<span style="color: black;">)</span><span style="color: black;">)</span>.<span style="color: black;">read</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">rstrip</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #483d8b;">''</span><span style="color: #483d8b;">' git rev-parse --not --branches | grep -v "$new" | git rev-list "$old".."$new" --stdin '</span><span style="color: #483d8b;">''</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> commits = <span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">'git rev-parse --not --branches | grep -v "%s" | git rev-list %s..%s --stdin --pretty=format:"Author: %%cn <%%ce>%%nDate: %%cd %%n%%n %%s %%n%%n %%b %%n %%n-------[post-receive marker]------%%n" --first-parent '</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>newrev, oldrev, newrev<span style="color: black;">)</span><span style="color: black;">)</span>.<span style="color: black;">read</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">rstrip</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> branch = refname.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #483d8b;">'/'</span><span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">-1</span><span style="color: black;">]</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> mail_subject = <span style="color: #483d8b;">'GITRECEIVE [%s/%s] %s files changed'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>machine, branch, <span style="color: #008000;">len</span><span style="color: black;">(</span>files_diffed.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\n</span>'</span><span style="color: black;">)</span><span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> branch == <span style="color: #483d8b;">'master-release'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Tagging release branch'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> tagname = <span style="color: #483d8b;">'livepush_%s'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span><span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">(</span><span style="color: #483d8b;">'%Y%m%d%H%M%S'</span>, <span style="color: #dc143c;">time</span>.<span style="color: black;">localtime</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span><span style="color: #483d8b;">'Creating a tag named: %s<span style="color: #000099; font-weight: bold;">\n</span><span style="color: #000099; font-weight: bold;">\n</span>'</span> <span style="color: #66cc66;">%</span> tagname<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">os</span>.<span style="color: black;">system</span><span style="color: black;">(</span><span style="color: #483d8b;">'git tag %s'</span> <span style="color: #66cc66;">%</span> tagname<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> mail_subject = <span style="color: #483d8b;">'%s (tagged: %s)'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>mail_subject, tagname<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> BUILD_HUDSON_JOB:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Queuing the Hudson job for "%s"'</span> <span style="color: #66cc66;">%</span> branch</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">os</span>.<span style="color: black;">system</span><span style="color: black;">(</span><span style="color: #483d8b;">'/usr/bin/env wget -q -O /dev/null http://%s/job/%s/build'</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>HUDSON_URL, branch<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> _send_commit_mail<span style="color: black;">(</span><span style="color: #dc143c;">user</span>, address, mail_subject, branch, commits, files_diffed, full_diff<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> branch == <span style="color: #483d8b;">'master'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #808080; font-style: italic;"># we don't want to update tickets and such for master/merges</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> commits = <span style="color: #008000;">filter</span><span style="color: black;">(</span><span style="color: #ff7700;font-weight:bold;">lambda</span> c: <span style="color: #008000;">len</span><span style="color: black;">(</span>c<span style="color: black;">)</span>, commits.<span style="color: black;">split</span><span style="color: black;">(</span><span style="color: #483d8b;">'-------[post-receive marker]------'</span><span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> commits.<span style="color: black;">reverse</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">for</span> c <span style="color: #ff7700;font-weight:bold;">in</span> commits:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> c.<span style="color: black;">find</span><span style="color: black;">(</span><span style="color: #483d8b;">'Squashed commit'</span><span style="color: black;">)</span> <span style="color: #66cc66;">></span>= <span style="color: #ff4500;">0</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">continue</span> <span style="color: #808080; font-style: italic;"># Skip bullshit squashed commit</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">for</span> attn <span style="color: #ff7700;font-weight:bold;">in</span> find_attn<span style="color: black;">(</span>c<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> handle_attn<span style="color: black;">(</span>branch, c, attn<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">for</span> ticket <span style="color: #ff7700;font-weight:bold;">in</span> find_re<span style="color: black;">(</span>c<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> handle_re<span style="color: black;">(</span>branch, c, ticket<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">for</span> ticket <span style="color: #ff7700;font-weight:bold;">in</span> find_qa<span style="color: black;">(</span>c<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> handle_qa<span style="color: black;">(</span>branch, c, ticket<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> op = OptionParser<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> op.<span style="color: black;">add_option</span><span style="color: black;">(</span><span style="color: #483d8b;">'-m'</span>, <span style="color: #483d8b;">'--mail'</span>, dest=<span style="color: #483d8b;">'address'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'Email address to mail git push messages to'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> op.<span style="color: black;">add_option</span><span style="color: black;">(</span><span style="color: #483d8b;">'-o'</span>, <span style="color: #483d8b;">'--oldrev'</span>, dest=<span style="color: #483d8b;">'oldrev'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'Old revision we<span style="color: #000099; font-weight: bold;">\'</span>re pushing from'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> op.<span style="color: black;">add_option</span><span style="color: black;">(</span><span style="color: #483d8b;">'-n'</span>, <span style="color: #483d8b;">'--newrev'</span>, dest=<span style="color: #483d8b;">'newrev'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'New revision we<span style="color: #000099; font-weight: bold;">\'</span>re pushing to'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> op.<span style="color: black;">add_option</span><span style="color: black;">(</span><span style="color: #483d8b;">'-r'</span>,<span style="color: #483d8b;">'--ref'</span>, dest=<span style="color: #483d8b;">'ref'</span>, <span style="color: #008000;">help</span>=<span style="color: #483d8b;">'Refname that we<span style="color: #000099; font-weight: bold;">\'</span>re pushing'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> opts, args = op.<span style="color: black;">parse_args</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> opts.<span style="color: black;">address</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff7700;font-weight:bold;">not</span> opts.<span style="color: black;">oldrev</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff7700;font-weight:bold;">not</span> opts.<span style="color: black;">newrev</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #ff7700;font-weight:bold;">not</span> opts.<span style="color: black;">ref</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'*** You left out some needed parameters! ***'</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> exit</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> mail_push<span style="color: black;">(</span>opts.<span style="color: black;">address</span>, opts.<span style="color: black;">oldrev</span>, opts.<span style="color: black;">newrev</span>, opts.<span style="color: black;">ref</span><span style="color: black;">)</span></div></li></ol></pre></div></div>
<p><br/></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/11/git_integration_with_hudson_and_trac#commentsGitSlideSoftware DevelopmentMon, 24 Nov 2008 05:51:04 +0000R. Tyler Croy197 at http://unethicalblogger.comDelightfully Wrong About Git
http://unethicalblogger.com/posts/2008/11/delightfully_wrong_about_git
<p>A very long time ago I mentioned on <a href="http://twitter.com">Twitter</a> that I was looking at Git as a replacement for <a href="http://subversion.tigris.org/" target="_blank">Subversion</a> and <a href="http://perforce.com" target="_blank">Perforce</a> with my personal projects, but lamented moving to <a href="http://git.or.cz/" target="_blank">Git</a> at Slide would not be feasible<center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/git_tweet.png"/></center>Like most disagreements I've had with people on technology in the past, immediately after I said it, I actively tried to prove myself wrong. Back in April when I made the statement above, <a href="http://subversion.tigris.org/" target="_blank">Subversion</a> 1.4 was "good enough" (just barely) for what we wanted to do as far as source control, but I became more and more curious about whether or not we <em>could</em> move to Git. <center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/git_twitter_2.jpeg"/></center></p>
<p>Back in April, after spending a week with projects like <a href="http://wiki.darcs.net/DarcsWiki/Tailor" target="_blank">Tailor</a> and <a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html" target="_blank">git-svn(1)</a> I started to look at the potential of moving just my team over to Git for evaluation purposes. By the end of May I had requested Git to be installed on the machines that we use for development on a day-to-day basis and we moved the team over to Git by the second week of June. </p>
<p>What followed were six months of sloshing uphill, some of the most notable milestones that we had to figure out in this time frame were:
<ul>
<li>Whereas in the Subversion architecture with a central repository there is a very clear development focal point for sharing code between developers, what is this in the <a href="http://git.or.cz/" target="_blank">Git</a> workflow?</li>
<li>How do you ensure developers don't forget code was committed "in that one branch, in that one repository" and keep track of code</li>
<li>How will Git integrate with <a href="https://hudson.dev.java.net/" target="_blank">Hudson</a>, <a href="http://trac.edgewall.org/" target="_blank">Trac</a> and our other pieces of development infrastructure? (<strong><em><a href="http://www.unethicalblogger.com/posts/2008/11/git_integration_with_hudson_and_trac" target="_blank">answered here</a></em></strong>)</li>
</ul>
<p> I'll be answering these questions and share some of the scripts, hooks, and documentation we've written internally to make moving to Git throughout the company a reality. I wish I could say I was responsible for it all, but there were a number of <a href="http://randomoblog.blogspot.com/" target="_blank">other</a> <a href="http://stuffonfire.com/" target="_blank">engineers</a> that were extremely important in defining best practices, and what this shiny new world without Subversion would look like.</p>
<p>At the end of the day, I'm pleased as punch with the transition. I don't hate <a href="http://subversion.tigris.org/" target="_blank">Subversion</a>, I just love <a href="http://git.or.cz/" target="_blank">Git</a>; call me "spoiled" but I think we deserve something more than a system that strives to be "a better <a href="http://i256.photobucket.com/albums/hh165/reddcloudd/failcat.jpg" target="_blank">CVS</a>".</p>
<p><strong>Update:</strong> I've posted an addendum: <a href="http://www.unethicalblogger.com/posts/2008/11/why_we_chose_git_a_rebuttal">Why we chose Git, a rebuttal</a></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/11/delightfully_wrong_about_git#commentsGitSlideSoftware DevelopmentSun, 23 Nov 2008 02:51:50 +0000R. Tyler Croy196 at http://unethicalblogger.comGit back into Subversion, Mostly Automagically (Part 3/3)
http://unethicalblogger.com/posts/2008/10/git_back_subversion_mostly_automagically_part_33
<p>Thus far I've covered most of the issues and hurdles we've addressed while experimenting with <a href="http://git.or.cz/" target="_blank">Git</a> at <a href="http://www.slide.com" target="_blank">Slide</a> in parts <a href="http://unethicalblogger.com/posts/2008/07/experimenting_with_git_slide_part_13" target="_blank"><strong>1</strong></a> and <a href="http://unethicalblogger.com/posts/2008/09/team_development_with_git_part_23" target="_blank"><strong>2</strong></a> of this series, the one thing I've not covered that is <strong>very</strong> important to address is how to work in the "hybrid" environment we currently have at Slide, where as one team works with Git and the rest of the company works in Subversion. Our setup involves having a "Git-to-Subverison" proxy repository such that everything to the "left" of that proxy repository is entirely Subversion without exceptions and everything to the "right" of that repository is entirely Git, without exceptions. Part of my original motivation for putting this post at the end of the series was that, when I originally wrote the first post on "<a href="http://unethicalblogger.com/posts/2008/07/experimenting_with_git_slide_part_13" target="_blank">Experimenting with Git at Slide</a>" I actually didn't have this part of the process figured out. That is to say, I was bringing code back and forth between Git and Subversion courtesy of <a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html" target="_blank">git-svn(1)</a> and some gnarly manual processes. </p>
<h3>No Habla Branching</h3>
<p>The primary issue when bringing changesets from Git to Subversion is based in the major differences between how the two handle branching and changesets to begin with. In theory, projects like <a href="http://progetti.arstecnica.it/tailor" target="_blank">Tailor</a> were created to help solve this issue by first translating both the source and destination repositories into an intermediary changeset format in order to cross-apply changes from one end to the other. Unfortunately after I spent a couple days battling with Tailor, I couldn't get it to properly handle some of the revisions in Slide's three year history.<br />
<!--break--><br />
If you've ever used <strong>git-svn(1)</strong> you might be familiar with the <strong>git-svn dcommit</strong> command, which will work for some percentage of users that want to maintain dual repositories between Git and Subversion, things break down however once you introduce branching into the mix.<br />
<center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/Git-SVN%20Branching.png" border="1"/></center>Up until Subversion 1.5, Subversion had no concept of "merge tracking" (even in 1.5, it requires the server and client to be 1.5, it also makes nasty use of svn props). Without the general support for "merge tracking" the concept of a changeset sourcing from a particular branch or the concept of a "merge commit" are entirely foreign in the land of Subversion. In less mumbo jumbo, this effectively means that the "revisions" that you would want to bring from Git into Subversion need to be "<em>flattened</em>" when being "dcommitted" into Subversion's trunk. Git supports a means of flattening revision history when merging and pulling by way of the "--squash" command line argument, so this flattening for git-svn <em>is</em> possible.</p>
<h2>Giant Disclaimer</h2>
<p>What I'm about to write I dutifully accept as Git-heresy, a nasty hack and not something I'm proud of.</p>
<h3>Flattening into Subversion</h3>
<p>First the icky bash script that supports properly flattening revisions into the "master" branch in the git-svn repository and dcommits the results:<br />
<div style="height: 400px; overflow: scroll;"><div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#!/bin/bash</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #007800;">MERGE_BRANCH=</span>mergemaster</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #007800;">REPO=</span>$<span style="color: #000000;">1</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #007800;">BRANCH=</span>$<span style="color: #000000;">2</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">[</span><span style="color: #7a0874; font-weight: bold;">[</span> -z <span style="color: #ff0000;">"${1}"</span> <span style="color: #000000; font-weight: bold;">||</span> -z <span style="color: #ff0000;">"${2}"</span> <span style="color: #7a0874; font-weight: bold;">]</span><span style="color: #7a0874; font-weight: bold;">]</span>; <span style="color: #000000; font-weight: bold;">then</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> You must provide a <span style="color: #000099; font-weight: bold;">\"</span>remote<span style="color: #000099; font-weight: bold;">\"</span> and a <span style="color: #000099; font-weight: bold;">\"</span>refspec<span style="color: #000099; font-weight: bold;">\"</span> for Git to use!"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> Exiting :("</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">exit</span> <span style="color: #000000;">1</span>;</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">fi</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #007800;">LATEST_COMMIT=</span>`git log --max-<span style="color: #007800;">count=</span><span style="color: #000000;">1</span> --no-merges --<span style="color: #007800;">pretty=</span>format:<span style="color: #ff0000;">"%H"</span>`</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Making sure we're on 'master'"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> setup_mergemaster </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Killing the old mergemaster branch"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git branch -D <span style="color: #007800;">$MERGE_BRANCH</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Creating a new mergemaster branch"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout -b <span style="color: #007800;">$MERGE_BRANCH</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> cleanup</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #c20cb9; font-weight: bold;">rm</span> -f .git<span style="color: #000000; font-weight: bold;">/</span>SVNPULL_MSG</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> prepare_message</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> Pulling from ${REPO}:${BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>REPO<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>> <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>MERGE_BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Merging across change from master to ${MERGE_BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull --no-commit --squash . master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #c20cb9; font-weight: bold;">cp</span> .git<span style="color: #000000; font-weight: bold;">/</span>SQUASH_MSG .git<span style="color: #000000; font-weight: bold;">/</span>SVNPULL_MSG</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> merge_to_svn</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git reset --hard <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>LATEST_COMMIT<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> setup_mergemaster</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> Pulling from ${REPO}:${BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>REPO<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>> <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>MERGE_BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Merging across change from master to ${MERGE_BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull --no-commit --squash . master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Committing..."</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git commit -a -F .git<span style="color: #000000; font-weight: bold;">/</span>SVNPULL_MSG <span style="color: #000000; font-weight: bold;">&&</span> git-svn dcommit --no-rebase</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> cleanup</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">setup_mergemaster</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">prepare_message</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">merge_to_svn</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> All done!"</span></div></li></ol></pre></div></div>
<p>Gross isn't it? There were some interesting things I learned when experimenting with this script, but first I'll explain how the script is used. As I mentioned above there is the "proxy repository", this script operates on the git-svn driven proxy repository, meaning this script is only invoked when code needs to be propogated from Git-to-Subversion as opposed to Subversion-to-Git which git-svn properly supports by default in all cases. Since this is a proxy repository, that means all the "real" code and goings-on occur in the "primary" Subversion, and "primary" Git repositories, so the code is going along this path: Primary_SVN <-> [proxy] <-> Primary_Git<br />
This setup means when we "pull" (or merge) from Primary_Git/master we are going to be flattening at that point in order to properly merge it into the Primary_SVN. Without further ado, here's the breakdown on the pieces of the script:<br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> setup_mergemaster </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Killing the old mergemaster branch"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git branch -D <span style="color: #007800;">$MERGE_BRANCH</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Creating a new mergemaster branch"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout -b <span style="color: #007800;">$MERGE_BRANCH</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li></ol></pre></div>What the setup_mergemaster branch is responsible for is deleting any prior branches that have been used for merging into the proxy repository and Primary_SVN. It gives us a "mergemaster" branch in the git-svn repository that is effectively at the same chronological point in time as the master branch <em>before</em> any merging occurs.<br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> prepare_message</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> Pulling from ${REPO}:${BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>REPO<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>> <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>MERGE_BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Merging across change from master to ${MERGE_BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull --no-commit --squash . master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #c20cb9; font-weight: bold;">cp</span> .git<span style="color: #000000; font-weight: bold;">/</span>SQUASH_MSG .git<span style="color: #000000; font-weight: bold;">/</span>SVNPULL_MSG</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li></ol></pre></div>The prepare_message function is part of the nastiest code in the entire script, in order to get an accurate "squashed commit" commit message when the changesets are pushed into Primary_SVN, we have to generate the commit message separately from the actual merging. Since this function is performing a `git pull` from "master" into "mergemaster" the changesets that are being pulled are going to be the only ones that show up (for reasons I'm about to explain).<br />
<div class="geshifilter"><pre class="geshifilter-bash"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #000000; font-weight: bold;">function</span> merge_to_svn</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">{</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git reset --hard <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>LATEST_COMMIT<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> setup_mergemaster</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"===> Pulling from ${REPO}:${BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>REPO<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>> <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git checkout <span style="color: #007800;">$<span style="color: #7a0874; font-weight: bold;">{</span>MERGE_BRANCH<span style="color: #7a0874; font-weight: bold;">}</span><span style="color: #000000; font-weight: bold;">|</span>></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Merging across change from master to ${MERGE_BRANCH}"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git pull --no-commit --squash . master</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">"==> Committing..."</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> git commit -a -F .git<span style="color: #000000; font-weight: bold;">/</span>SVNPULL_MSG <span style="color: #000000; font-weight: bold;">&&</span> git-svn dcommit --no-rebase</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> cleanup</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #7a0874; font-weight: bold;">}</span></div></li></ol></pre></div>If you noticed above in the full script block, the "LATEST_COMMIT" code, here's where it's used, it is one of the most important pieces of the entire script. Basically the LATEST_COMMIT piece of script grabs the latest non-merge-commit hash from the `git log` output and saves it for later use (here) where it's used to rollback the proxy repository to the point in time <em>just before</em> the last merge commit. This is done to avoid issues with git-svn(1) not understanding how to handle merge commits whatsoever. After rolling back the proxy repository, a new "mergemaster" branch is created. After the mergemaster branch is created, the actual Primary_Git changesets that differ between the proxy repository and Primary_Git are pulled into the proxy repository's master branch, and sqaushed into the mergemaster branch where they are subsequently committed with the commit message that was prepared before. The "prepare_message" part of the script becomes important at that step because the "squashed commit" message that Git generates at this point in time will effectively contain <strong>every</strong> commit that has ever been proxied across in this manner ever.</p>
<p>After the "merge_to_svn" function has been run the "transaction" is entirely completed and the changesets that once differed between Primary_SVN/trunk and Primary_Git/master are now normalized.</p>
<h3>Mostly Automagically</h3>
<p>In the near future I intend on incorporating this script into the post-receive hook on Primary_Git in such a way that will <strong>truly</strong> propogate changesets automatically from Primary_Git into Primary_SVN, but currently I'm utilizing one of my new favorite "hammers', <a href="https://hudson.dev.java.net/" target="_blank">Hudson</a> (see: <a href="http://unethicalblogger.com/posts/2008/08/oneline_automated_testing" target="_blank">One-line Automated Testing</a>). Currently there are two jobs set up for proxying changesets across, the first "Subversion-to-Git" simply polls Subversion for changes and executes a series of commands when changes come in: <strong>git-svn fetch && git merge git-svn && git push $Primary_Git master</strong>. This is fairly straight-forward and fits in line with what git-svn(1) is intended to do. The other job that I created is "Git-to-Subversion" which must be manually invoked by a user, but still will automatically take care of squashing commits into Primary_SVN/trunk (i.e. <strong>bash svnproxy.sh $Primary_Git master</strong>).</p>
<h3>Wrap-up</h3>
<p>Admittedly, this sort of setup leaves a lot to be desired. In the ideal world, Tailor would have coped with both our Git and our Subversion repositories in such a way that would have made this script nothing more than a silly idea I had on a bus. Unfortunately that wasn't case and the time budget I had for figuring out a way to force Tailor to work was about 47.5 hours less than it took me to sit down and write the script above. I'd be interested to see other solutions other organizations are utilizing to migrate from one system to the other, but at the time of this writing I can't honestly say I've heard much about people dealing with the "hybrid" scenario that we have currently at Slide.<br/></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/10/git_back_subversion_mostly_automagically_part_33#commentsGitSlideSoftware DevelopmentThu, 02 Oct 2008 06:41:52 +0000R. Tyler Croy192 at http://unethicalblogger.comTeam Development with Git (Part 2/3)
http://unethicalblogger.com/posts/2008/09/team_development_with_git_part_23
<p>In my last post on Git, <a href="http://unethicalblogger.com/posts/2008/07/experimenting_with_git_slide_part_13" target="_blank">Experimenting with Git at Slide</a>, I discussed most of the technical hurdles that stood in our way with evaluating Git for a Subversion tree that has 90k+ revisions and over 2GB of data held within. As I've learned from any project that involves more than just myself, technology is only half the battle, the other half is the human element. One of the most difficult things to "migrate" when switching to something as critical to a developer's workflow as a <a href="http://en.wikipedia.org/wiki/Revision_control" target="_blank">VCS</a>, is habits, good ones and bad ones.</p>
<h3>The Bad Habits</h3>
<p>When moving my team over to Git, I was able to identify some habits that I view as "bad" that could either be blamed on how we have used Subversion here at Slide, or the development workflow that Subversion encourages. For the sake of avoiding flamewars, I'll say it's 51% us, 49% the system.<br />
<!--break--></p>
<ul>
<li><strong>The Occasional Committer</strong></li>
</ul>
<div style="margin: 25px; margin-bottom: 0px; margin-top: 0px">Chances are that if you're working on "something super important!" you fall into this bad habit. Because of the nature of <strong>trunk</strong> in Subversion, if you commit half-finished work into a team-branch or trunk itself, you could cause plenty of pain for your fellow developers. As a result, you tend to commit at the end of a long day working on something, or only after something has been completed. The 9 hours of sweat and frustration you've spent pounding away on 300 lines of code is now summed up in one commit message:</div>
<blockquote><p>Turns out there was a race-condition here, re #52516</p></blockquote>
<div style="margin: 25px; margin-top: 0px; margin-bottom: 0px;">Now three months from now when you return to the same 300 lines of code and try to figure out what the hell led to this mess, you're left with the commit message above, and nothing more.</div>
<ul>
<li><strong>The Less-than-attentive Developer</strong></li>
</ul>
<div style="margin: 25px; margin-top: 0px; margin-bottom: 0px;">I've worked on a Mac for the majority of my time at Slide, as do most of my compatriots, and sooner or later one of two things will happen:<span class="geshifilter"><code class="bash geshifilter-bash">svn add some<span style="color: #000000; font-weight: bold;">/</span>directory<span style="color: #000000; font-weight: bold;">/</span></code></span> and/or <span class="geshifilter"><code class="bash geshifilter-bash">svn commit</code></span>. This usually results in a second commit to come into the tree with a commit message like:</div>
<blockquote><p>Whoops, accidentally checked in resource forks</p></blockquote>
<div style="margin: 25px; margin-top: 0px; margin-bottom: 0px;">This isn't that large of a problem, except for the implication of the second command there, <span class="geshifilter"><code class="bash geshifilter-bash">svn commit</code></span> will commit <strong>all</strong> outstanding changes in your working copy starting in the current working directory, and recursing through children directories. I'm probably more anal-retentive about my commits than most, but I usually do a <strong>diff</strong> before I commit to make sure I'm aware of what I'm about to commit, but I've seen plenty of developers skip this step.</div>
<ul>
<li><strong>The Over-Confident Merger</strong></li>
</ul>
<div style="margin: 25px; margin-top: 0px; margin-bottom: 0px;">I've fallen into this trap numerous times when merging "old" branches back into trunk, especially with binary files that may have been changed in trunk, or in my branch (hell if I know!). One thing I can speak to anecdotally from our work at Slide, is that the probability of nonsensical conflicts rises with a branch's age. The rate of our repository progresses at about 50 commits to trunk per day (~150 commits across the board), if there is a branch cut from trunk, usually within two weeks it can become <strong>extremely difficult</strong> to merge back into trunk without constant "refreshes" or merges from trunk into the branch. </p>
<p>If you're not careful when folding that branch back down into trunk, you can inadvertantly revert old binary files or even text files to previous states which will usually cause other individuals in the engineering organization gripe at you and your QA department to pelt you with rocks. For bonus points, you could (as I have done before) accidentally commit conflicting files earning a gold star and a dunce hat for the day. This merging pain led me to originally write my <a href="http://unethicalblogger.com/posts/r_tyler_ballance/subversion_branching_with_less_pain" target="_blank">merge-safe.py</a> script so long ago. </div>
<h3>The Slide Way to Git</h3>
<p>Fortunately for us, I think the decentralized nature of Git has helped us enforce some best practices when it comes to the bad habits above. "The Occassional Committer" is all but done away with thanks to the ability to atomically commit and revert revisions at a whim and have those changes not propogated to other developers until there has been an <em>explicit</em> push or pull. </p>
<p>Unfortunately however, "The Less-than-attentive Developer" isn't solved so easily. To date I've sat next to two engineers that were new to Git, and watched them both execute the same fateful command: <span class="geshifilter"><code class="bash geshifilter-bash">git add .</code></span><br />
Not realizing their mistake, they accidentally commit a truckload of build and temporary files (.so, .swp, .pyc, etc) interspersed with their regular work that they meant to commit. Git cannot prevent a developer from shooting themselves in the foot, but it does prevent them from shooting everybody else in the foot along with it (unless they commit, and then push their changes upwards).</p>
<p>"The Over-confident Merger" grows more and more confident in the Git-based workflow. Since Git handles changesets atomically, it becomes trivial to merge branch histories together or cherry-pick one revision and apply to an entirely separate branch. I've not yet seen a Git conflict that wasn't a <strong>true</strong> conflict insofar that it was quite literally one line of code changing in two different ways between branch histories. As an aside, when using <strong>git-svn</strong>, be prepared for all the merging "fun" that Subversion has to offer when propogating changes between the two systems.</p>
<h4>Basic Team Workflow</h4>
<p><center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/basic_slide_workflow.png" rel="lightbox"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/basic_slide_workflow.png" width="470"/></a></center>The manner in which we use Git is more like a centralized-decentralized version control system. We still have a "master" repository, which provides a central synchronization point when pushing stage servers, or when bringing code into Subversion to be pushed to live servers. For any particular project one of the developers will create a branch that will serve as the primary project branch, take the "superpoke-proj" branch as an example. That developer will push this branch to "origin" (or the master repository) such that other developers can "track" that branch and contribute code. For the purposes of this example, let's say Paul and Peter are working in "superpoke-proj", while Paul is working he will incrementally commit his work, but once he has resolved/fixed a ticket, he will perform a <span class="geshifilter"><code class="bash geshifilter-bash">git push</code></span> and then mark the ticket appropriately such that a QA engineer can verify the fix. If Paul and Peter are working on something that "breaks the build" but they need to collaborate on it together, Paul can perform a <span class="geshifilter"><code class="bash geshifilter-bash">git pull</code></span> from Peter and vice versa, and again, once they're done those changes will be <em>pushed</em> to origin. This model allows for developers to work in relative isolation so they're not inadvertantly stepping on each others' toes, but also close enough that they can collaborate in explicit terms, i.e. when they are ready for changes to be propogated to each other or the rest of the team.</p>
<h3>Conclusion</h3>
<p>Our workflow, like most things at companies under 500 employees is still a "work in progress™". I think we've found the right balance thus far for the team between freedom and process the allow for sufficient mucking around in the codebase in a way that provides the most amount of time actually writing code with as little possible time spent dealing with the overhead of anything else (merging, etc). There's nothing inherently special in the way we use Git, but we've found that it works for the way we work, which is to say in a very tight release schedule that's requires multiple branches per week and plenty of merging from branch-to-branch whether it be from another team or another part of the same team.</p>
<p>Of course, your mileage may vary.<br/></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/09/team_development_with_git_part_23#commentsGitSlideSoftware DevelopmentTue, 30 Sep 2008 06:22:02 +0000R. Tyler Croy191 at http://unethicalblogger.comExperimenting with Git at Slide (Part 1/3)
http://unethicalblogger.com/posts/2008/07/experimenting_with_git_slide_part_13
<p>For the past two months I've been experimenting with varying levels of success with <a href="http://git.or.cz/" target="_blank">Git</a> inside of <a href="http://www.slide.com" target="_blank">Slide, Inc.</a>. Currently Slide makes use of <a href="http://subversion.tigris.org/" target="_blank">Subversion</a> and relies heavily on branches in Subversion for everything from project specific branches to release branches (branches that can live anywhere from under 12 hours to three weeks). There are plenty of other blog posts about the pitfalls of branching in Subversion that I won't go into here, suffice to say, it is...sub-par. Below is a rough diagram of our general current workflow with Subversion (I've had some other developers ask me "why don't you just work in trunk?" to which I usually wax poetic about the chaos of trunk when any project gets over 5 active developers (Slide engineering is somewhere between 30-50 engineers)).<br />
<center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/Subversion%20Workflow.png" rel="lightbox"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/Subversion%20Workflow.png" width="460"/></a></center></p>
<ul>
<li><a href="#catch">There's always a catch</a></li>
<li><a href="#svn">Subversion at Slide</a></li>
<li><a href="#toying">Toying with Git</a></li>
<li><a href="#git">Git at Slide</a></li>
</ul>
<p><a name="catch"/></p>
<h3>There's always a catch</h3>
<p>There are three major problems we've run up against with utilizing Subversion as our version control system at Slide:
<ul>
<li>Subversion's "branches" make context switching difficult</li>
<li>Depending on the age of a branch cut from <span style="font-family: monospace;">trunk/</span>, merges and maintainence is between difficult and impossible</li>
<li>Merging Subversion branches into each other causes a near total loss of revision history</li>
</ul>
<p>Given that branches are a critical part of Slide's development process, we've historically looked at branch-strong version control systems as alternatives, such as <a href="http://perforce.com" target="_blank">Perforce</a>. Before I joined Slide in April of 2007, I was a heavy user of Perforce for my own consulting projects as well as for some of my work with the <a href="http://FreeBSD.org" target="_blank">FreeBSD project</a> as part of the Summer of Code program. In fact, my <a href="http://stuffonfire.com/" target="_blank">boss</a> sent out a "Perforce Petition" to our engineering list on my third day at Slide...we still haven't switched away from Perforce. </p>
<p><center><a href="http://agentdero.cachefly.net/unethicalblogger.com/images/git_tweet.png" rel="lightbox"><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/git_tweet.png" width="500"/></a></center></p>
<p>Up until earlier this year I hadn't given it a second thought until the team I was working with grew and grew such that between me and four other engineers we were pushing a release anywhere from once to three times a week. That meant we were creating a Subversion "branch" multiple times a week, and a significant part of my daily routine became merging to our release branch and refreshing project branches from <span style="font-family: monospace;">trunk/</span>. All of a sudden Git was looking prettier and prettier, despite some of its warts. At this point in time I was already using Git for some of my personal projects that I never have time for, so I knew at the bare minimum that it was functional. What I didn't know was how to deploy and use it with a large engineering team that works in very high churn short iterations, like Slide's.<br />
<a name="svn"/></p>
<h3>Subversion at Slide</h3>
<p>Moving our source tree over into a system other than Subversion, from Subverison, was destined to be painful. The tree at Slide is deceptively large, we have a substantial amount of <a href="http://www.python.org" target="_blank">Python</a> running around (as Slide is built, top-to-bottom, in Python) and an incredible amount of Adobe Flash assets (.swf files), Adobe Illustrator assets (.ai files) and plenty of binary files, like images (.png/gif/jpeg). Currently a full checkout of <span style="font-family: monospace;">trunk/</span> is roughly <strong>2.5GB</strong> including artwork, flash, server and web application code. We also have roughly <strong>88k</strong> revisions in Subversion, the summation of three years of the company's existence. Fortunately somebody along the line wrote a script (in Perl however) called "<a href="http://www.kernel.org/pub/software/scm/git/docs/git-svn.html" target="_blank">git-svn(1)</a>" that is designed to do exactly what I needed, move a giant tree from Subversion to Git, from start to finish (similar to <a href="http://public.perforce.com/wiki/SVN2P4" target="_blank">svn2p4</a> in Perforce parlance).</p>
<p><a name="toying"></p>
<h3>Toying with Git</h3>
<p>When I first ran the command `<span style="font-family: monospace;">git-svn init $SVN</span>` I let the the command run for somewhere close to 6-7 hours before completing, I was shocked at the size of the generated repository. If our Git repository were to be left unpacked <span style="font-family: monospace;">.git/</span> <strong>alone</strong> would be close to <strong>9GB</strong>, adding the actual code on top of it, ~11GB. I decided that maybe packing this repository would be a good idea so I ran `<span style="font-family: monospace;">git gc</span>` and went to grab a coke from the fridge ... and the machine ran out of memory. One of our quad-core, 8GB RAM, shared development machines ran out of memory?!<br />
<center><img src="http://agentdero.cachefly.net/unethicalblogger.com/images/do-not-want.jpg"/></center></p>
<p>After lurking in #git on Freenode for a while I determined two things
<ol>
<li>Apparently nobody uses Git for projects this large</li>
<li>Git was retaining too much memory, like a memory leak, but just don't call it a memory leak.</li>
</ol>
<p> To compound this, the rules for memory usage with Git are vastly different between a 32-bit machine and a 64-bit machine, and because we're just that cool, we're using 64-bit machines across the board. The amount of memory Git decides to keep resident while doing repository-intensive operations like the `<span style="font-family: monospace;">git gc</span>`, is 256MB on 32-bit machines, and 8GB on 64-bit machines. As these machines are shared between developers we use of <span style="font-family: monospace;">ulimit(1)</span>, when you limit memory usage with <span style="font-family: monospace;">ulimit(1)</span> it restricts <em>address space</em> meaning both virtual and resident memory. When Git tried to <span style="font-family: monospace;">mmap(2)</span> gigabytes of address space to do it's operations, the kernel stepped in to intervene and started returning <span style="font-family: monospace;">ENOMEM</span> to Git which promptly exited.</p>
<p>After raising this enough times, I finally caught <a href="http://www.spearce.org/" target="_blank">spearce</a> who was able to identify the problem and supply a <a href="http://marc.info/?l=git&m=121558741815427&w=2" target="_blank">patch</a> that fixed the memory allocation issues with Git and a repository of Slide's size. First obstacle overcome, now I could actually test a Git workflow inside of Slide.</p>
<p><a name="git"></p>
<h3>Git at Slide</h3>
<p>Now that I could pack the repository on our development machines, I could get the repository down to a reasonable 3.0GB, i.e. <span style="font-family: monospace;">.git/</span> weighed in at 3GB making a entire tree ~5.5GB (far more managable than 11GB). Despite Git being a decentralized version control system, we still needed some semblance of centralization to ensure a couple basic rules for a sane workflow:
<ul>
<li>A centralize place to synchronize distributed versions of the repository</li>
<li>Changesets cannot be lost, ever.</li>
<li>QA must not be over-burdened when testing releases</li>
</ul>
<p>This meant we needed a centralized, secure, repository which left us two options: Git over WebDav (https/http) <em>or</em> <a href="http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way" target="_blank">Gitosis</a>. After discovering that `<span style="font-family: monospace;">git-http-push(1)</span>, the executable responsible for doing Git pushes over WebDav has tremendous memory issues, I abandoned that as an option (a `<span style="font-family: monospace;">git push</span>` of the repository resulted in memory usage peaking at 11GB virtual, 3.5GB resident memory).</p>
<p>If you are looking to deploy Git for a larger audience in a corporate environment, I highly recommend Gitosis. What Gitosis does is allows for SSH to be used as the transport protocol for Git, and provides authentication by use of limited-shell user accounts and SSH keys; it's not perfect but it's the closest thing to maintainable for larger installations of Git (in my opinion). </p>
<p>So far the experimenting with Git at Slide is pretty localized to just my team, but with a combination of Gitosis, git-svn(1) and some "best practices" defined for handling the new system we've successfully continued development for over the past month without any major issues. </p>
<p>As this post is already quite lengthy, I'll be discussing the following two parts of our experimenting in subsequent posts:
<ul>
<li><a href="http://unethicalblogger.com/posts/2008/09/team_development_with_git_part_23">Team Development with Git</a></li>
<li>Git back to Subversion, mostly automatically.</li>
</ul>
<p><br/></p>
<hr/>
<em>Did you know!</em> <a href="http://www.slide.com/static/jobs">Slide is hiring</a>! Looking for talented engineers to write some good Python and/or JavaScript, feel free to contact me at tyler[at]<a href="http://slide.com">slide</a></p>
http://unethicalblogger.com/posts/2008/07/experimenting_with_git_slide_part_13#commentsGitOpinionSlideSoftware DevelopmentSun, 27 Jul 2008 12:23:48 +0000R. Tyler Croy182 at http://unethicalblogger.com