Notes on Life, ‘Puters and Hawaii

Replacing Braid or Piston (for Git) with 40 lines of Rake

I was playing around with Braid and Piston for Git and didn’t like either of them.  I didn’t feel like Piston was ready for Git and Braid seemed to complex. I just wanted git subtrees.  Braid is doing the same thing but with more code around it. I like simple. It actually turns out that Git has all the tools built in to do sub-tree merging and updating.  It’s really easy with a few sake (or rake) tasks.  I am using this on a rails project with more than a dozen plugins and vendor/rails.

Update 6/7: I added a diff task and corrected a typo

# Git Sub-Trees Sake Tasks
# http://rubyurl.com/BwnY < - Read about the technique vs submodules
#
# This works super good with Rails plugins or when you are actively working
# on two related projects.
#
# Add these tasks with:
#   sake -i http://pastie.caboo.se/213492.rb
#
# Example Usage:
#
#   sake git:subtree:remote
#     Git Repo Url? git://github.com/mislav/will_paginate.git
#     Remote Name? will_paginate
#   sake git:subtree:merge
#     Branch? will_paginate/tags/2.2.2
#     Destination? vendor/plugins/will_paginate
#   git commit -m 'merged in new 2.2.2 will_paginate'
#
# From time to time update your remotes:
#   git remote update
#   -or-
#   git fetch will_paginate
#
# View diffs:
#
#   sake git:subtree:diff
#     Branch? will_paginate/tags/2.2.3
#
# Merge in updates:
#
#   sake git:subtree:update
#     Branch? will_paginate/tags/2.2.3
#   git commit -m 'merged in new 2.2.3 will_paginate'

desc 'Add an external project as a remote'
task 'git:subtree:remote' do
  require "readline"
  url = begin
    print "Git Repo Url? "
    Readline.readline.chomp
  end
  name = begin
    print "Remote Name? "
    Readline.readline.chomp
  end
  `git remote add #{name} #{url}`
  `git config remote.#{name}.fetch refs/heads/*:refs/remotes/#{name}/*`
  `git config --add remote.#{name}.fetch refs/tags/*:refs/remotes/#{name}/tags/*`
  `git config remote.#{name}.tagopt --no-tags`
  `git fetch #{name}`
end

desc 'Merge an external project as a sub-tree'
task 'git:subtree:merge' do
  require "readline"
  branch = begin
    print "Branch? "
    Readline.readline.chomp
  end
  dest = begin
    print "Destination? "
    Readline.readline.chomp
  end
  `git merge --squash -s ours --no-commit #{branch}`
  `git read-tree --prefix=#{dest} -u #{branch}`
end

desc 'Update an existing subtree project'
task 'git:subtree:update' do
  require "readline"
  branch = begin
    print "Branch? "
    Readline.readline.chomp
  end
  `git merge --squash -s subtree --no-commit #{branch}`
end

desc 'Show subtree diff against remote'
task 'git:subtree:diff' do
  require "readline"
  branch = begin
    print "Branch? "
    Readline.readline.chomp
  end
  puts `git-diff-tree -p #{branch}`
end

13 Responses to “Replacing Braid or Piston (for Git) with 40 lines of Rake”

  1. Tim, what was the reason of using the following lines in the git:subtree:add:

    git config remote.#{name}.fetch refs/heads/*:refs/remotes/#{name}/*
    git fetch #{name}

    The first one is triggered by:
    git remote add #{name} #{url}

    and the second one by:
    git remote add -f #{name} #{url}

  2. You don’t want to run fetch immediately because of the lines right after the remote add which tells git how and where to stash those commits. After the remote has fully been configured then and only then do you do the fetch.

  3. Yay, I’m an example!

    So, this looks nice. I suppose if I fix bugs in some plugin, I can push back to its repo/create patches?

  4. [...] Tim Dysinger » Replacing Braid or Piston (for Git) with 40 lines of Rake (tags: git rake piston braid) [...]

  5. Heya,

    FYI, braid does know about branches.

    I replied in more detail on my “braid 0.4 annoucement post”:http://evil.che.lu/2008/4/30/ann-braid-0-4-piston-lookalike-for-git#comments

    Cristi

  6. It seems braid is updated recently with some more features. At first I posted a teasing “I don’t git this project” to Cristis site. But after looking over the changes to 0.4 of Braid it is looking better all the time. I have no doubt that Braid will win as the de-facto subtree tool. http://evil.che.lu/2008/4/30/ann-braid-0-4-piston-lookalike-for-git Good job Cristi.

    I wasn’t trying to say, “here is the only deal in town that you should care about” with this post. Only that git-subtrees is the way to go and they are pretty simple and built into git.

    I think I’ll be sticking with my simple rake tasks for the foreseeable future. I know git well enough that I can deal with most any situation. Braid is overkill for me.

  7. Tim, I like the simplicity of these sake commands, but am left with one question: is there a good way using this technique to get a diff of local changes to one of the subtrees? Being a git newb, I’m scratching my head…

  8. Nathaniel,

    Yes there is. You can use the command

    git-diff-tree -p remotename/branch ; # for a raw patch view
    git-diff-tree –stat remotename/branch ; # for a summary view
    git-diff-tree –help

    And you can add a new sake task for that if you wanted.

    -Tim

  9. Time - excellent, thanks!

  10. I’m a bit lost - where is the git:subtree:add task? It’s listed in the usage but not actually in the code.

    Cheers

    Anton

  11. I recommend checking out Thor. It’s kinda like Rake, but actually testable and has installed tasks like Sake:

    http://github.com/wycats/thor

  12. @Anton,

    Good catch - I had changed the name of a task but not corrected the example usage. I have changed it to be correct now. I also added another task for looking at the diff of the remote vs what’s been previously integrated.

    -Tim

  13. Cheers Tim! I figured it out after a bit of fiddling and guesswork.

    Nice work overall with the script. It really helps git noobies like myself make the transition over to git from svn + piston. Your solution definitely looks to be the cleanest and simplest.

    The best thing about it is I can see exactly what commands you are issuing to git so in time, when my git knowledge starts to expand, I’ll actually be able to understand what the hell is going on! In the mean time I can just be content that it all works as advertised.

    Cheers!

    p.s I know this is a sake issue but you might be able to help - is it possible to tell sake to update the script? At the moment I’m removing the tasks one by one and then reinstalling the script. Is there a cleaner way to do this? I’ve checked sake –help and nothing jumped out at me.

Leave a Reply