Permalink
Browse files

convert readme to markdown

  • Loading branch information...
1 parent 85fea86 commit 6bb41e4c1023495d76bae4dd5d839c8bc5af9092 @mojombo mojombo committed Feb 28, 2009
Showing with 873 additions and 222 deletions.
  1. +4 −0 History.txt
  2. +22 −0 LICENSE
  3. +210 −0 README.md
  4. +0 −222 README.txt
  5. +637 −0 test/fixtures/show_cc
View
4 History.txt
@@ -1,3 +1,7 @@
+==
+ * Minor Enhancements
+ * Convert readme to markdown
+
== 1.0.3 / 2009-02-13
* Minor Enhancements
* Added Grit::Commit#to_patch for plaintext formatted patches.
View
22 LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2007-2009 Tom Preston-Werner
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
210 README.md
@@ -0,0 +1,210 @@
+Grit
+====
+
+Grit gives you object oriented read/write access to Git repositories via Ruby.
+The main goals are stability and performance. To this end, some of the
+interactions with Git repositories are done by shelling out to the system's
+`git` command, and other interactions are done with pure Ruby
+reimplementations of core Git functionality. This choice, however, is
+transparent to end users, and you need not know which method is being used.
+
+This software was developed to power GitHub, and should be considered
+production ready. An extensive test suite is provided to verify its correctness.
+
+Grit is maintained by Tom Preston-Werner, Scott Chacon, Chris Wanstrath, and
+PJ Hyett.
+
+This documentation is accurate as of Grit 1.0.2.
+
+
+## Requirements #############################################################
+
+* git (http://git-scm.com) tested with 1.6.0.2
+
+
+## Install ##################################################################
+
+Easiest install is via RubyGems:
+
+ $ gem install grit
+
+or
+
+ $ gem sources -a http://gems.github.com/ (you only need to do this once)
+ $ gem install mojombo-grit
+
+The gem from GitHub will generally be available sooner than the gem from
+Rubyforge. Both sources will eventually contain the same releases.
+
+
+## Source ###################################################################
+
+Grit's Git repo is available on GitHub, which can be browsed at:
+
+ http://github.com/mojombo/grit
+
+and cloned with:
+
+ git clone git://github.com/mojombo/grit.git
+
+
+## Usage ####################################################################
+
+Grit gives you object model access to your Git repositories. Once you have
+created a `Repo` object, you can traverse it to find parent commits,
+trees, blobs, etc.
+
+### Initialize a Repo object
+
+The first step is to create a `Grit::Repo` object to represent your repo. In
+this documentation I include the `Grit` module to reduce typing.
+
+ require 'grit'
+ include Grit
+ repo = Repo.new("/Users/tom/dev/grit")
+
+In the above example, the directory `/Users/tom/dev/grit` is my working
+directory and contains the `.git` directory. You can also initialize Grit with
+a bare repo.
+
+ repo = Repo.new("/var/git/grit.git")
+
+### Getting a list of commits
+
+From the `Repo` object, you can get a list of commits as an array of `Commit`
+objects.
+
+ repo.commits
+ # => [#<Grit::Commit "e80bbd2ce67651aa18e57fb0b43618ad4baf7750">,
+ #<Grit::Commit "91169e1f5fa4de2eaea3f176461f5dc784796769">,
+ #<Grit::Commit "038af8c329ef7c1bae4568b98bd5c58510465493">,
+ #<Grit::Commit "40d3057d09a7a4d61059bca9dca5ae698de58cbe">,
+ #<Grit::Commit "4ea50f4754937bf19461af58ce3b3d24c77311d9">]
+
+Called without arguments, `Repo#commits` returns a list of up to ten commits
+reachable by the **master** branch (starting at the latest commit). You can
+ask for commits beginning at a different branch, commit, tag, etc.
+
+ repo.commits('mybranch')
+ repo.commits('40d3057d09a7a4d61059bca9dca5ae698de58cbe')
+ repo.commits('v0.1')
+
+You can specify the maximum number of commits to return.
+
+ repo.commits('master', 100)
+
+If you need paging, you can specify a number of commits to skip.
+
+ repo.commits('master', 10, 20)
+
+The above will return commits 21-30 from the commit list.
+
+### The Commit object
+
+`Commit` objects contain information about that commit.
+
+ head = repo.commits.first
+
+ head.id
+ # => "e80bbd2ce67651aa18e57fb0b43618ad4baf7750"
+
+ head.parents
+ # => [#<Grit::Commit "91169e1f5fa4de2eaea3f176461f5dc784796769">]
+
+ head.tree
+ # => #<Grit::Tree "3536eb9abac69c3e4db583ad38f3d30f8db4771f">
+
+ head.author
+ # => #<Grit::Actor "Tom Preston-Werner <tom@mojombo.com>">
+
+ head.authored_date
+ # => Wed Oct 24 22:02:31 -0700 2007
+
+ head.committer
+ # => #<Grit::Actor "Tom Preston-Werner <tom@mojombo.com>">
+
+ head.committed_date
+ # => Wed Oct 24 22:02:31 -0700 2007
+
+ head.message
+ # => "add Actor inspect"
+
+You can traverse a commit's ancestry by chaining calls to `#parents`.
+
+ repo.commits.first.parents[0].parents[0].parents[0]
+
+The above corresponds to **master^^^** or **master~3** in Git parlance.
+
+### The Tree object
+
+A tree records pointers to the contents of a directory. Let's say you want
+the root tree of the latest commit on the **master** branch.
+
+ tree = repo.commits.first.tree
+ # => #<Grit::Tree "3536eb9abac69c3e4db583ad38f3d30f8db4771f">
+
+ tree.id
+ # => "3536eb9abac69c3e4db583ad38f3d30f8db4771f"
+
+Once you have a tree, you can get the contents.
+
+ contents = tree.contents
+ # => [#<Grit::Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">,
+ #<Grit::Blob "81d2c27608b352814cbe979a6acd678d30219678">,
+ #<Grit::Tree "c3d07b0083f01a6e1ac969a0f32b8d06f20c62e5">,
+ #<Grit::Tree "4d00fe177a8407dbbc64a24dbfc564762c0922d8">]
+
+This tree contains two `Blob` objects and two `Tree` objects. The trees are
+subdirectories and the blobs are files. Trees below the root have additional
+attributes.
+
+ contents.last.name
+ # => "lib"
+
+ contents.last.mode
+ # => "040000"
+
+There is a convenience method that allows you to get a named sub-object
+from a tree.
+
+ tree / "lib"
+ # => #<Grit::Tree "e74893a3d8a25cbb1367cf241cc741bfd503c4b2">
+
+You can also get a tree directly from the repo if you know its name.
+
+ repo.tree
+ # => #<Grit::Tree "master">
+
+ repo.tree("91169e1f5fa4de2eaea3f176461f5dc784796769")
+ # => #<Grit::Tree "91169e1f5fa4de2eaea3f176461f5dc784796769">
+
+### The Blob object
+
+A blob represents a file. Trees often contain blobs.
+
+ blob = tree.contents.first
+ # => #<Grit::Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">
+
+A blob has certain attributes.
+
+ blob.id
+ # => "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666"
+
+ blob.name
+ # => "README.txt"
+
+ blob.mode
+ # => "100644"
+
+ blob.size
+ # => 7726
+
+You can get the data of a blob as a string.
+
+ blob.data
+ # => "Grit is a library to ..."
+
+You can also get a blob directly from the repo if you know its name.
+
+ repo.blob("4ebc8aea50e0a67e000ba29a30809d0a7b9b2666")
+ # => #<Grit::Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">
View
222 README.txt
@@ -1,222 +0,0 @@
-grit
- by Tom Preston-Werner, Scott Chacon
- http://github.com/mojombo/grit
-
-== DESCRIPTION:
-
-Grit is a Ruby library for extracting information from a git repository in an
-object oriented manner.
-
-== REQUIREMENTS:
-
-* git (http://git-scm.com) tested with 1.6.0.2
-
-== INSTALL:
-
-Easiest install is via RubyGems:
-
- $ gem install grit
-
-or
-
- $ gem sources -a http://gems.github.com/ (you only need to do this once)
- $ gem install mojombo-grit
-
-The gem from GitHub will generally be available sooner than the gem from
-Rubyforge. Both sources will eventually contain the same releases.
-
-== SOURCE:
-
-Grit's git repo is available on GitHub, which can be browsed at:
-
- http://github.com/mojombo/grit
-
-and cloned from:
-
- git://github.com/mojombo/grit.git
-
-== USAGE:
-
-Grit gives you object model access to your git repository. Once you have
-created a repository object, you can traverse it to find parent commit(s),
-trees, blobs, etc.
-
-= Initialize a Repo object
-
-The first step is to create a Grit::Repo object to represent your repo. I
-include the Grit module so reduce typing.
-
- require 'grit'
- include Grit
- repo = Repo.new("/Users/tom/dev/grit")
-
-In the above example, the directory /Users/tom/dev/grit is my working
-repo and contains the .git directory. You can also initialize Grit with a
-bare repo.
-
- repo = Repo.new("/var/git/grit.git")
-
-= Getting a list of commits
-
-From the Repo object, you can get a list of commits as an array of Commit
-objects.
-
- repo.commits
- # => [#<Grit::Commit "e80bbd2ce67651aa18e57fb0b43618ad4baf7750">,
- #<Grit::Commit "91169e1f5fa4de2eaea3f176461f5dc784796769">,
- #<Grit::Commit "038af8c329ef7c1bae4568b98bd5c58510465493">,
- #<Grit::Commit "40d3057d09a7a4d61059bca9dca5ae698de58cbe">,
- #<Grit::Commit "4ea50f4754937bf19461af58ce3b3d24c77311d9">]
-
-Called without arguments, Repo#commits returns a list of up to ten commits
-reachable by the master branch (starting at the latest commit). You can ask
-for commits beginning at a different branch, commit, tag, etc.
-
- repo.commits('mybranch')
- repo.commits('40d3057d09a7a4d61059bca9dca5ae698de58cbe')
- repo.commits('v0.1')
-
-You can specify the maximum number of commits to return.
-
- repo.commits('master', 100)
-
-If you need paging, you can specify a number of commits to skip.
-
- repo.commits('master', 10, 20)
-
-The above will return commits 21-30 from the commit list.
-
-= The Commit object
-
-Commit objects contain information about that commit.
-
- head = repo.commits.first
-
- head.id
- # => "e80bbd2ce67651aa18e57fb0b43618ad4baf7750"
-
- head.parents
- # => [#<Grit::Commit "91169e1f5fa4de2eaea3f176461f5dc784796769">]
-
- head.tree
- # => #<Grit::Tree "3536eb9abac69c3e4db583ad38f3d30f8db4771f">
-
- head.author
- # => #<Grit::Actor "Tom Preston-Werner <tom@mojombo.com>">
-
- head.authored_date
- # => Wed Oct 24 22:02:31 -0700 2007
-
- head.committer
- # => #<Grit::Actor "Tom Preston-Werner <tom@mojombo.com>">
-
- head.committed_date
- # => Wed Oct 24 22:02:31 -0700 2007
-
- head.message
- # => "add Actor inspect"
-
-You can traverse a commit's ancestry by chaining calls to #parents.
-
- repo.commits.first.parents[0].parents[0].parents[0]
-
-The above corresponds to master^^^ or master~3 in git parlance.
-
-= The Tree object
-
-A tree records pointers to the contents of a directory. Let's say you want
-the root tree of the latest commit on the master branch.
-
- tree = repo.commits.first.tree
- # => #<Grit::Tree "3536eb9abac69c3e4db583ad38f3d30f8db4771f">
-
- tree.id
- # => "3536eb9abac69c3e4db583ad38f3d30f8db4771f"
-
-Once you have a tree, you can get the contents.
-
- contents = tree.contents
- # => [#<Grit::Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">,
- #<Grit::Blob "81d2c27608b352814cbe979a6acd678d30219678">,
- #<Grit::Tree "c3d07b0083f01a6e1ac969a0f32b8d06f20c62e5">,
- #<Grit::Tree "4d00fe177a8407dbbc64a24dbfc564762c0922d8">]
-
-This tree contains two Blob objects and two Tree objects. The trees are
-subdirectories and the blobs are files. Trees below the root have additional
-attributes.
-
- contents.last.name
- # => "lib"
-
- contents.last.mode
- # => "040000"
-
-There is a convenience method that allows you to get a named sub-object
-from a tree.
-
- tree/"lib"
- # => #<Grit::Tree "e74893a3d8a25cbb1367cf241cc741bfd503c4b2">
-
-You can also get a tree directly from the repo if you know its name.
-
- repo.tree
- # => #<Grit::Tree "master">
-
- repo.tree("91169e1f5fa4de2eaea3f176461f5dc784796769")
- # => #<Grit::Tree "91169e1f5fa4de2eaea3f176461f5dc784796769">
-
-= The Blob object
-
-A blob represents a file. Trees often contain blobs.
-
- blob = tree.contents.first
- # => #<Grit::Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">
-
-A blob has certain attributes.
-
- blob.id
- # => "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666"
-
- blob.name
- # => "README.txt"
-
- blob.mode
- # => "100644"
-
- blob.size
- # => 7726
-
-You can get the data of a blob as a string.
-
- blob.data
- # => "Grit is a library to ..."
-
-You can also get a blob directly from the repo if you know its name.
-
- repo.blob("4ebc8aea50e0a67e000ba29a30809d0a7b9b2666")
- # => #<Grit::Blob "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666">
-
-== LICENSE:
-
-(The MIT License)
-
-Copyright (c) 2007 Tom Preston-Werner
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
637 test/fixtures/show_cc
@@ -0,0 +1,637 @@
+commit f8dd1f0b48e1106a62b47cc2927609ca589dc39a
+tree 4c23a5137714e62c52f22e99b3104122868400ab
+parent a0710955e70cbceef8cf805645a447f1b370b966
+parent b724247612be9f55df7914cb86b64703810d7b73
+author administrator <gmalamid@thoughtworks.com> 1224499981 +0100
+committer administrator <gmalamid@thoughtworks.com> 1224499981 +0100
+
+ imported William Morgans utilities too, which are very very nice
+
+diff --cc git-publish-branch
+index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b28b4fde621dff1c72bcb570183bbe923e4a24b2
+new file mode 100755
+--- /dev/null
++++ b/git-publish-branch
+@@@ -1,0 -1,0 +1,70 @@@
+++#!/usr/bin/env ruby
+++
+++## git-publish-branch: a simple script to ease the unnecessarily complex
+++## task of "publishing" a branch, i.e., taking a local branch, creating a
+++## reference to it on a remote repo, and setting up the local branch to
+++## track the remote one, all in one go. you can even delete that remote
+++## reference.
+++##
+++## Usage: git publish-branch [-d] <branch> [repository]
+++##
+++## '-d' signifies deletion. <branch> is the branch to publish, and
+++## [repository] defaults to "origin". The remote branch name will be the
+++## same as the local branch name. Don't make life unnecessarily complex
+++## for yourself.
+++##
+++## Note that unpublishing a branch doesn't delete the local branch.
+++## Safety first!
+++##
+++## git-publish-branch Copyright 2008 William Morgan <wmorgan-git-wt-add@masanjin.net>.
+++## This program is free software: you can redistribute it and/or modify
+++## it under the terms of the GNU General Public License as published by
+++## the Free Software Foundation, either version 3 of the License, or (at
+++## your option) any later version.
+++##
+++## This program is distributed in the hope that it will be useful,
+++## but WITHOUT ANY WARRANTY; without even the implied warranty of
+++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++## GNU General Public License for more details.
+++##
+++## You can find the GNU General Public License at:
+++## http://www.gnu.org/licenses/
+++
+++def exec cmd
+++ puts cmd
+++ system cmd or die unless $fake
+++end
+++
+++def die s=nil
+++ $stderr.puts s if s
+++ exit(-1)
+++end
+++
+++head = `git symbolic-ref HEAD`.chomp.gsub(/refs\/heads\//, "")
+++delete = ARGV.delete "-d"
+++$fake = ARGV.delete "-n"
+++branch = (ARGV.shift || head).gsub(/refs\/heads\//, "")
+++remote = ARGV.shift || "origin"
+++local_ref = `git show-ref heads/#{branch}`
+++remote_ref = `git show-ref remotes/#{remote}/#{branch}`
+++remote_config = `git config branch.#{branch}.merge`
+++
+++if delete
+++ ## we don't do any checking here because the remote branch might actually
+++ ## exist, whether we actually know about it or not.
+++ exec "git push #{remote} :refs/heads/#{branch}"
+++
+++ unless local_ref.empty?
+++ exec "git config --unset branch.#{branch}.remote"
+++ exec "git config --unset branch.#{branch}.merge"
+++ end
+++else
+++ die "No local branch #{branch} exists!" if local_ref.empty?
+++ die "A remote branch #{branch} on #{remote} already exists!" unless remote_ref.empty?
+++ die "Local branch #{branch} is already a tracking branch!" unless remote_config.empty?
+++
+++ exec "git push #{remote} #{branch}:refs/heads/#{branch}"
+++ exec "git config branch.#{branch}.remote #{remote}"
+++ exec "git config branch.#{branch}.merge refs/heads/#{branch}"
+++end
+++
+diff --cc git-rank-contributors
+index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3b272206d185e2f9b8089e6aaa00fde6acc308ef
+new file mode 100755
+--- /dev/null
++++ b/git-rank-contributors
+@@@ -1,0 -1,0 +1,60 @@@
+++#!/usr/bin/env ruby
+++
+++## git-rank-contributors: a simple script to trace through the logs and
+++## rank contributors by the total size of the diffs they're responsible for.
+++## A change counts twice as much as a plain addition or deletion.
+++##
+++## Output may or may not be suitable for inclusion in a CREDITS file.
+++## Probably not without some editing, because people often commit from more
+++## than one address.
+++##
+++## git-rank-contributors Copyright 2008 William Morgan <wmorgan-git-wt-add@masanjin.net>.
+++## This program is free software: you can redistribute it and/or modify
+++## it under the terms of the GNU General Public License as published by
+++## the Free Software Foundation, either version 3 of the License, or (at
+++## your option) any later version.
+++##
+++## This program is distributed in the hope that it will be useful,
+++## but WITHOUT ANY WARRANTY; without even the implied warranty of
+++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++## GNU General Public License for more details.
+++##
+++## You can find the GNU General Public License at:
+++## http://www.gnu.org/licenses/
+++
+++class String
+++ def obfuscate; gsub(/@/, " at the ").gsub(/\.(\w+)(>|$)/, ' dot \1s\2') end
+++ def htmlize; gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;") end
+++end
+++
+++lines = {}
+++verbose = ARGV.delete("-v")
+++obfuscate = ARGV.delete("-o")
+++htmlize = ARGV.delete("-h")
+++
+++author = nil
+++state = :pre_author
+++`git log -M -C -C -p --no-color`.each do |l|
+++ case
+++ when (state == :pre_author || state == :post_author) && l =~ /Author: (.*)$/
+++ author = $1
+++ state = :post_author
+++ lines[author] ||= 0
+++ when state == :post_author && l =~ /^\+\+\+/
+++ state = :in_diff
+++ when state == :in_diff && l =~ /^[\+\-]/
+++ lines[author] += 1
+++ when state == :in_diff && l =~ /^commit /
+++ state = :pre_author
+++ end
+++end
+++
+++lines.sort_by { |a, c| -c }.each do |a, c|
+++ a = a.obfuscate if obfuscate
+++ a = a.htmlize if htmlize
+++ if verbose
+++ puts "#{a}: #{c} lines of diff"
+++ else
+++ puts a
+++ end
+++end
+diff --cc git-show-merges
+index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..12907502a6c6885e7183a0e71d9cfeed77917824
+new file mode 100755
+--- /dev/null
++++ b/git-show-merges
+@@@ -1,0 -1,0 +1,49 @@@
+++#!/usr/bin/env ruby
+++
+++## git-show-merges: a simple script to show you which topic branches have
+++## been merged into the current branch, and which haven't. (Or, specify
+++## the set of merge branches you're interested in on the command line.)
+++##
+++## git-show-merges Copyright 2008 William Morgan <wmorgan-git-wt-add@masanjin.net>.
+++## This program is free software: you can redistribute it and/or modify
+++## it under the terms of the GNU General Public License as published by
+++## the Free Software Foundation, either version 3 of the License, or (at
+++## your option) any later version.
+++##
+++## This program is distributed in the hope that it will be useful,
+++## but WITHOUT ANY WARRANTY; without even the implied warranty of
+++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++## GNU General Public License for more details.
+++##
+++## You can find the GNU General Public License at:
+++## http://www.gnu.org/licenses/
+++heads = if ARGV.empty?
+++ [`git symbolic-ref HEAD`.chomp]
+++else
+++ ARGV
+++end.map { |r| r.gsub(/refs\/heads\//, "") }
+++
+++branches = `git show-ref --heads`.
+++ scan(/^\S+ refs\/heads\/(\S+)$/).
+++ map { |a| a.first }
+++
+++unknown = heads - branches
+++unless unknown.empty?
+++ $stderr.puts "Unknown branch: #{unknown.first}"
+++ exit(-1)
+++end
+++
+++branches -= heads
+++
+++heads.each do |h|
+++ merged = branches.select { |b| `git log #{h}..#{b}` == "" }
+++ unmerged = branches - merged
+++
+++ puts "merged into #{h}:"
+++ merged.each { |b| puts " #{b}" }
+++ puts
+++ puts "not merged into #{h}: "
+++ unmerged.each { |b| puts " #{b}" }
+++
+++ puts
+++end
+diff --cc git-wt-add
+index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3125ab5495e5f2ab55ed48af5303031fae02c2ac
+new file mode 100755
+--- /dev/null
++++ b/git-wt-add
+@@@ -1,0 -1,0 +1,196 @@@
+++#!/usr/bin/env ruby
+++
+++## git-wt-add: A darcs-style interactive staging script for git. As the
+++## name implies, git-wt-add walks you through unstaged changes on a
+++## hunk-by-hunk basis and allows you to pick the ones you'd like staged.
+++##
+++## git-wt-add Copyright 2007 William Morgan <wmorgan-git-wt-add@masanjin.net>.
+++## This program is free software: you can redistribute it and/or modify
+++## it under the terms of the GNU General Public License as published by
+++## the Free Software Foundation, either version 3 of the License, or
+++## (at your option) any later version.
+++##
+++## This program is distributed in the hope that it will be useful,
+++## but WITHOUT ANY WARRANTY; without even the implied warranty of
+++## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+++## GNU General Public License for more details.
+++##
+++## You can find the GNU General Public License at:
+++## http://www.gnu.org/licenses/
+++
+++COLOR = /\e\[\d*m/
+++
+++class Hunk
+++ attr_reader :file, :file_header, :diff
+++ attr_accessor :disposition
+++
+++ def initialize file, file_header, diff
+++ @file = file
+++ @file_header = file_header
+++ @diff = diff
+++ @disposition = :unknown
+++ end
+++
+++ def self.make_from diff
+++ ret = []
+++ state = :outside
+++ file_header = hunk = file = nil
+++
+++ diff.each do |l| # a little state machine to parse git diff output
+++ reprocess = false
+++ begin
+++ reprocess = false
+++ case
+++ when state == :outside && l =~ /^(#{COLOR})*diff --git a\/(.+) b\/(\2)/
+++ file = $2
+++ file_header = ""
+++ when state == :outside && l =~ /^(#{COLOR})*index /
+++ when state == :outside && l =~ /^(#{COLOR})*(---|\+\+\+) /
+++ file_header += l + "\n"
+++ when state == :outside && l =~ /^(#{COLOR})*@@ /
+++ state = :in_hunk
+++ hunk = l + "\n"
+++ when state == :in_hunk && l =~ /^(#{COLOR})*(@@ |diff --git a)/
+++ ret << Hunk.new(file, file_header, hunk)
+++ state = :outside
+++ reprocess = true
+++ when state == :in_hunk
+++ hunk += l + "\n"
+++ else
+++ raise "unparsable diff input: #{l.inspect}"
+++ end
+++ end while reprocess
+++ end
+++
+++ ## add the final hunk
+++ ret << Hunk.new(file, file_header, hunk) if hunk
+++
+++ ret
+++ end
+++end
+++
+++def help
+++ puts <<EOS
+++y: record this patch
+++n: don't record it
+++w: wait and decide later, defaulting to no
+++
+++s: don't record the rest of the changes to this file
+++f: record the rest of the changes to this file
+++
+++d: record selected patches, skipping all the remaining patches
+++a: record all the remaining patches
+++q: cancel record
+++
+++j: skip to next patch
+++k: back up to previous patch
+++c: calculate number of patches
+++h or ?: show this help
+++
+++<Space>: accept the current default (which is capitalized)
+++EOS
+++end
+++
+++def walk_through hunks
+++ skip_files, record_files = {}, {}
+++ skip_rest = record_rest = false
+++
+++ while hunks.any? { |h| h.disposition == :unknown }
+++ pos = 0
+++ until pos >= hunks.length
+++ h = hunks[pos]
+++ if h.disposition != :unknown
+++ pos += 1
+++ next
+++ elsif skip_rest || skip_files[h.file]
+++ h.disposition = :ignore
+++ pos += 1
+++ next
+++ elsif record_rest || record_files[h.file]
+++ h.disposition = :record
+++ pos += 1
+++ next
+++ end
+++
+++ puts "Hunk from #{h.file}"
+++ puts h.diff
+++ print "Shall I stage this change? (#{pos + 1}/#{hunks.length}) [ynWsfqadk], or ? for help: "
+++ c = $stdin.getc
+++ puts
+++ case c
+++ when ?y: h.disposition = :record
+++ when ?n: h.disposition = :ignore
+++ when ?w, ?\ : h.disposition = :unknown
+++ when ?s
+++ h.disposition = :ignore
+++ skip_files[h.file] = true
+++ when ?f
+++ h.disposition = :record
+++ record_files[h.file] = true
+++ when ?d: skip_rest = true
+++ when ?a: record_rest = true
+++ when ?q: exit
+++ when ?k
+++ if pos > 0
+++ hunks[pos - 1].disposition = :unknown
+++ pos -= 2 # double-bah
+++ end
+++ else
+++ help
+++ pos -= 1 # bah
+++ end
+++
+++ pos += 1
+++ puts
+++ end
+++ end
+++end
+++
+++def make_patch hunks
+++ patch = ""
+++ did_header = {}
+++ hunks.each do |h|
+++ next unless h.disposition == :record
+++ unless did_header[h.file]
+++ patch += h.file_header
+++ did_header[h.file] = true
+++ end
+++ patch += h.diff
+++ end
+++
+++ patch.gsub COLOR, ""
+++end
+++
+++### execution starts here ###
+++
+++diff = `git diff`.split(/\r?\n/)
+++if diff.empty?
+++ puts "No unstaged changes."
+++ exit
+++end
+++hunks = Hunk.make_from diff
+++
+++## unix-centric!
+++state = `stty -g`
+++begin
+++ `stty -icanon` # immediate keypress mode
+++ walk_through hunks
+++ensure
+++ `stty #{state}`
+++end
+++
+++patch = make_patch hunks
+++if patch.empty?
+++ puts "No changes selected for staging."
+++else
+++ IO.popen("git apply --cached", "w") { |f| f.puts patch }
+++ puts <<EOS
+++Staged patch of #{patch.split("\n").size} lines.
+++
+++Possible next commands:
+++ git diff --cached: see staged changes
+++ git commit: commit staged changes
+++ git reset: unstage changes
+++EOS
+++end
+++
+diff --cc git-wtf
+index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea4b27f6812cf9daf84b047bff4443cc554f92bf
+new file mode 100755
+--- /dev/null
++++ b/git-wtf
+@@@ -1,0 -1,0 +1,223 @@@
+++#!/usr/bin/env ruby
+++
+++## git-wtf: display the state of your repository in a readable and easy-to-scan
+++## format.
+++##
+++## git-wtf tries to ease the task of having many git branches. It's also useful
+++## for getting a summary of how tracking branches relate to a remote server.
+++##
+++## git-wtf shows you:
+++## - How your branch relates to the remote repo, if it's a tracking branch.
+++## - How your branch relates to non-feature ("version") branches, if it's a
+++## feature branch.
+++## - How your branch relates to the feature branches, if it's a version branch.
+++##
+++## For each of these relationships, git-wtf displays the commits pending on
+++## either side, if any. It displays checkboxes along the side for easy scanning
+++## of merged/non-merged branches.
+++##
+++## If you're working against a remote repo, git-wtf is best used between a 'git
+++## fetch' and a 'git merge' (or 'git pull' if you don't mind the redundant
+++## network access).
+++##
+++## Usage: git wtf [branch+] [-l|--long] [-a|--all] [--dump-config]
+++##
+++## If [branch] is not specified, git-wtf will use the current branch. With
+++## --long, you'll see author info and date for each commit. With --all, you'll
+++## see all commits, not just the first 5. With --dump-config, git-wtf will
+++## print out its current configuration in YAML format and exit.
+++##
+++## git-wtf uses some heuristics to determine which branches are version
+++## branches, and which are feature branches. (Specifically, it assumes the
+++## version branches are named "master", "next" and "edge".) If it guesses
+++## incorrectly, you will have to create a .git-wtfrc file.
+++##
+++## git-wtf looks for a .git-wtfrc file starting in the current directory, and
+++## recursively up to the root. The config file is a YAML file that specifies
+++## the version branches, any branches to ignore, and the max number of commits
+++## to display when --all isn't used. To start building a configuration file,
+++## run "git-wtf --dump-config > .git-wtfrc" and edit it.
+++##
+++## IMPORTANT NOTE: all local branches referenced in .git-wtfrc must be prefixed
+++## with heads/, e.g. "heads/master". Remote branches must be of the form
+++## remotes/<remote>/<branch>.
+++##
+++## git-wtf Copyright 2008 William Morgan <wmorgan-git-wt-add@masanjin.net>.
+++## This program is free software: you can redistribute it and/or modify it
+++## under the terms of the GNU General Public License as published by the Free
+++## Software Foundation, either version 3 of the License, or (at your option)
+++## any later version.
+++##
+++## This program is distributed in the hope that it will be useful, but WITHOUT
+++## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+++## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+++## more details.
+++##
+++## You can find the GNU General Public License at: http://www.gnu.org/licenses/
+++
+++
+++require 'yaml'
+++CONFIG_FN = ".git-wtfrc"
+++
+++class Numeric; def pluralize s; "#{to_s} #{s}" + (self != 1 ? "s" : "") end end
+++
+++$long = ARGV.delete("--long") || ARGV.delete("-l")
+++$all = ARGV.delete("--all") || ARGV.delete("-a")
+++$dump_config = ARGV.delete("--dump-config")
+++
+++## find config file
+++$config = { "versions" => %w(heads/master heads/next heads/edge), "ignore" => [], "max_commits" => 5 }.merge begin
+++ p = File.expand_path "."
+++ fn = while true
+++ fn = File.join p, CONFIG_FN
+++ break fn if File.exist? fn
+++ pp = File.expand_path File.join(p, "..")
+++ break if p == pp
+++ p = pp
+++ end
+++
+++ (fn && YAML::load_file(fn)) || {} # YAML turns empty files into false
+++end
+++
+++if $dump_config
+++ puts $config.to_yaml
+++ exit(0)
+++end
+++
+++## the set of commits in 'to' that aren't in 'from'.
+++## if empty, 'to' has been merged into 'from'.
+++def commits_between from, to
+++ if $long
+++ `git log --pretty=format:"- %s [%h] (%ae; %ar)" #{from}..#{to}`
+++ else
+++ `git log --pretty=format:"- %s [%h]" #{from}..#{to}`
+++ end.split(/[\r\n]+/)
+++end
+++
+++def show_commits commits, prefix=" "
+++ if commits.empty?
+++ puts "#{prefix} none"
+++ else
+++ max = $all ? commits.size : $config["max_commits"]
+++ max -= 1 if max == commits.size - 1 # never show "and 1 more"
+++ commits[0 ... max].each { |c| puts "#{prefix}#{c}" }
+++ puts "#{prefix}... and #{commits.size - max} more." if commits.size > max
+++ end
+++end
+++
+++def ahead_behind_string ahead, behind
+++ [ahead.empty? ? nil : "#{ahead.size.pluralize 'commit'} ahead",
+++ behind.empty? ? nil : "#{behind.size.pluralize 'commit'} behind"].
+++ compact.join("; ")
+++end
+++
+++def show b, all_branches
+++ puts "Local branch: #{b[:local_branch]}"
+++ both = false
+++
+++ if b[:remote_branch]
+++ pushc = commits_between b[:remote_branch], b[:local_branch]
+++ pullc = commits_between b[:local_branch], b[:remote_branch]
+++
+++ both = !pushc.empty? && !pullc.empty?
+++ if pushc.empty?
+++ puts "[x] in sync with remote"
+++ else
+++ action = both ? "push after rebase / merge" : "push"
+++ puts "[ ] NOT in sync with remote (needs #{action})"
+++ show_commits pushc
+++ end
+++
+++ puts "\nRemote branch: #{b[:remote_branch]} (#{b[:remote_url]})"
+++
+++ if pullc.empty?
+++ puts "[x] in sync with local"
+++ else
+++ action = pushc.empty? ? "merge" : "rebase / merge"
+++ puts "[ ] NOT in sync with local (needs #{action})"
+++ show_commits pullc
+++
+++ both = !pushc.empty? && !pullc.empty?
+++ end
+++ end
+++
+++ vbs, fbs = all_branches.partition { |name, br| $config["versions"].include? br[:local_branch] }
+++ if $config["versions"].include? b[:local_branch]
+++ puts "\nFeature branches:" unless fbs.empty?
+++ fbs.each do |name, br|
+++ remote_ahead = b[:remote_branch] ? commits_between(b[:remote_branch], br[:local_branch]) : []
+++ local_ahead = commits_between b[:local_branch], br[:local_branch]
+++ if local_ahead.empty? && remote_ahead.empty?
+++ puts "[x] #{br[:name]} is merged in"
+++ elsif local_ahead.empty? && b[:remote_branch]
+++ puts "(x) #{br[:name]} merged in (only locally)"
+++ else
+++ behind = commits_between br[:local_branch], b[:local_branch]
+++ puts "[ ] #{br[:name]} is NOT merged in (#{ahead_behind_string local_ahead, behind})"
+++ show_commits local_ahead
+++ end
+++ end
+++ else
+++ puts "\nVersion branches:" unless vbs.empty? # unlikely
+++ vbs.each do |v, br|
+++ ahead = commits_between v, b[:local_branch]
+++ if ahead.empty?
+++ puts "[x] merged into #{v}"
+++ else
+++ #behind = commits_between b[:local_branch], v
+++ puts "[ ] NOT merged into #{v} (#{ahead.size.pluralize 'commit'} ahead)"
+++ show_commits ahead
+++ end
+++ end
+++ end
+++
+++ puts "\nWARNING: local and remote branches have diverged. A merge will occur unless you rebase." if both
+++end
+++
+++branches = `git show-ref`.inject({}) do |hash, l|
+++ sha1, ref = l.chomp.split " refs/"
+++ next hash if $config["ignore"].member? ref
+++ next hash unless ref =~ /^heads\/(.+)/
+++ name = $1
+++ hash[name] = { :name => name, :local_branch => ref }
+++ hash
+++end
+++
+++remotes = `git config --get-regexp ^remote\.\*\.url`.inject({}) do |hash, l|
+++ l =~ /^remote\.(.+?)\.url (.+)$/ or next hash
+++ hash[$1] ||= $2
+++ hash
+++end
+++
+++`git config --get-regexp ^branch\.`.each do |l|
+++ case l
+++ when /branch\.(.*?)\.remote (.+)/
+++ branches[$1] ||= {}
+++ branches[$1][:remote] = $2
+++ branches[$1][:remote_url] = remotes[$2]
+++ when /branch\.(.*?)\.merge ((refs\/)?heads\/)?(.+)/
+++ branches[$1] ||= {}
+++ branches[$1][:remote_mergepoint] = $4
+++ end
+++end
+++
+++branches.each { |k, v| v[:remote_branch] = "#{v[:remote]}/#{v[:remote_mergepoint]}" if v[:remote] && v[:remote_mergepoint] }
+++
+++show_dirty = ARGV.empty?
+++targets = if ARGV.empty?
+++ [`git symbolic-ref HEAD`.chomp.sub(/^refs\/heads\//, "")]
+++else
+++ ARGV
+++end.map { |t| branches[t] or abort "Error: can't find branch #{t.inspect}." }
+++
+++targets.each { |t| show t, branches }
+++
+++modified = show_dirty && `git ls-files -m` != ""
+++uncommitted = show_dirty && `git diff-index --cached HEAD` != ""
+++
+++puts if modified || uncommitted
+++puts "NOTE: working directory contains modified files" if modified
+++puts "NOTE: staging area contains staged but uncommitted files" if uncommitted
+++
+++# the end!
+++

0 comments on commit 6bb41e4

Please sign in to comment.