CINXE.COM
tbaggery
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>tbaggery</title> <link href="http://tbaggery.com/atom.xml" rel="self"/> <link href="http://tbaggery.com/"/> <updated>2020-03-30T23:15:47-04:00</updated> <id>http://tbaggery.com/</id> <author> <name>Tim Pope</name> <uri>http://tpo.pe/</uri> </author> <entry> <title>Effortless Ctags with Git</title> <link href="http://tbaggery.com/2011/08/08/effortless-ctags-with-git.html"/> <updated>2011-08-08T00:00:00-04:00</updated> <id>http://tbaggery.com/2011/08/08/effortless-ctags-with-git</id> <content type="html"><p>In case you’ve been living under a programming rock, Ctags (specifically <a href="http://ctags.sourceforge.net/">Exuberant Ctags</a>, not the BSD version shipped with OS X) indexes source code to make it easy to jump to functions, variables, classes, and other identifiers in (among other editors) Vim (see <code class="highlighter-rouge">:help tags</code>). The major downside to Ctags is having to manually rebuild that index all the time. That’s where the <a href="http://smartic.us/2009/04/03/creating-ctags-with-git-hooks/">not-so-novel idea</a> of re-indexing from various Git commit hooks comes in.</p> <p>Git hooks are repository specific. <a href="http://blog.tobiascrawley.net/2009/01/01/generating-a-tags-file-from-a-git-hook/">Some</a> would recommend using a script to install said hooks into a given repository. But for me, that’s too manual. Let’s set up a default set of hooks that Git will use as a template when creating or cloning a repository (requires Git 1.7.1 or newer):</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash">git config <span class="nt">--global</span> init.templatedir <span class="s1">'~/.git_template'</span> <span class="nb">mkdir</span> <span class="nt">-p</span> ~/.git_template/hooks</code></pre></figure> <p>Now onto the first hook, which isn’t actually a hook at all, but rather a script the other hooks will call. Place in <code class="highlighter-rouge">.git_template/hooks/ctags</code> and mark as executable:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> <span class="nb">set</span> <span class="nt">-e</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">"/usr/local/bin:</span><span class="nv">$PATH</span><span class="s2">"</span> <span class="nb">trap</span> <span class="s1">'rm -f "$$.tags"'</span> EXIT git ls-files | <span class="se">\</span> ctags <span class="nt">--tag-relative</span> <span class="nt">-L</span> - <span class="nt">-f</span><span class="s2">"</span><span class="nv">$$</span><span class="s2">.tags"</span> <span class="nt">--languages</span><span class="o">=</span><span class="nt">-javascript</span>,sql <span class="nb">mv</span> <span class="s2">"</span><span class="nv">$$</span><span class="s2">.tags"</span> <span class="s2">"tags"</span></code></pre></figure> <p><strong>March 2020 edit: Changed to generate tag file in work tree, not Git dir, because Fugitive no longer provides built-in support for the latter.</strong></p> <p>Making this a separate script makes it easy to invoke <code class="highlighter-rouge">.git/hooks/ctags</code> for a one-off re-index (or <code class="highlighter-rouge">git config --global alias.ctags '!.git/hooks/ctags'</code>, then <code class="highlighter-rouge">git ctags</code>), as well as easy to edit for that special case repository that needs a different set of options to <code class="highlighter-rouge">ctags</code>. For example, I might want to re-enable indexing for JavaScript or SQL files, which I’ve disabled here because I’ve found both to be of limited value and noisy in the warning department.</p> <p>Here come the hooks. Mark all four of them executable and place them in <code class="highlighter-rouge">.git_template/hooks</code>. Use this same content for the first three: <code class="highlighter-rouge">post-commit</code>, <code class="highlighter-rouge">post-merge</code>, and <code class="highlighter-rouge">post-checkout</code> (actually my <code class="highlighter-rouge">post-checkout</code> hook includes <a href="https://github.com/tpope/hookup">hookup</a> as well).</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> .git/hooks/ctags <span class="o">&gt;</span>/dev/null 2&gt;&amp;1 &amp;</code></pre></figure> <p>I’ve forked it into the background so that my Git workflow remains as latency-free as possible.</p> <p>One more hook that oftentimes gets overlooked: <code class="highlighter-rouge">post-rewrite</code>. This is fired after <code class="highlighter-rouge">git commit --amend</code> and <code class="highlighter-rouge">git rebase</code>, but the former is already covered by <code class="highlighter-rouge">post-commit</code>. Here’s mine:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> <span class="k">case</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="k">in </span>rebase<span class="p">)</span> <span class="nb">exec</span> .git/hooks/post-merge <span class="p">;;</span> <span class="k">esac</span></code></pre></figure> <p>Once you get this all set up, you can use <code class="highlighter-rouge">git init</code> in existing repositories to copy these hooks in.</p> <p>So what does this get you? Any new repositories you create or clone will be immediately indexed with Ctags and set up to re-index every time you check out, commit, merge, or rebase. Basically, you’ll never have to manually run Ctags on a Git repository again.</p> </content> </entry> <entry> <title>Bundle While You Git</title> <link href="http://tbaggery.com/2011/02/07/bundle-while-you-git.html"/> <updated>2011-02-07T00:00:00-05:00</updated> <id>http://tbaggery.com/2011/02/07/bundle-while-you-git</id> <content type="html"><p><em>Update: <a href="https://github.com/tpope/hookup">hookup</a> does all this for you, plus migrates your database!</em></p> <p>I recently discovered <a href="http://rvm.beginrescueend.com/">RVM</a>’s ability to create a project <code class="highlighter-rouge">.rvmrc</code> when running <code class="highlighter-rouge">rvm use --rvmrc</code>. Commented out by default is a section for automatically running Bundler when <code class="highlighter-rouge">cd</code>ing to your project:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Bundle while reducing excess noise.</span> <span class="nb">printf</span> <span class="s2">"Bundling your gems. This may take a few minutes on a fresh clone.</span><span class="se">\n</span><span class="s2">"</span> bundle | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s1">'^Using '</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s1">' is complete'</span> | <span class="nb">sed</span> <span class="s1">'/^$/d'</span></code></pre></figure> <p>I excitedly enabled this, as the less I have to run the <code class="highlighter-rouge">bundle</code> command manually, the better. But it quickly became apparent that this is the wrong time to bundle. Nothing changes when I <code class="highlighter-rouge">cd</code> into a project, so why would the bundle be out of date? Except after the initial clone, this trick did nothing but eat valuable seconds (sometimes minutes). What I really needed was a way to bundle right after a <code class="highlighter-rouge">git checkout</code> or <code class="highlighter-rouge">git pull --rebase</code> (or throughout a <code class="highlighter-rouge">git bisect</code>, if we want to get really ambitious).</p> <p>As luck would have it, Git provides a post-checkout hook that runs in all those cases. Let’s take a stab at it, shall we? Create <code class="highlighter-rouge">.git/hooks/post-checkout</code> with the following content and <code class="highlighter-rouge">chmod +x</code> it:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> <span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> Gemfile <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">command</span> <span class="nt">-v</span> bundle <span class="o">&gt;</span>/dev/null<span class="p">;</span> <span class="k">then</span> <span class="c"># $GIT_DIR can cause chaos if Bundler in turn invokes Git.</span> <span class="c"># Unset it in a subshell so it remains set later in the hook.</span> <span class="o">(</span><span class="nb">unset </span>GIT_DIR<span class="p">;</span> <span class="nb">exec </span>bundle<span class="o">)</span> <span class="c"># Even if bundling fails, exit from `git checkout` with a zero status.</span> <span class="nb">true </span><span class="k">fi</span></code></pre></figure> <p>Awesome? You bet, but it runs on every HEAD switch, which means we still lose valuable seconds even when the <code class="highlighter-rouge">Gemfile</code> isn’t updated. Luckily, the post-checkout hook receives the old and new HEAD as arguments, so it’s easy to check for changes to the <code class="highlighter-rouge">Gemfile</code>, <code class="highlighter-rouge">Gemfile.lock</code>, or the project’s gem spec:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$1</span> <span class="o">=</span> 0000000000000000000000000000000000000000 <span class="o">]</span><span class="p">;</span> <span class="k">then</span> <span class="c"># Special case for initial clone: compare to empty directory.</span> <span class="nv">old</span><span class="o">=</span>4b825dc642cb6eb9a060e54bf8d69288fbee4904 <span class="k">else </span><span class="nv">old</span><span class="o">=</span><span class="nv">$1</span> <span class="k">fi if</span> <span class="o">[</span> <span class="nt">-f</span> Gemfile <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">command</span> <span class="nt">-v</span> bundle <span class="o">&gt;</span>/dev/null <span class="o">&amp;&amp;</span> git diff <span class="nt">--name-only</span> <span class="nv">$old</span> <span class="nv">$2</span> | egrep <span class="nt">-q</span> <span class="s1">'^Gemfile|\.gemspec$'</span> <span class="k">then</span> <span class="o">(</span><span class="nb">unset </span>GIT_DIR<span class="p">;</span> <span class="nb">exec </span>bundle<span class="o">)</span> <span class="nb">true </span><span class="k">fi</span></code></pre></figure> <p>What else? Well, we could silence some of the noise like RVM does:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$1</span> <span class="o">=</span> 0000000000000000000000000000000000000000 <span class="o">]</span><span class="p">;</span> <span class="k">then </span><span class="nv">old</span><span class="o">=</span>4b825dc642cb6eb9a060e54bf8d69288fbee4904 <span class="k">else </span><span class="nv">old</span><span class="o">=</span><span class="nv">$1</span> <span class="k">fi if</span> <span class="o">[</span> <span class="nt">-f</span> Gemfile <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">command</span> <span class="nt">-v</span> bundle <span class="o">&gt;</span>/dev/null <span class="o">&amp;&amp;</span> git diff <span class="nt">--name-only</span> <span class="nv">$old</span> <span class="nv">$2</span> | egrep <span class="nt">-q</span> <span class="s1">'^Gemfile|\.gemspec$'</span> <span class="k">then</span> <span class="o">(</span><span class="nb">unset </span>GIT_DIR<span class="p">;</span> <span class="nb">exec </span>bundle<span class="o">)</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s1">'^Using '</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s1">' is complete'</span> <span class="nb">true </span><span class="k">fi</span></code></pre></figure> <p>Left as an exercise to the reader: installing Bundler if it’s not already installed (personally, I still do that from the <code class="highlighter-rouge">.rvmrc</code>), and <a href="http://www.google.com/search?q=init.templatedir">configuring Git to install that hook into new and freshly cloned repositories</a>.</p> <p>Last but not least, here’s a complimentary, complementary pre-commit hook:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/sh</span> git diff <span class="nt">--exit-code</span> <span class="nt">--cached</span> <span class="nt">--</span> Gemfile Gemfile.lock <span class="o">&gt;</span>/dev/null <span class="o">||</span> bundle check</code></pre></figure> </content> </entry> <entry> <title>Reduce Your Rails Schema Conflicts</title> <link href="http://tbaggery.com/2010/10/24/reduce-your-rails-schema-conflicts.html"/> <updated>2010-10-24T00:00:00-04:00</updated> <id>http://tbaggery.com/2010/10/24/reduce-your-rails-schema-conflicts</id> <content type="html"><p><em>Update: <a href="https://github.com/tpope/hookup">hookup</a> does all this for you and more!</em></p> <p>Tired of conflicts on your schema file that boil down to the version specification at the top? Me too. After reading Will Leinweber’s article on <a href="http://bitfission.com/blog/2010/07/auto-merge-gemfile-lock.html">resolving Gemfile.lock conflicts automatically</a>, I decided I could do better. I started with a <a href="http://twitter.com/tpope/status/28627293895">tweet</a> and a <a href="http://gist.github.com/643979">gist</a>, but after seeing the reception it received, I’m expanding it to a full-on blog post.</p> <p>The snippet below goes in your <code class="highlighter-rouge">~/.gitconfig</code>, and is basically a simple algorithm that resolves a schema version conflict by picking the higher number. Personally, I’d rather have an ugly mess in my Git config file than a second file I have to copy around everywhere or a gem I have to install.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[merge "railsschema"] name = newer Rails schema version driver = "ruby -e '\n\ system %(git), %(merge-file), %(--marker-size=%L), %(%A), %(%O), %(%B)\n\ b = File.read(%(%A))\n\ b.sub!(/^&lt;+ .*\\nActiveRecord::Schema\\.define.:version =&gt; (\\d+). do\\n=+\\nActiveRecord::Schema\\.define.:version =&gt; (\\d+). do\\n&gt;+ .*/) do\n\ %(ActiveRecord::Schema.define(:version =&gt; #{[$1, $2].max}) do)\n\ end\n\ File.open(%(%A), %(w)) {|f| f.write(b)}\n\ exit 1 if b.include?(%(&lt;)*%L)'" </code></pre></div></div> <p>Now all that’s left is to tell Git to use that algorithm for <code class="highlighter-rouge">db/schema.rb</code>. You have to do this on a per project basis. You can either commit it to <code class="highlighter-rouge">.gitattributes</code> or keep it locally in <code class="highlighter-rouge">.git/info/attributes</code>.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>db/schema.rb merge=railsschema </code></pre></div></div> <p>That’s it. I haven’t beat on it too hard yet, but it’s handled simple conflicts beautifully.</p> </content> </entry> <entry> <title>Smack a Ho.st</title> <link href="http://tbaggery.com/2010/03/04/smack-a-ho-st.html"/> <updated>2010-03-04T00:00:00-05:00</updated> <id>http://tbaggery.com/2010/03/04/smack-a-ho-st</id> <content type="html"><p>With <a href="http://matthewhutchinson.net/2011/1/10/configuring-subdomains-in-development-with-lvhme">lvh.me</a> being shorter and less offensive, I’ve decided this joke has run its course and am letting the domain expire.</p> </content> </entry> <entry> <title>Episode IV: A New Pope</title> <link href="http://tbaggery.com/2010/02/28/episode-iv-a-new-pope.html"/> <updated>2010-02-28T00:00:00-05:00</updated> <id>http://tbaggery.com/2010/02/28/episode-iv-a-new-pope</id> <content type="html"><p>I’ve moved my blog to Jekyll. I think this is the post where I’m supposed to apologize for falling off the blog wagon and promise to post more in the future, though truth be told I have few regrets and make no promises. My aged Drupal install had gotten to the point where I felt actively discouraged from posting. Now that I’ve rectified that, I’ve at least enabled myself to post in the future if I have any flashes of inspiration. Still, I promise nothing. I only migrated the handful of posts that seemed less than completely obsolete.</p> <p>My migration process produced two noteworthy artifacts: <a href="http://github.com/tpope/vim-liquid">Vim syntax highlighting for Liquid</a> and <a href="http://github.com/tpope/vim-markdown">the same for Markdown</a>. There are existing implementations of both of these, but they had limitations I could not accept (most notably, I couldn’t combine them). The Liquid set has some Jekyll specific goodies like YAML front matter highlighting and support for Pygments <code class="highlighter-rouge">highlight</code> blocks. You need to explicitly specify which types of <code class="highlighter-rouge">highlight</code> blocks you want highlighted and map between the Pygments type and Vim type in your <code class="highlighter-rouge">vimrc</code>:</p> <figure class="highlight"><pre><code class="language-vim" data-lang="vim"><span class="k">let</span> <span class="nv">g:liquid_highlight_types</span><span class="p">=[</span><span class="s2">"html"</span><span class="p">,</span><span class="s2">"erb=eruby"</span><span class="p">,</span><span class="s2">"html+erb=eruby.html"</span><span class="p">]</span></code></pre></figure> </content> </entry> <entry> <title>A Note About Git Commit Messages</title> <link href="http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html"/> <updated>2008-04-19T00:00:00-04:00</updated> <id>http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages</id> <content type="html"><p>I want to take a moment to elaborate on what makes a well formed commit message. I think the best practices for commit message formatting is one of the little details that makes Git great. Understandably, some of the first commits to rails.git have messages of the really-long-line variety, and I want to expand on why this is a poor practice.</p> <p>Here’s a model Git commit message:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Capitalized, short (50 chars or less) summary More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or "Fixes bug." This convention matches up with commit messages generated by commands like git merge and git revert. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, followed by a single space, with blank lines in between, but conventions vary here - Use a hanging indent </code></pre></div></div> <p>Let’s start with a few of the reasons why wrapping your commit messages to 72 columns is a good thing.</p> <ul> <li><code class="highlighter-rouge">git log</code> doesn’t do any special special wrapping of the commit messages. With the default pager of <code class="highlighter-rouge">less -S</code>, this means your paragraphs flow far off the edge of the screen, making them difficult to read. On an 80 column terminal, if we subtract 4 columns for the indent on the left and 4 more for symmetry on the right, we’re left with 72 columns.</li> <li><code class="highlighter-rouge">git format-patch --stdout</code> converts a series of commits to a series of emails, using the messages for the message body. Good email netiquette dictates we wrap our plain text emails such that there’s room for a few levels of nested reply indicators without overflow in an 80 column terminal. (The current rails.git workflow doesn’t include email, but who knows what the future will bring.)</li> </ul> <p>Vim users can meet this requirement by installing my <a href="http://github.com/tpope/vim-git">vim-git runtime files</a>, or by simply setting the following option in your git commit message file:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:set textwidth=72 </code></pre></div></div> <p>For Textmate, you can adjust the “Wrap Column” option under the view menu, then use <code class="highlighter-rouge">^Q</code> to rewrap paragraphs (be sure there’s a blank line afterwards to avoid mixing in the comments). Here’s a shell command to add 72 to the menu so you don’t have to drag to select each time:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ defaults write com.macromates.textmate OakWrapColumns '( 40, 72, 78 )' </code></pre></div></div> <p>More important than the mechanics of formatting the body is the practice of having a subject line. As the example indicates, you should shoot for about 50 characters (though this isn’t a hard maximum) and always, always follow it with a blank line. This first line should be a concise summary of the changes introduced by the commit; if there are any technical details that cannot be expressed in these strict size constraints, put them in the body instead. The subject line is used all over Git, oftentimes in truncated form if too long of a message was used. The following are just a handful of examples of where it ends up:</p> <ul> <li><code class="highlighter-rouge">git log --pretty=oneline</code> shows a terse history mapping containing the commit id and the summary</li> <li><code class="highlighter-rouge">git rebase --interactive</code> provides the summary for each commit in the editor it invokes</li> <li>if the config option <code class="highlighter-rouge">merge.summary</code> is set, the summaries from all merged commits will make their way into the merge commit message</li> <li><code class="highlighter-rouge">git shortlog</code> uses summary lines in the changelog-like output it produces</li> <li><code class="highlighter-rouge">git format-patch</code>, <code class="highlighter-rouge">git send-email</code>, and related tools use it as the subject for emails</li> <li>reflogs, a local history accessible with <code class="highlighter-rouge">git reflog</code> intended to help you recover from stupid mistakes, get a copy of the summary</li> <li><code class="highlighter-rouge">gitk</code> has a column for the summary</li> <li>GitHub uses the summary in various places in their user interface</li> </ul> <p>The subject/body distinction may seem unimportant but it’s one of many subtle factors that makes Git history so much more pleasant to work with than Subversion.</p> </content> </entry> <entry> <title>Example Git Workflows: Maintaining a Long Lived Topic Branch</title> <link href="http://tbaggery.com/2008/04/18/example-git-workflows-maintaining-a-long-lived-topic-branch.html"/> <updated>2008-04-18T00:00:00-04:00</updated> <id>http://tbaggery.com/2008/04/18/example-git-workflows-maintaining-a-long-lived-topic-branch</id> <content type="html"><p>In our <a href="/2008/04/17/example-git-workflows-merging-a-contributor-via-pull.html">previous post in the series</a>, we took a look at how a Rails core committer could best pull in a branch submitted by a contributer. Today, we’re going to be looking at things from the opposite side: How a contributer can best maintain a branch intended for pulling.</p> <p>Note that contrary to the initial assumptions of many, <code class="highlighter-rouge">git pull</code> is not necessarily the be-all end-all solution for getting your work included upstream. For simple bug fixes and features, it’s generally easier for all involved if you submit <code class="highlighter-rouge">git format-patch --stdout</code> output. The real gains of a merge based workflow come when you start to collaborate with others on a topic branch. Having said that, let’s get on with the example.</p> <p>Let’s start with the basics. We’ll be using GitHub for the upstream repository in this example. If you haven’t already done, so grab the clone the latest upstream over at GitHub.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git clone git://github.com/rails/rails.git $ cd rails </code></pre></div></div> <p>Next, set up a public repository. With GitHub, this is straightforward: sign in and click the fork button on <a href="http://github.com/rails/rails">the rails/rails.git repository page</a>. Once that’s done, take note of your push URL and add it to your repository. Here, we use the name <code class="highlighter-rouge">mine</code>, to distinguish it from the <code class="highlighter-rouge">origin</code> remote which is the upstream we cloned from.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git remote add mine git@github.com:yourname/rails.git </code></pre></div></div> <p>With that set up, we can get to work. Start by checking out a branch for your feature. Below, we use <code class="highlighter-rouge">git fetch</code> to download the latest updates, then create a new branch <code class="highlighter-rouge">my_wonderful_feature</code> based off the <code class="highlighter-rouge">master</code> branch of the remote <code class="highlighter-rouge">origin</code>, which is our upstream.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git fetch $ git checkout -b my_wonderful_feature origin/master </code></pre></div></div> <p>Obviously, unless it’s a bug fix or feature targeted for the stable line, we should start by building off the tip of the upstream <code class="highlighter-rouge">master</code> branch, right? Well, not necessarily; that’s really a holdover thought process from Subversion. There are several advantages to picking a recent tag like <code class="highlighter-rouge">v2.0.2</code> and building off of that instead:</p> <ul> <li>No churn of constantly updating to the latest.</li> <li>Not having to worry about breakages in the latest edge. If a test breaks, you know (in theory) that you’re the one that broke it.</li> <li>People can easily test your changes in their application without testing everything else introduced by the particular revision of edge that you forked.</li> </ul> <p>Of course, this is a trade off, as there are advantages to working on edge as well:</p> <ul> <li>Get to leverage the very latest infrastructure changes in Rails.</li> <li>Lesser chance of something that works in your version breaking once it’s merged into a later revision.</li> </ul> <p>The right choice depends both on your feature and the current state of the upstream. Given that the last release of Rails happened back in Subversion and the next is just around the corner, it generally makes sense right now to base things off of a recent commit. However, once 2.1 hits, I’d seriously advise people to consider branching off of <code class="highlighter-rouge">v2.1.0</code> instead. Even if you do choose the edge route, don’t be afraid to back up a few revisions if the current one has broken tests or otherwise doesn’t suit your needs.</p> <p>Okay, so you’ve thought it through and decided on <code class="highlighter-rouge">origin/master</code> for the <code class="highlighter-rouge">my_wonderful_feature</code> branch you’re implementing. You’ve created your branch, done a few commits, and you’re ready to publicize it. Using the <code class="highlighter-rouge">mine</code> remote we created earlier, this is a simple process:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git push mine my_wonderful_feature </code></pre></div></div> <p>If you want the public branch name to be named differently, that’s a snap too:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git push mine my_wonderful_feature:my_humble_request </code></pre></div></div> <p>With that done, your branch is ready to be pulled. Here’s the command a core committer would use to merge your work, which can be mentioned in the Lighthouse ticket, or on #rails-contrib, or however you want to communicate it to the powers that be:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git pull git://github.com/yourname/rails.git my_wonderful_feature </code></pre></div></div> <p>Great. So a few days later, you get some feedback, and decide to do some further work on your branch. If you’re a Subversion veteran, your first reaction is probably to update to the latest edge before continuing:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git pull $ git merge origin/master </code></pre></div></div> <p>But wait! Is there a reason you need to update? Frequently updating complicates the history graph and adds noise to the logs:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Merge branch 'master' of git://github.com/rails/rails </code></pre></div></div> <p>There are certainly times when it’s appropriate to merge the latest upstream, like if there are conflicts that need to be resolved before it can be added upstream. When these times come, go ahead and merge, but give a descriptive commit message:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git merge v2.0.3 -m 'Synchronize with 2.0.3 release' $ git merge origin/master -m 'Leverage new Rails.public_path method' $ git merge origin/master -m 'Resolve conflicts with upstream' </code></pre></div></div> <p>If you’d like to try merging (for example, to test that tests still pass) but discard it when you’re done (for example, you’re planning on doing more work on the branch before publishing), use the following pattern. <code class="highlighter-rouge">HEAD@{1}</code> means “the previous commit referenced by <code class="highlighter-rouge">HEAD</code>”, that is, the commit before the merge.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git merge origin/master $ rake test $ git reset --hard HEAD@{1} </code></pre></div></div> <p>All right, you made the needed changes and your contribution was merged upstream. Here’s the syntax to delete a branch from your remote repository so it doesn’t clutter things up:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git push mine :my_wonderful_feature </code></pre></div></div> </content> </entry> <entry> <title>Example Git Workflows: Merging a Contributor via Pull</title> <link href="http://tbaggery.com/2008/04/17/example-git-workflows-merging-a-contributor-via-pull.html"/> <updated>2008-04-17T00:00:00-04:00</updated> <id>http://tbaggery.com/2008/04/17/example-git-workflows-merging-a-contributor-via-pull</id> <content type="html"><p>Welcome to my series of articles providing example workflows for working with the Rails core Git repository. My first post is designed for Rails core committers and explains one way to deal with contributions that come in the form of a Git URL and a branch name.</p> <p>Based on my interactions with the core, I’d deduced the following are common requirements when merging a contributor’s work, and modeled my example around them:</p> <ul> <li>Preserve history: don’t squash to a single commit</li> <li>Provide a paper trail of which core committer merged the contribution</li> <li>Add a <code class="highlighter-rouge">CHANGELOG</code> entry on top</li> </ul> <p>First, a recommended configuration option. This will add a short summary of the changes a merge introduces to the commit message.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git config --global merge.summary true </code></pre></div></div> <p>On to the merge. The first thing we want to do is bring our <code class="highlighter-rouge">master</code> branch up to date. The assumption here is that we don’t have any local work done in <code class="highlighter-rouge">master</code>: it’s a clean branch tracking the upstream, so that <code class="highlighter-rouge">git pull</code> will simply fast forward the latest changes.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git checkout master $ git pull </code></pre></div></div> <p>Next, let’s pull in our contributor’s branch.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git pull --no-ff --no-commit git://github.com/someone/rails.git branch </code></pre></div></div> <p>We use <code class="highlighter-rouge">--no-ff</code> to disallow fast forwarding, even if the remote branch is up to date. This ensures there will always be a merge commit, providing for the paper trail requirement mentioned earlier. The <code class="highlighter-rouge">--no-commit</code> option gives us a chance to alter the work tree as part of the merge commit. Putting hefty changes here would be confusing, but it is the perfect chance to make <code class="highlighter-rouge">CHANGELOG</code> edits.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ edit activesupport/CHANGELOG $ git add activesupport/CHANGELOG </code></pre></div></div> <p>Once the <code class="highlighter-rouge">CHANGELOG</code> is to your liking, we are ready to finalize it. The commit message is already in place but feel free to replace it if it doesn’t give a clear picture of what changed.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git commit $ git push </code></pre></div></div> </content> </entry> <entry> <title>Best Practices for Contributing to Rails with Git</title> <link href="http://tbaggery.com/2008/04/13/best-practices-for-contributing-to-rails-with-git.html"/> <updated>2008-04-13T00:00:00-04:00</updated> <id>http://tbaggery.com/2008/04/13/best-practices-for-contributing-to-rails-with-git</id> <content type="html"><p>The Ruby on Rails core is now <a href="http://www.github.com/rails/rails">hosted on Git</a> This is great news for Git fans like myself. For those of Rails core contributors who are coming late to the party, here’s a quick list of tips I’ve put together especially for you. This no substitute for <a href="http://www.kernel.org/pub/software/scm/git/docs/tutorial.html">a proper tutorial</a> but rather a Rails biased supplement to one.</p> <p>The first thing you do should be configure a real name and email. By default, Git chooses a default name based on the GECOS data (which is probably right) and a default email based on your login and hostname (which is almost certainly wrong). Best practices dictate you use your real name and email here, not your login, IRC handle, or any other aliases you may have. These fields will be immortalized in the repository history so make sure you get them right.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git config --global user.name "Tim Pope" $ git config --global user.email "foo@gmail.com" </code></pre></div></div> <p>While you’re configuring, you may want to enable coloring for some commands:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git config --global color.diff auto $ git config --global color.status auto $ git config --global color.branch auto $ git config --global color.interactive auto </code></pre></div></div> <p>While Git will accept just about any commit message you feed to it, sticking to best practices makes the log a lot easier to work with. A model commit message is shown below.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Short (50 chars or less) summary of changes More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Write your commit message in the present tense: "Fix bug" and not "Fixed bug." This convention matches up with commit messages generated by commands like git merge and git revert. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here </code></pre></div></div> <p>As far as submitting your work to the Rails core, the workflow here is still being fleshed out. For now, either give a public URL and branch where your contribution can be found, or use the following series of commands to get a file that can be easily applied by anyone with the <code class="highlighter-rouge">git am</code> command to reconstruct your history locally.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git checkout my_funky_branch $ git rebase origin/master $ git format-patch --stdout origin/master.. &gt; my_funky_patches </code></pre></div></div> <p>Here’s a tip for keeping up to date: In lieu of using <code class="highlighter-rouge">git pull</code> to download the latest changes, use <code class="highlighter-rouge">git pull --rebase</code>. Instead of cluttering the history with a merge commit, it reapplies your changes to the latest upstream. The only caveat is that you shouldn’t use this method if you’ve already published the changes to another repository. Doing so would cause problems for anyone who has already downloaded the original commits.</p> </content> </entry> <entry> <title>Easy Ruby Examples</title> <link href="http://tbaggery.com/2007/05/03/easy-ruby-examples.html"/> <updated>2007-05-03T00:00:00-04:00</updated> <id>http://tbaggery.com/2007/05/03/easy-ruby-examples</id> <content type="html"><p>Today I devised a little script to generate Ruby examples showing code and output.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% rubydemo '[1,2,3].inject {|m,o|m+o}' [1,2,3].inject {|m,o|m+o} #=&gt; 6 % rubydemo 1/0 1/0 #=&gt; #&lt;ZeroDivisionError: divided by 0&gt; </code></pre></div></div> <p>Here’s the script I used for generation. (It could have been one line if I didn’t care so much about exception formatting.)</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1">#!/usr/bin/env ruby</span> <span class="nb">print</span> <span class="no">ARGV</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">" "</span><span class="p">)</span> <span class="o">+</span> <span class="s2">" #=&gt; "</span> <span class="k">begin</span> <span class="nb">p</span><span class="p">(</span><span class="nb">eval</span><span class="p">(</span><span class="no">ARGV</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">" "</span><span class="p">),</span><span class="nb">binding</span><span class="p">,</span><span class="s2">"(demo)"</span><span class="p">))</span> <span class="k">rescue</span> <span class="no">Exception</span> <span class="o">=&gt;</span> <span class="n">e</span> <span class="nb">puts</span> <span class="s2">"#&lt;</span><span class="si">#{</span><span class="n">e</span><span class="p">.</span><span class="nf">class</span><span class="si">}</span><span class="s2">: </span><span class="si">#{</span><span class="n">e</span><span class="p">.</span><span class="nf">message</span><span class="p">[</span><span class="sr">/.*/</span><span class="p">]</span><span class="si">}</span><span class="s2">&gt;"</span> <span class="k">end</span></code></pre></figure> <p>The real killer app, though, is using it in conjunction with IRC. Here’s the alias I used in Irssi.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/alias rd exec -nosh - -out rubydemo $* </code></pre></div></div> <p>Now I need merely do <code class="highlighter-rouge">/rd 2+2</code> to get a beautifully formatted <code class="highlighter-rouge">2+2 #=&gt; 4</code> in any conversation.</p> </content> </entry> <entry> <title>Auto-loading Ruby Code</title> <link href="http://tbaggery.com/2007/02/11/auto-loading-ruby-code.html"/> <updated>2007-02-11T00:00:00-05:00</updated> <id>http://tbaggery.com/2007/02/11/auto-loading-ruby-code</id> <content type="html"><p>An incredibly useful technique when using Ruby is to auto-load at start-up a custom library written for exactly that purpose. This is easy to accomplish with a couple of environment variables, but I see very little discussion on the subject. Thus, I’ve written a nice summary of how to go about setting this up.</p> <p>The secret to running code at start-up is <code class="highlighter-rouge">RUBYOPT</code>. You’ve probably seen this environment variable before if you’ve used RubyGems. It is recommended to set this to a value of <code class="highlighter-rouge">rubygems</code>. This is equivalent to always calling ruby as <code class="highlighter-rouge">ruby -rubygems</code>. The <code class="highlighter-rouge">-r</code> option requires a library, so this option essentially does a <code class="highlighter-rouge">require 'ubygems'</code> each time Ruby is started. (The odd name of <code class="highlighter-rouge">ubygems</code> was picked to look nice next to <code class="highlighter-rouge">-r</code>.)</p> <p>We’re going to be doing something similar, but with our own library. I’ve traditionally put this file in <code class="highlighter-rouge">~/.ruby/lib/tpope.rb</code>, but I will be using the more user-agnostic <code class="highlighter-rouge">mine.rb</code> for the purposes of this example.</p> <p>The first thing we need to do is find the right place to set this environment variable, along with setting <code class="highlighter-rouge">RUBYLIB</code> to a path that holds our file. If you are using a bourne compatible shell, like bash, add this to your shell’s rc file (<code class="highlighter-rouge">~/.bashrc</code> for bash):</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ruby/lib/mine.rb"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then </span><span class="nv">RUBYLIB</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.ruby/lib"</span> <span class="nv">RUBYOPT</span><span class="o">=</span><span class="s2">"rmine"</span> <span class="nb">export </span>RUBYLIB RUBYOPT <span class="k">fi</span></code></pre></figure> <p>For C shells, add the following to <code class="highlighter-rouge">~/.cshrc</code>:</p> <figure class="highlight"><pre><code class="language-csh" data-lang="csh">if ( -f "$HOME/.ruby/lib/mine.rb" ) then setenv RUBYLIB "$HOME/.ruby/lib" setenv RUBYOPT "rmine" endif</code></pre></figure> <p>If you are running under a GUI environment, you will want to make this variables known to the top level GUI as well. Both Windows and Mac OS X have interfaces to set environment variables. Some desktop environments running under X11 provide such an interface as well, or you can just add similar lines to <code class="highlighter-rouge">~/.xsession</code> or <code class="highlighter-rouge">~/.xinitrc</code>, depending on your setup.</p> <p>So now that we have this configured, what can we add to this new file? Plenty of things. Let’s start by enhancing the <code class="highlighter-rouge">$LOAD_PATH</code> (also known as <code class="highlighter-rouge">$:</code>). My strategy here is to pop the current working directory off the end, add a few more directories, then restore it.</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">old_current</span> <span class="o">=</span> <span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">pop</span> <span class="k">if</span> <span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">last</span> <span class="o">==</span> <span class="s1">'.'</span> <span class="sx">%w(.ruby/lib ruby/lib .ruby ruby)</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span> <span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">unshift</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s2">"~/</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">"</span><span class="p">))</span> <span class="k">end</span> <span class="no">Dir</span><span class="p">[</span><span class="no">File</span><span class="p">.</span><span class="nf">expand_path</span><span class="p">(</span><span class="s1">'~/ruby/*/lib'</span><span class="p">)].</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span> <span class="vg">$LOAD_PATH</span> <span class="o">&lt;&lt;</span> <span class="n">dir</span> <span class="k">end</span> <span class="vg">$LOAD_PATH</span> <span class="o">&lt;&lt;</span> <span class="n">old_current</span> <span class="k">if</span> <span class="n">old_current</span> <span class="vg">$LOAD_PATH</span><span class="p">.</span><span class="nf">uniq!</span></code></pre></figure> <p>I add a wide variety of directories here, feel free to adjust as you see fit. Note the addition of every directory matching <code class="highlighter-rouge">~/ruby/*/lib</code>, which makes it easy to install new libraries in <code class="highlighter-rouge">~/ruby</code>. This is something that would be tricky to do just by adjusting <code class="highlighter-rouge">RUBYLIB</code>.</p> <p>Next, let’s load RubyGems. We can do this in such a way that it won’t fail on systems where it’s not installed.</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">begin</span> <span class="nb">require</span> <span class="s1">'rubygems'</span> <span class="k">rescue</span> <span class="no">LoadError</span> <span class="k">end</span></code></pre></figure> <p>What else? How about always keeping good old <code class="highlighter-rouge">Symbol#to_proc</code> handy.</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Symbol</span> <span class="k">def</span> <span class="nf">to_proc</span> <span class="no">Proc</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|*</span><span class="n">args</span><span class="o">|</span> <span class="n">args</span><span class="p">.</span><span class="nf">shift</span><span class="p">.</span><span class="nf">__send__</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span><span class="o">*</span><span class="n">args</span><span class="p">)</span> <span class="p">}</span> <span class="k">end</span> <span class="k">end</span></code></pre></figure> <p>One thing to be careful about is not letting your code grow dependent on this auto-loaded library. For example, if you add the above snippet and start doing <code class="highlighter-rouge">pets.each(&amp;:feed)</code> all over the place, your code will break for other people without your library. I still define <code class="highlighter-rouge">Symbol#to_proc</code> because it’s handy for debug statements and quick scripts and it’s easy enough to avoid using where it’s important. But I avoid something more invasive like <code class="highlighter-rouge">require 'active_support'</code> because it’s harder to keep track of everything it provides. Use your own best judgement.</p> </content> </entry> </feed>