From d7675d38d648e3f67a377214569f15e2c997b513 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Sat, 19 Jul 2008 20:06:49 -0400 Subject: [PATCH] Implement Blob, Tree and Lazy, add tests for Tree. Lots of changes this commit. More in-depth: * Added text fixtures from Git Python * Add Git->getDir * Add Git::$executeKwords (doesn't do anything yet, we need to merge upstream changes) * Implemented Git_Tree expansion in Git_Commit * Make Git_Repo properties public, as they are not lazy-loaded * Implement Git_Repo->tree() * Implement GitHarness, for easy access to fixtures * Setup Mocks Signed-off-by: Edward Z. Yang --- .gitattributes | 1 + library/Git.php | 23 +- library/Git/Blob.php | 63 ++ library/Git/Commit.php | 4 +- library/Git/Lazy.php | 51 ++ library/Git/Repo.php | 16 +- library/Git/Tree.php | 93 +++ tests/Git/TreeTest.php | 68 ++ tests/GitHarness.php | 8 + tests/fixtures/blame | 131 ++++ tests/fixtures/cat_file_blob | 1 + tests/fixtures/cat_file_blob_size | 1 + tests/fixtures/diff_2 | 54 ++ tests/fixtures/diff_2f | 19 + tests/fixtures/diff_f | 15 + tests/fixtures/diff_i | 201 ++++++ tests/fixtures/diff_mode_only | 1152 +++++++++++++++++++++++++++++++ tests/fixtures/diff_new_mode | 14 + tests/fixtures/diff_numstat | 2 + tests/fixtures/diff_p | 610 ++++++++++++++++ tests/fixtures/for_each_ref | Bin 0 -> 58 bytes tests/fixtures/for_each_ref_tags | Bin 0 -> 58 bytes tests/fixtures/ls_tree_a | 7 + tests/fixtures/ls_tree_b | 2 + tests/fixtures/ls_tree_commit | 3 + tests/fixtures/rev_list | 24 + tests/fixtures/rev_list_commit_diffs | 8 + tests/fixtures/rev_list_commit_idabbrev | 8 + tests/fixtures/rev_list_commit_stats | 7 + tests/fixtures/rev_list_count | 655 ++++++++++++++++++ tests/fixtures/rev_list_delta_a | 8 + tests/fixtures/rev_list_delta_b | 11 + tests/fixtures/rev_list_single | 7 + tests/fixtures/rev_parse | 1 + tests/fixtures/show_empty_commit | 6 + tests/index.php | 13 +- 36 files changed, 3274 insertions(+), 13 deletions(-) create mode 100644 .gitattributes create mode 100644 library/Git/Blob.php create mode 100644 library/Git/Lazy.php create mode 100644 library/Git/Tree.php create mode 100644 tests/Git/TreeTest.php create mode 100644 tests/GitHarness.php create mode 100644 tests/fixtures/blame create mode 100644 tests/fixtures/cat_file_blob create mode 100644 tests/fixtures/cat_file_blob_size create mode 100644 tests/fixtures/diff_2 create mode 100644 tests/fixtures/diff_2f create mode 100644 tests/fixtures/diff_f create mode 100644 tests/fixtures/diff_i create mode 100644 tests/fixtures/diff_mode_only create mode 100644 tests/fixtures/diff_new_mode create mode 100644 tests/fixtures/diff_numstat create mode 100644 tests/fixtures/diff_p create mode 100644 tests/fixtures/for_each_ref create mode 100644 tests/fixtures/for_each_ref_tags create mode 100644 tests/fixtures/ls_tree_a create mode 100644 tests/fixtures/ls_tree_b create mode 100644 tests/fixtures/ls_tree_commit create mode 100644 tests/fixtures/rev_list create mode 100644 tests/fixtures/rev_list_commit_diffs create mode 100644 tests/fixtures/rev_list_commit_idabbrev create mode 100644 tests/fixtures/rev_list_commit_stats create mode 100644 tests/fixtures/rev_list_count create mode 100644 tests/fixtures/rev_list_delta_a create mode 100644 tests/fixtures/rev_list_delta_b create mode 100644 tests/fixtures/rev_list_single create mode 100644 tests/fixtures/rev_parse create mode 100644 tests/fixtures/show_empty_commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5f2af4e --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/tests/fixtures/* -crlf \ No newline at end of file diff --git a/library/Git.php b/library/Git.php index 4e3cdcd..2f098f4 100644 --- a/library/Git.php +++ b/library/Git.php @@ -1,9 +1,12 @@ dir = $dir; } /** + * Returns current working directory. + */ + public function getDir() {return $this->dir;} + + /** * Executes a command on shell and returns output. */ public function execute($command) { diff --git a/library/Git/Blob.php b/library/Git/Blob.php new file mode 100644 index 0000000..9ba2bf4 --- /dev/null +++ b/library/Git/Blob.php @@ -0,0 +1,63 @@ +repo = $repo; + foreach ($kwargs as $k => $v) { + $this->$k = $v; + } + } + /** + * Size of this blob in bytes. + */ + public function size() { + if (is_null($this->_size)) { + $this->_size = (int) $this->repo->git->catFile($this->id, array('s' => true)); + } + return $this->_size; + } + /** + * Returns the binary contents of this blob. + */ + public function data() { + if (is_null($this->_data)) { + $this->_data = $this->repo->git->catFile($this->id, array('p' => true)); + } + return $this->_data; + } + // This function isn't implemented because we don't really have a + // good MIME implementation by default on PHP. Will implement after + // more investigation. + // public function mimeType() { + /** + * Basename of this blob. + */ + public function basename() { + return basename($this->__get('name')); + } + // public static function blame($repo, $commit, $file) { + public function __toString() { + return '(Git_Blob "' . $this->id . '")'; + } + public function __get($name) { + if (method_exists($this, $name)) return $this->$name(); + else return $this->$name; + } +} diff --git a/library/Git/Commit.php b/library/Git/Commit.php index 967feb6..3029b46 100644 --- a/library/Git/Commit.php +++ b/library/Git/Commit.php @@ -29,9 +29,7 @@ class Git_Commit { } continue; } elseif ($k == 'tree') { - //$this->tree = new Git_Tree($repo, array('id' => $v)); - // :TODO: Implement Git_Tree - $this->tree = $v; + $this->tree = new Git_Tree($repo, array('id' => $v)); continue; } $this->$k = $v; diff --git a/library/Git/Lazy.php b/library/Git/Lazy.php new file mode 100644 index 0000000..7cdde2f --- /dev/null +++ b/library/Git/Lazy.php @@ -0,0 +1,51 @@ +_baked = true; + } + /** + * Implements public lazy loading. + */ + public function __get($name) { + if ($name[0] == '_') throw new Git_Exception('Cannot access private property ' . $name); + // Quick and easy way of emulating Python's @property decorator + if (method_exists($this, $name)) { + return $this->$name(); + } + if (isset($this->$name)) return $this->$name; + if (!$this->_baked) { + $this->bake(); + $this->_baked = true; + } + return $this->$name; + } + /** + * Implements the loading operation; after this method is + * called the class should be ready to go. + */ + abstract protected function bake(); +} diff --git a/library/Git/Repo.php b/library/Git/Repo.php index 8c380dc..69181ae 100644 --- a/library/Git/Repo.php +++ b/library/Git/Repo.php @@ -11,17 +11,17 @@ class Git_Repo { /** * Path to this repository. */ - protected $path; + public $path; /** * Whether or not this is a bare repository. */ - protected $bare; + public $bare; /** * Interface to Git itself. */ - protected $git; + public $git; /** * @param $path Path to the repository (either working copy or bare) @@ -52,7 +52,15 @@ class Git_Repo { // public function commitCount($start = 'master') { // * public function commit($id) { // public function commitDeltasFrom($other_repo, $ref = 'master', $other_ref = 'master') { - // * public function tree($treeish = 'master', $paths = array()) { + /** + * Returns the Git_Tree object for a given treeish reference. + * @param $treeish Reference to retrieve, can be branch name, or filename + * @param $paths Optional array of directory paths to restrict to. + * @return Git_Tree for that path. + */ + public function tree($treeish = 'master', $paths = array()) { + return Git_Tree::construct($this, $treeish, $paths); + } // * public function blob($id) { /** diff --git a/library/Git/Tree.php b/library/Git/Tree.php new file mode 100644 index 0000000..dbe2a38 --- /dev/null +++ b/library/Git/Tree.php @@ -0,0 +1,93 @@ +repo = $repo; + foreach ($kwargs as $k => $v) { + $this->$k = $v; + } + } + /** + * Lazy loads the tree's contents. + */ + protected function bake() { + $tmp = Git_Tree::construct($this->repo, $this->id); + $this->contents = $tmp->contents; + } + /** + * Constructs and fully initializes a tree. + */ + public static function construct($repo, $treeish, $paths = array()) { + $output = $repo->git->lsTree($treeish, $paths); + $tree = new Git_Tree($repo, array('id' => $treeish)); + $tree->constructInitialize($repo, $treeish, $output); + return $tree; + } + /** + * Initializes a tree object based on the output of the ls-tree command. + */ + public function constructInitialize($repo, $id, $text) { + $this->repo = $repo; + $this->id = $id; + $this->contents = array(); + foreach (explode("\n", $text) as $line) { + $out = self::contentFromString($repo, $line); + if (!$out) continue; + $this->contents[] = $out; + } + $this->markBaked(); + } + /** + * Creates a content object (blob or tree) based on the ls-tree command. + */ + public function contentFromString($repo, $text) { + $text = str_replace("\t", ' ', $text); + $bits = explode(' ', $text); + if (count($bits) != 4) return; + list($mode, $type, $id, $name) = $bits; + switch ($type) { + case 'tree': + return new Git_Tree($repo, compact('id', 'mode', 'name')); + case 'blob': + return new Git_Blob($repo, compact('id', 'mode', 'name')); + case 'commit': + return; + default: + throw new Git_Exception('Invalid type: ' . $type); + } + } + /** + * Retrieves a named object in this tree's contents. + * @param Filename of object to return. This function does NOT search + * recursively. + * @return Null if file not found, otherwise Git_Tree or Git_Commit file. + */ + public function get($file) { + foreach ($this->contents as $c) { + if ($c->name == $file) return $c; + } + } + /** + * Retrieves the basename of this tree. + */ + public function basename() { + return basename($this->__get('name')); + } + public function __toString() { + return '(Git_Tree "'.$this->id.'")'; + } +} diff --git a/tests/Git/TreeTest.php b/tests/Git/TreeTest.php new file mode 100644 index 0000000..2b17459 --- /dev/null +++ b/tests/Git/TreeTest.php @@ -0,0 +1,68 @@ +repo = new Git_Repo(GIT_REPO); + $this->git = $this->repo->git = new GitMock(); + $this->tree = new Git_Tree($this->repo); + } + function testContentsShouldCache() { + $this->git->setReturnValue('__call', $this->fixture('ls_tree_a') . $this->fixture('ls_tree_b')); + $this->git->expectCallCount('__call', 2); + $this->git->expectAt(0, '__call', array('lsTree', array('master', array()))); + $this->git->expectAt(1, '__call', array('lsTree', array('34868e6e7384cb5ee51c543a8187fdff2675b5a7', array()))); + $tree = $this->repo->tree('master'); + list($child) = array_slice($tree->contents, -1); + $child->contents; + $child->contents; + } + function testContentFromStringTreeShouldReturnTree() { + list($text) = array_slice(explode("\n", trim($this->fixture('ls_tree_a'))), -1); + $tree = $this->tree->contentFromString(null, $text); + $this->assertIsA($tree, 'Git_Tree'); + $this->assertIdentical('650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44', $tree->id); + $this->assertIdentical('040000', $tree->mode); + $this->assertIdentical('test', $tree->name); + } + function testContentFromStringTreeShouldReturnBlob() { + list($text) = explode("\n", trim($this->fixture('ls_tree_b'))); + $blob = $this->tree->contentFromString(null, $text); + $this->assertIsA($blob, 'Git_Blob'); + $this->assertIdentical('aa94e396335d2957ca92606f909e53e7beaf3fbb', $blob->id); + $this->assertIdentical('100644', $blob->mode); + $this->assertIdentical('grit.rb', $blob->name); + } + function testContentFromStringTreeShouldReturnCommit() { + list($x, $text) = explode("\n", trim($this->fixture('ls_tree_commit'))); + $tree = $this->tree->contentFromString(null, $text); + $this->assertIdentical($tree, null); + } + function testContentFromStringInvalidTypeShouldThrowException() { + $this->expectException(new Git_Exception('Invalid type: bogus')); + $this->tree->contentFromString(null, "040000 bogus 650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44\ttest"); + } + function testGet() { + // Python implementation patched blob here, but it doesn't seem + // to ever be called. Weird. They then go on to test with zero-length + // files, which shouldn't have any bearing either. We have condensed + // those tests. + $this->git->setReturnValue('__call', $this->fixture('ls_tree_a')); + $this->git->expectOnce('__call', array('lsTree', array('master', array()))); + $tree = $this->repo->tree('master'); + $this->assertIdentical('aa06ba24b4e3f463b3c4a85469d0fb9e5b421cf8', $tree->get('lib')->id); + $this->assertIdentical('8b1e02c0fb554eed2ce2ef737a68bb369d7527df', $tree->get('README.txt')->id); + } + function testGetWithCommits() { + $this->git->setReturnValue('__call', $this->fixture('ls_tree_commit')); + $this->git->expectOnce('__call', array('lsTree', array('master', array()))); + $tree = $this->repo->tree('master'); + $this->assertIdentical($tree->get('bar'), null); + $this->assertIdentical('2afb47bcedf21663580d5e6d2f406f08f3f65f19', $tree->get('foo')->id); + $this->assertIdentical('f623ee576a09ca491c4a27e48c0dfe04be5f4a2e', $tree->get('baz')->id); + } + function testToString() { + $tree = new Git_Tree($this->repo, array('id' => 'abc')); + $this->assertIdentical('(Git_Tree "abc")', (string) $tree); + } +} diff --git a/tests/GitHarness.php b/tests/GitHarness.php new file mode 100644 index 0000000..28ceef0 --- /dev/null +++ b/tests/GitHarness.php @@ -0,0 +1,8 @@ + +author-time 1191997100 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1191997100 +committer-tz -0700 +filename lib/grit.rb +summary initial grit setup +boundary + $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed +634396b2f541a9f2d58b00be1a07f0c358b999b3 2 2 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 3 3 + # core +634396b2f541a9f2d58b00be1a07f0c358b999b3 4 4 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 5 5 + # stdlib +634396b2f541a9f2d58b00be1a07f0c358b999b3 6 6 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 7 7 + # internal requires +3b1930208a82457747d76729ae088e90edca4673 8 8 1 +author Tom Preston-Werner +author-mail +author-time 1192267241 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192267241 +committer-tz -0700 +filename lib/grit.rb +summary big refactor to do lazy loading + require 'grit/lazy' +4c8124ffcf4039d292442eeccabdeca5af5c5017 8 9 1 +author Tom Preston-Werner +author-mail +author-time 1191999972 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1191999972 +committer-tz -0700 +filename lib/grit.rb +summary implement Grit#heads + require 'grit/errors' +d01a4cfad6ea50285c4710243e3cbe019d381eba 9 10 1 +author Tom Preston-Werner +author-mail +author-time 1192032303 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192032303 +committer-tz -0700 +filename lib/grit.rb +summary convert to Grit module, refactor to be more OO + require 'grit/git' +4c8124ffcf4039d292442eeccabdeca5af5c5017 9 11 1 + require 'grit/head' +a47fd41f3aa4610ea527dcc1669dfdb9c15c5425 10 12 1 +author Tom Preston-Werner +author-mail +author-time 1192002639 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192002639 +committer-tz -0700 +filename lib/grit.rb +summary add more comments throughout + require 'grit/commit' +b17b974691f0a26f26908495d24d9c4c718920f8 13 13 1 +author Tom Preston-Werner +author-mail +author-time 1192271832 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192271832 +committer-tz -0700 +filename lib/grit.rb +summary started implementing Tree + require 'grit/tree' +74fd66519e983a0f29e16a342a6059dbffe36020 14 14 1 +author Tom Preston-Werner +author-mail +author-time 1192317005 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192317005 +committer-tz -0700 +filename lib/grit.rb +summary add Blob + require 'grit/blob' +d01a4cfad6ea50285c4710243e3cbe019d381eba 12 15 1 + require 'grit/repo' +634396b2f541a9f2d58b00be1a07f0c358b999b3 9 16 1 + +d01a4cfad6ea50285c4710243e3cbe019d381eba 14 17 1 + module Grit +b6e1b765e0c15586a2c5b9832854f95defd71e1f 18 18 6 +author Tom Preston-Werner +author-mail +author-time 1192860483 +author-tz -0700 +committer Tom Preston-Werner +committer-mail +committer-time 1192860483 +committer-tz -0700 +filename lib/grit.rb +summary implement Repo.init_bare + class << self +b6e1b765e0c15586a2c5b9832854f95defd71e1f 19 19 + attr_accessor :debug +b6e1b765e0c15586a2c5b9832854f95defd71e1f 20 20 + end +b6e1b765e0c15586a2c5b9832854f95defd71e1f 21 21 + +b6e1b765e0c15586a2c5b9832854f95defd71e1f 22 22 + self.debug = false +b6e1b765e0c15586a2c5b9832854f95defd71e1f 23 23 + +634396b2f541a9f2d58b00be1a07f0c358b999b3 11 24 2 + VERSION = '1.0.0' +634396b2f541a9f2d58b00be1a07f0c358b999b3 12 25 + end \ No newline at end of file diff --git a/tests/fixtures/cat_file_blob b/tests/fixtures/cat_file_blob new file mode 100644 index 0000000..70c379b --- /dev/null +++ b/tests/fixtures/cat_file_blob @@ -0,0 +1 @@ +Hello world \ No newline at end of file diff --git a/tests/fixtures/cat_file_blob_size b/tests/fixtures/cat_file_blob_size new file mode 100644 index 0000000..b4de394 --- /dev/null +++ b/tests/fixtures/cat_file_blob_size @@ -0,0 +1 @@ +11 diff --git a/tests/fixtures/diff_2 b/tests/fixtures/diff_2 new file mode 100644 index 0000000..1f060c7 --- /dev/null +++ b/tests/fixtures/diff_2 @@ -0,0 +1,54 @@ +diff --git a/lib/grit/commit.rb b/lib/grit/commit.rb +index a093bb1db8e884cccf396b297259181d1caebed4..80fd3d527f269ecbd570b65b8e21fd85baedb6e9 100644 +--- a/lib/grit/commit.rb ++++ b/lib/grit/commit.rb +@@ -156,12 +156,8 @@ module Grit + + def diffs + if parents.empty? +- diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id) +- if diff =~ /diff --git a/ +- diff = diff.sub(/.+?(diff --git a)/m, '\1') +- else +- diff = '' +- end ++ diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id) ++ diff = diff.sub(/.+?(diff --git a)/m, '\1') + Diff.list_from_string(@repo, diff) + else + self.class.diff(@repo, parents.first.id, @id) +diff --git a/test/fixtures/show_empty_commit b/test/fixtures/show_empty_commit +deleted file mode 100644 +index ea25e32a409fdf74c1b9268820108d1c16dcc553..0000000000000000000000000000000000000000 +--- a/test/fixtures/show_empty_commit ++++ /dev/null +@@ -1,6 +0,0 @@ +-commit 1e3824339762bd48316fe87bfafc853732d43264 +-tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 +-author Tom Preston-Werner 1157392833 +0000 +-committer Tom Preston-Werner 1157392833 +0000 +- +- initial directory structure +diff --git a/test/test_commit.rb b/test/test_commit.rb +index fdeb9000089b052f0b31a845e0173e9b089e06a0..bdbc450e08084d7d611e985cfa12fb424cab29b2 100644 +--- a/test/test_commit.rb ++++ b/test/test_commit.rb +@@ -98,18 +98,6 @@ class TestCommit < Test::Unit::TestCase + assert_equal true, diffs[5].new_file + end + +- def test_diffs_on_initial_import_with_empty_commit +- Git.any_instance.expects(:show).with( +- {:full_index => true, :pretty => 'raw'}, +- '634396b2f541a9f2d58b00be1a07f0c358b999b3' +- ).returns(fixture('show_empty_commit')) +- +- @c = Commit.create(@r, :id => '634396b2f541a9f2d58b00be1a07f0c358b999b3') +- diffs = @c.diffs +- +- assert_equal [], diffs +- end +- + # to_s + + def test_to_s diff --git a/tests/fixtures/diff_2f b/tests/fixtures/diff_2f new file mode 100644 index 0000000..5246cd6 --- /dev/null +++ b/tests/fixtures/diff_2f @@ -0,0 +1,19 @@ +diff --git a/lib/grit/commit.rb b/lib/grit/commit.rb +index a093bb1db8e884cccf396b297259181d1caebed4..80fd3d527f269ecbd570b65b8e21fd85baedb6e9 100644 +--- a/lib/grit/commit.rb ++++ b/lib/grit/commit.rb +@@ -156,12 +156,8 @@ module Grit + + def diffs + if parents.empty? +- diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id) +- if diff =~ /diff --git a/ +- diff = diff.sub(/.+?(diff --git a)/m, '\1') +- else +- diff = '' +- end ++ diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id) ++ diff = diff.sub(/.+?(diff --git a)/m, '\1') + Diff.list_from_string(@repo, diff) + else + self.class.diff(@repo, parents.first.id, @id) diff --git a/tests/fixtures/diff_f b/tests/fixtures/diff_f new file mode 100644 index 0000000..48a4925 --- /dev/null +++ b/tests/fixtures/diff_f @@ -0,0 +1,15 @@ +diff --git a/lib/grit/diff.rb b/lib/grit/diff.rb +index 537955bb86a8ceaa19aea89e75ccbea5ce6f2698..00b0b4a67eca9242db5f8991e99625acd55f040c 100644 +--- a/lib/grit/diff.rb ++++ b/lib/grit/diff.rb +@@ -27,6 +27,10 @@ module Grit + while !lines.empty? + m, a_path, b_path = *lines.shift.match(%r{^diff --git a/(\S+) b/(\S+)$}) + ++ if lines.first =~ /^old mode/ ++ 2.times { lines.shift } ++ end ++ + new_file = false + deleted_file = false + diff --git a/tests/fixtures/diff_i b/tests/fixtures/diff_i new file mode 100644 index 0000000..cec64e1 --- /dev/null +++ b/tests/fixtures/diff_i @@ -0,0 +1,201 @@ +commit 634396b2f541a9f2d58b00be1a07f0c358b999b3 +Author: Tom Preston-Werner +Date: Tue Oct 9 23:18:20 2007 -0700 + + initial grit setup + +diff --git a/History.txt b/History.txt +new file mode 100644 +index 0000000000000000000000000000000000000000..81d2c27608b352814cbe979a6acd678d30219678 +--- /dev/null ++++ b/History.txt +@@ -0,0 +1,5 @@ ++== 1.0.0 / 2007-10-09 ++ ++* 1 major enhancement ++ * Birthday! ++ +diff --git a/Manifest.txt b/Manifest.txt +new file mode 100644 +index 0000000000000000000000000000000000000000..641972d82c6d1b51122274ae8f6a0ecdfb56ee22 +--- /dev/null ++++ b/Manifest.txt +@@ -0,0 +1,7 @@ ++History.txt ++Manifest.txt ++README.txt ++Rakefile ++bin/grit ++lib/grit.rb ++test/test_grit.rb +\ No newline at end of file +diff --git a/README.txt b/README.txt +new file mode 100644 +index 0000000000000000000000000000000000000000..8b1e02c0fb554eed2ce2ef737a68bb369d7527df +--- /dev/null ++++ b/README.txt +@@ -0,0 +1,48 @@ ++grit ++ by FIX (your name) ++ FIX (url) ++ ++== DESCRIPTION: ++ ++FIX (describe your package) ++ ++== FEATURES/PROBLEMS: ++ ++* FIX (list of features or problems) ++ ++== SYNOPSIS: ++ ++ FIX (code sample of usage) ++ ++== REQUIREMENTS: ++ ++* FIX (list of requirements) ++ ++== INSTALL: ++ ++* FIX (sudo gem install, anything else) ++ ++== LICENSE: ++ ++(The MIT License) ++ ++Copyright (c) 2007 FIX ++ ++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. +diff --git a/Rakefile b/Rakefile +new file mode 100644 +index 0000000000000000000000000000000000000000..ff69c3684a18592c741332b290492aa39d980e02 +--- /dev/null ++++ b/Rakefile +@@ -0,0 +1,17 @@ ++# -*- ruby -*- ++ ++require 'rubygems' ++require 'hoe' ++require './lib/grit.rb' ++ ++Hoe.new('grit', GitPython.VERSION) do |p| ++ p.rubyforge_name = 'grit' ++ # p.author = 'FIX' ++ # p.email = 'FIX' ++ # p.summary = 'FIX' ++ # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n") ++ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1] ++ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n") ++end ++ ++# vim: syntax=Ruby +diff --git a/bin/grit b/bin/grit +new file mode 100644 +index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 +diff --git a/lib/grit.rb b/lib/grit.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..32cec87d1e78946a827ddf6a8776be4d81dcf1d1 +--- /dev/null ++++ b/lib/grit.rb +@@ -0,0 +1,12 @@ ++$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed ++ ++# core ++ ++# stdlib ++ ++# internal requires ++require 'grit/grit' ++ ++class Grit ++ VERSION = '1.0.0' ++end +\ No newline at end of file +diff --git a/lib/grit/errors.rb b/lib/grit/errors.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..b3be31553741937607a89be8b6a2ab1df208852e +--- /dev/null ++++ b/lib/grit/errors.rb +@@ -0,0 +1,4 @@ ++class Grit ++ class InvalidGitRepositoryError < StandardError ++ end ++end +\ No newline at end of file +diff --git a/lib/grit/grit.rb b/lib/grit/grit.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..48fd36e16081ec09903f7a0e2253b3d16f9efb01 +--- /dev/null ++++ b/lib/grit/grit.rb +@@ -0,0 +1,24 @@ ++class Grit ++ attr_accessor :path ++ ++ # Create a new Grit instance ++ # +path+ is the path to either the root git directory or the bare git repo ++ # ++ # Examples ++ # g = Grit.new("/Users/tom/dev/grit") ++ # g = Grit.new("/Users/tom/public/grit.git") ++ def initialize(path) ++ if File.exist?(File.join(path, '.git')) ++ self.path = File.join(path, '.git') ++ elsif File.exist?(path) && path =~ /\.git$/ ++ self.path = path ++ else ++ raise InvalidGitRepositoryError.new(path) unless File.exist?(path) ++ end ++ end ++ ++ # Return the project's description. Taken verbatim from REPO/description ++ def description ++ File.open(File.join(self.path, 'description')).read.chomp ++ end ++end +\ No newline at end of file +diff --git a/test/helper.rb b/test/helper.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..56e21da6b4ce3021d2754775dfa589947a4e37e5 +--- /dev/null ++++ b/test/helper.rb +@@ -0,0 +1,5 @@ ++require File.join(File.dirname(__FILE__), *%w[.. lib grit]) ++ ++require 'test/unit' ++ ++GRIT_REPO = File.join(File.dirname(__FILE__), *%w[..]) +diff --git a/test/test_grit.rb b/test/test_grit.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..93aa481b37629797df739380306ae689e13f2855 +--- /dev/null ++++ b/test/test_grit.rb +@@ -0,0 +1,11 @@ ++require File.dirname(__FILE__) + '/helper' ++ ++class TestGrit < Test::Unit::TestCase ++ def setup ++ @g = Grit.new(GRIT_REPO) ++ end ++ ++ def test_description ++ assert_equal "Grit is a ruby library for interfacing with git repositories.", @g.description ++ end ++end +\ No newline at end of file diff --git a/tests/fixtures/diff_mode_only b/tests/fixtures/diff_mode_only new file mode 100644 index 0000000..6fc18f6 --- /dev/null +++ b/tests/fixtures/diff_mode_only @@ -0,0 +1,1152 @@ +diff --git a/bin/merb b/bin/merb +old mode 100644 +new mode 100755 +diff --git a/lib/merb.rb b/lib/merb.rb +index 76cb3e269e46fdf9b63cda7cb563c6cf40fdcb15..a2ab4ed47f9cb2ab942da5c46a2b561758a0d704 100644 +--- a/lib/merb.rb ++++ b/lib/merb.rb +@@ -15,7 +15,7 @@ require 'merb_core/core_ext' + require 'merb_core/gem_ext/erubis' + require 'merb_core/logger' + require 'merb_core/version' +- ++require 'merb_core/controller/mime' + + module Merb + class << self +@@ -23,6 +23,7 @@ module Merb + def start(argv=ARGV) + Merb::Config.parse_args(argv) + BootLoader.run ++ + case Merb::Config[:adapter] + when "mongrel" + adapter = Merb::Rack::Mongrel +diff --git a/lib/merb_core/boot/bootloader.rb b/lib/merb_core/boot/bootloader.rb +index d873924860bf4da06ac93db5c6a188f63dd1c3cc..57da75f05e28e8a256922bf345ccd3902e0a0b02 100644 +--- a/lib/merb_core/boot/bootloader.rb ++++ b/lib/merb_core/boot/bootloader.rb +@@ -20,7 +20,7 @@ module Merb + end + + def run +- subclasses.each {|klass| Object.full_const_get(klass).new.run } ++ subclasses.each {|klass| Object.full_const_get(klass).run } + end + + def after(klass) +@@ -37,95 +37,128 @@ module Merb + + end + +-class Merb::BootLoader::BuildFramework < Merb::BootLoader +- def run +- build_framework ++class Merb::BootLoader::LoadInit < Merb::BootLoader ++ def self.run ++ if Merb::Config[:init_file] ++ require Merb.root / Merb::Config[:init_file] ++ elsif File.exists?(Merb.root / "config" / "merb_init.rb") ++ require Merb.root / "config" / "merb_init" ++ elsif File.exists?(Merb.root / "merb_init.rb") ++ require Merb.root / "merb_init" ++ elsif File.exists?(Merb.root / "application.rb") ++ require Merb.root / "application" ++ end ++ end ++end ++ ++class Merb::BootLoader::Environment < Merb::BootLoader ++ def self.run ++ Merb.environment = Merb::Config[:environment] ++ end ++end ++ ++class Merb::BootLoader::Logger < Merb::BootLoader ++ def self.run ++ Merb.logger = Merb::Logger.new(Merb.dir_for(:log) / "test_log") ++ Merb.logger.level = Merb::Logger.const_get(Merb::Config[:log_level].upcase) rescue Merb::Logger::INFO + end ++end ++ ++class Merb::BootLoader::BuildFramework < Merb::BootLoader ++ class << self ++ def run ++ build_framework ++ end + +- # This method should be overridden in merb_init.rb before Merb.start to set up a different +- # framework structure +- def build_framework +- %[view model controller helper mailer part].each do |component| +- Merb.push_path(component.to_sym, Merb.root_path("app/#{component}s")) ++ # This method should be overridden in merb_init.rb before Merb.start to set up a different ++ # framework structure ++ def build_framework ++ %w[view model controller helper mailer part].each do |component| ++ Merb.push_path(component.to_sym, Merb.root_path("app/#{component}s")) ++ end ++ Merb.push_path(:application, Merb.root_path("app/controllers/application.rb")) ++ Merb.push_path(:config, Merb.root_path("config/router.rb")) ++ Merb.push_path(:lib, Merb.root_path("lib")) + end +- Merb.push_path(:application, Merb.root_path("app/controllers/application.rb")) +- Merb.push_path(:config, Merb.root_path("config/router.rb")) +- Merb.push_path(:lib, Merb.root_path("lib")) + end + end + + class Merb::BootLoader::LoadPaths < Merb::BootLoader + LOADED_CLASSES = {} + +- def run +- # Add models, controllers, and lib to the load path +- $LOAD_PATH.unshift Merb.load_paths[:model].first if Merb.load_paths[:model] +- $LOAD_PATH.unshift Merb.load_paths[:controller].first if Merb.load_paths[:controller] +- $LOAD_PATH.unshift Merb.load_paths[:lib].first if Merb.load_paths[:lib] ++ class << self ++ def run ++ # Add models, controllers, and lib to the load path ++ $LOAD_PATH.unshift Merb.load_paths[:model].first if Merb.load_paths[:model] ++ $LOAD_PATH.unshift Merb.load_paths[:controller].first if Merb.load_paths[:controller] ++ $LOAD_PATH.unshift Merb.load_paths[:lib].first if Merb.load_paths[:lib] + +- # Require all the files in the registered load paths +- puts Merb.load_paths.inspect +- Merb.load_paths.each do |name, path| +- Dir[path.first / path.last].each do |file| +- klasses = ObjectSpace.classes.dup +- require f +- LOADED_CLASSES[file] = ObjectSpace.classes - klasses ++ # Require all the files in the registered load paths ++ puts Merb.load_paths.inspect ++ Merb.load_paths.each do |name, path| ++ Dir[path.first / path.last].each do |file| ++ klasses = ObjectSpace.classes.dup ++ require file ++ LOADED_CLASSES[file] = ObjectSpace.classes - klasses ++ end + end + end +- end + +- def reload(file) +- if klasses = LOADED_CLASSES[file] +- klasses.each do |klass| +- remove_constant(klass) ++ def reload(file) ++ if klasses = LOADED_CLASSES[file] ++ klasses.each do |klass| ++ remove_constant(klass) ++ end + end ++ load file + end +- load file +- end + +- def remove_constant(const) +- # This is to support superclasses (like AbstractController) that track +- # their subclasses in a class variable. Classes that wish to use this +- # functionality are required to alias it to _subclasses_list. Plugins +- # for ORMs and other libraries should keep this in mind. +- if klass.superclass.respond_to?(:_subclasses_list) +- klass.superclass.send(:_subclasses_list).delete(klass) +- klass.superclass.send(:_subclasses_list).delete(klass.to_s) +- end ++ def remove_constant(const) ++ # This is to support superclasses (like AbstractController) that track ++ # their subclasses in a class variable. Classes that wish to use this ++ # functionality are required to alias it to _subclasses_list. Plugins ++ # for ORMs and other libraries should keep this in mind. ++ if klass.superclass.respond_to?(:_subclasses_list) ++ klass.superclass.send(:_subclasses_list).delete(klass) ++ klass.superclass.send(:_subclasses_list).delete(klass.to_s) ++ end + +- parts = const.to_s.split("::") +- base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::")) +- object = parts[-1].intern +- Merb.logger.debugger("Removing constant #{object} from #{base}") +- base.send(:remove_const, object) if object ++ parts = const.to_s.split("::") ++ base = parts.size == 1 ? Object : Object.full_const_get(parts[0..-2].join("::")) ++ object = parts[-1].intern ++ Merb.logger.debugger("Removing constant #{object} from #{base}") ++ base.send(:remove_const, object) if object ++ end + end + + end + + class Merb::BootLoader::Templates < Merb::BootLoader +- def run +- template_paths.each do |path| +- Merb::Template.inline_template(path) ++ class << self ++ def run ++ template_paths.each do |path| ++ Merb::Template.inline_template(path) ++ end + end +- end + +- def template_paths +- extension_glob = "{#{Merb::Template::EXTENSIONS.keys.join(',')}}" ++ def template_paths ++ extension_glob = "{#{Merb::Template::EXTENSIONS.keys.join(',')}}" + +- # This gets all templates set in the controllers template roots +- # We separate the two maps because most of controllers will have +- # the same _template_root, so it's silly to be globbing the same +- # path over and over. +- template_paths = Merb::AbstractController._abstract_subclasses.map do |klass| +- Object.full_const_get(klass)._template_root +- end.uniq.map {|path| Dir["#{path}/**/*.#{extension_glob}"] } ++ # This gets all templates set in the controllers template roots ++ # We separate the two maps because most of controllers will have ++ # the same _template_root, so it's silly to be globbing the same ++ # path over and over. ++ template_paths = Merb::AbstractController._abstract_subclasses.map do |klass| ++ Object.full_const_get(klass)._template_root ++ end.uniq.compact.map {|path| Dir["#{path}/**/*.#{extension_glob}"] } + +- # This gets the templates that might be created outside controllers +- # template roots. eg app/views/shared/* +- template_paths << Dir["#{Merb.dir_for(:view)}/**/*.#{extension_glob}"] if Merb.dir_for(:view) ++ # This gets the templates that might be created outside controllers ++ # template roots. eg app/views/shared/* ++ template_paths << Dir["#{Merb.dir_for(:view)}/**/*.#{extension_glob}"] if Merb.dir_for(:view) + +- template_paths.flatten.compact.uniq +- end ++ template_paths.flatten.compact.uniq ++ end ++ end + end + + class Merb::BootLoader::Libraries < Merb::BootLoader +@@ -145,18 +178,41 @@ class Merb::BootLoader::Libraries < Merb::BootLoader + def self.add_libraries(hsh) + @@libraries.merge!(hsh) + end +- +- def run ++ ++ def self.run + @@libraries.each do |exclude, choices| + require_first_working(*choices) unless Merb::Config[exclude] + end + end +- +- def require_first_working(first, *rest) ++ ++ def self.require_first_working(first, *rest) + p first, rest + require first + rescue LoadError + raise LoadError if rest.empty? + require_first_working rest.unshift, *rest + end ++end ++ ++class Merb::BootLoader::MimeTypes < Merb::BootLoader ++ def self.run ++ # Sets the default mime-types ++ # ++ # By default, the mime-types include: ++ # :all:: no transform, */* ++ # :yaml:: to_yaml, application/x-yaml or text/yaml ++ # :text:: to_text, text/plain ++ # :html:: to_html, text/html or application/xhtml+xml or application/html ++ # :xml:: to_xml, application/xml or text/xml or application/x-xml, adds "Encoding: UTF-8" response header ++ # :js:: to_json, text/javascript ot application/javascript or application/x-javascript ++ # :json:: to_json, application/json or text/x-json ++ Merb.available_mime_types.clear ++ Merb.add_mime_type(:all, nil, %w[*/*]) ++ Merb.add_mime_type(:yaml, :to_yaml, %w[application/x-yaml text/yaml]) ++ Merb.add_mime_type(:text, :to_text, %w[text/plain]) ++ Merb.add_mime_type(:html, :to_html, %w[text/html application/xhtml+xml application/html]) ++ Merb.add_mime_type(:xml, :to_xml, %w[application/xml text/xml application/x-xml], :Encoding => "UTF-8") ++ Merb.add_mime_type(:js, :to_json, %w[text/javascript application/javascript application/x-javascript]) ++ Merb.add_mime_type(:json, :to_json, %w[application/json text/x-json]) ++ end + end +\ No newline at end of file +diff --git a/lib/merb_core/config.rb b/lib/merb_core/config.rb +index c92f2e6f071c234551ecb16a4716d47fa92f6c7b..ab0864e0174b54833c758f9f22a840d3b53c7653 100644 +--- a/lib/merb_core/config.rb ++++ b/lib/merb_core/config.rb +@@ -92,6 +92,10 @@ module Merb + options[:cluster] = nodes + end + ++ opts.on("-I", "--init-file FILE", "Name of the file to load first") do |init_file| ++ options[:init_file] = init_file ++ end ++ + opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000.") do |port| + options[:port] = port + end +@@ -261,29 +265,29 @@ module Merb + + @configuration = Merb::Config.apply_configuration_from_file options, environment_merb_yml + +- case Merb::Config[:environment].to_s +- when 'production' +- Merb::Config[:reloader] = Merb::Config.fetch(:reloader, false) +- Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, false) +- Merb::Config[:cache_templates] = true +- else +- Merb::Config[:reloader] = Merb::Config.fetch(:reloader, true) +- Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, true) +- end +- +- Merb::Config[:reloader_time] ||= 0.5 if Merb::Config[:reloader] == true +- +- +- if Merb::Config[:reloader] +- Thread.abort_on_exception = true +- Thread.new do +- loop do +- sleep( Merb::Config[:reloader_time] ) +- ::Merb::BootLoader.reload if ::Merb::BootLoader.app_loaded? +- end +- Thread.exit +- end +- end ++ # case Merb::Config[:environment].to_s ++ # when 'production' ++ # Merb::Config[:reloader] = Merb::Config.fetch(:reloader, false) ++ # Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, false) ++ # Merb::Config[:cache_templates] = true ++ # else ++ # Merb::Config[:reloader] = Merb::Config.fetch(:reloader, true) ++ # Merb::Config[:exception_details] = Merb::Config.fetch(:exception_details, true) ++ # end ++ # ++ # Merb::Config[:reloader_time] ||= 0.5 if Merb::Config[:reloader] == true ++ # ++ # ++ # if Merb::Config[:reloader] ++ # Thread.abort_on_exception = true ++ # Thread.new do ++ # loop do ++ # sleep( Merb::Config[:reloader_time] ) ++ # ::Merb::BootLoader.reload if ::Merb::BootLoader.app_loaded? ++ # end ++ # Thread.exit ++ # end ++ # end + @configuration + end + +diff --git a/lib/merb_core/controller/abstract_controller.rb b/lib/merb_core/controller/abstract_controller.rb +index fbf83372793da6da4b803b799994f0e341fddf88..f5e9a59057d67a6d56377a516a726cf51aa03d6f 100644 +--- a/lib/merb_core/controller/abstract_controller.rb ++++ b/lib/merb_core/controller/abstract_controller.rb +@@ -96,7 +96,7 @@ class Merb::AbstractController + # the superclass. + #--- + # @public +- def _template_location(action, controller = controller_name, type = nil) ++ def _template_location(action, type = nil, controller = controller_name) + "#{controller}/#{action}" + end + +@@ -106,6 +106,8 @@ class Merb::AbstractController + # own subclasses. We're using a Set so we don't have to worry about + # uniqueness. + self._abstract_subclasses = Set.new ++ self._template_root = Merb.dir_for(:view) ++ + def self.subclasses_list() _abstract_subclasses end + + class << self +@@ -114,7 +116,6 @@ class Merb::AbstractController + # The controller that is being inherited from Merb::AbstractController + def inherited(klass) + _abstract_subclasses << klass.to_s +- klass._template_root ||= Merb.dir_for(:view) + super + end + +diff --git a/lib/merb_core/controller/merb_controller.rb b/lib/merb_core/controller/merb_controller.rb +index 7283f006bb0501b29f825da129600cf045264b62..98af6ef3330a6b3f46d7bb1f8643261e28155ae5 100644 +--- a/lib/merb_core/controller/merb_controller.rb ++++ b/lib/merb_core/controller/merb_controller.rb +@@ -71,6 +71,10 @@ class Merb::Controller < Merb::AbstractController + end + end + ++ def _template_location(action, type = nil, controller = controller_name) ++ "#{controller}/#{action}.#{type}" ++ end ++ + # Sets the variables that came in through the dispatch as available to + # the controller. This is called by .build, so see it for more + # information. +@@ -107,9 +111,7 @@ class Merb::Controller < Merb::AbstractController + request.cookies[_session_id_key] = request.params[_session_id_key] + end + end +- @_request, @_response, @_status, @_headers = +- request, response, status, headers +- ++ @request, @response, @status, @headers = request, response, status, headers + nil + end + +@@ -135,7 +137,8 @@ class Merb::Controller < Merb::AbstractController + @_benchmarks[:action_time] = Time.now - start + end + +- _attr_reader :request, :response, :status, :headers ++ attr_reader :request, :response, :headers ++ attr_accessor :status + def params() request.params end + def cookies() request.cookies end + def session() request.session end +diff --git a/lib/merb_core/controller/mime.rb b/lib/merb_core/controller/mime.rb +index d17570786ca318cff7201c4b1e947ae229b01de8..ff9abe4d1c452aeabfcf5f7dc7a2c7cdd3f67035 100644 +--- a/lib/merb_core/controller/mime.rb ++++ b/lib/merb_core/controller/mime.rb +@@ -8,7 +8,7 @@ module Merb + + # Any specific outgoing headers should be included here. These are not + # the content-type header but anything in addition to it. +- # +tranform_method+ should be set to a symbol of the method used to ++ # +transform_method+ should be set to a symbol of the method used to + # transform a resource into this mime type. + # For example for the :xml mime type an object might be transformed by + # calling :to_xml, or for the :js mime type, :to_json. +@@ -71,27 +71,6 @@ module Merb + def mime_by_request_header(header) + available_mime_types.find {|key,info| info[request_headers].include?(header)}.first + end +- +- # Resets the default mime-types +- # +- # By default, the mime-types include: +- # :all:: no transform, */* +- # :yaml:: to_yaml, application/x-yaml or text/yaml +- # :text:: to_text, text/plain +- # :html:: to_html, text/html or application/xhtml+xml or application/html +- # :xml:: to_xml, application/xml or text/xml or application/x-xml, adds "Encoding: UTF-8" response header +- # :js:: to_json, text/javascript ot application/javascript or application/x-javascript +- # :json:: to_json, application/json or text/x-json +- def reset_default_mime_types! +- available_mime_types.clear +- Merb.add_mime_type(:all, nil, %w[*/*]) +- Merb.add_mime_type(:yaml, :to_yaml, %w[application/x-yaml text/yaml]) +- Merb.add_mime_type(:text, :to_text, %w[text/plain]) +- Merb.add_mime_type(:html, :to_html, %w[text/html application/xhtml+xml application/html]) +- Merb.add_mime_type(:xml, :to_xml, %w[application/xml text/xml application/x-xml], :Encoding => "UTF-8") +- Merb.add_mime_type(:js, :to_json, %w[text/javascript application/javascript application/x-javascript]) +- Merb.add_mime_type(:json, :to_json, %w[application/json text/x-json]) +- end + + end + end +\ No newline at end of file +diff --git a/lib/merb_core/controller/mixins/render.rb b/lib/merb_core/controller/mixins/render.rb +index 8e096546d4647bb597ab2e00a4b15d09db35e9c9..a298263af7d655d9ce43007554f3827046831287 100644 +--- a/lib/merb_core/controller/mixins/render.rb ++++ b/lib/merb_core/controller/mixins/render.rb +@@ -51,21 +51,22 @@ module Merb::RenderMixin + + # If you don't specify a thing to render, assume they want to render the current action + thing ||= action_name.to_sym +- ++ + # Content negotiation + opts[:format] ? (self.content_type = opts[:format]) : content_type + + # Do we have a template to try to render? + if thing.is_a?(Symbol) || opts[:template] +- ++ + # Find a template path to look up (_template_location adds flexibility here) +- template_location = _template_root / (opts[:template] || _template_location(thing)) ++ template_location = _template_root / (opts[:template] || _template_location(thing, content_type)) ++ + # Get the method name from the previously inlined list + template_method = Merb::Template.template_for(template_location) + + # Raise an error if there's no template + raise TemplateNotFound, "No template found at #{template_location}" unless +- self.respond_to?(template_method) ++ template_method && self.respond_to?(template_method) + + # Call the method in question and throw the content for later consumption by the layout + throw_content(:for_layout, self.send(template_method)) +diff --git a/lib/merb_core/controller/mixins/responder.rb b/lib/merb_core/controller/mixins/responder.rb +index e910b2b32c844ab51cf2a10d0ad26c314dbb3631..5ac67fb907aaf9f95effc7eb3cbb07b8963ce022 100644 +--- a/lib/merb_core/controller/mixins/responder.rb ++++ b/lib/merb_core/controller/mixins/responder.rb +@@ -97,6 +97,8 @@ module Merb + # and none of the provides methods can be used. + module ResponderMixin + ++ TYPES = {} ++ + class ContentTypeAlreadySet < StandardError; end + + # ==== Parameters +@@ -105,6 +107,7 @@ module Merb + base.extend(ClassMethods) + base.class_eval do + class_inheritable_accessor :class_provided_formats ++ self.class_provided_formats = [] + end + base.reset_provides + end +@@ -178,171 +181,253 @@ module Merb + def reset_provides + only_provides(:html) + end +- +- # ==== Returns +- # The current list of formats provided for this instance of the controller. +- # It starts with what has been set in the controller (or :html by default) +- # but can be modifed on a per-action basis. +- def _provided_formats +- @_provided_formats ||= class_provided_formats.dup ++ end ++ ++ # ==== Returns ++ # The current list of formats provided for this instance of the controller. ++ # It starts with what has been set in the controller (or :html by default) ++ # but can be modifed on a per-action basis. ++ def _provided_formats ++ @_provided_formats ||= class_provided_formats.dup ++ end ++ ++ # Sets the provided formats for this action. Usually, you would ++ # use a combination of +provides+, +only_provides+ and +does_not_provide+ ++ # to manage this, but you can set it directly. ++ # ++ # ==== Parameters ++ # *formats:: A list of formats to be passed to provides ++ # ++ # ==== Raises ++ # Merb::ResponderMixin::ContentTypeAlreadySet:: ++ # Content negotiation already occured, and the content_type is set. ++ # ++ # ==== Returns ++ # Array:: List of formats passed in ++ def _set_provided_formats(*formats) ++ if @_content_type ++ raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set" + end +- +- # Sets the provided formats for this action. Usually, you would +- # use a combination of +provides+, +only_provides+ and +does_not_provide+ +- # to manage this, but you can set it directly. +- # +- # ==== Parameters +- # *formats:: A list of formats to be passed to provides +- # +- # ==== Raises +- # Merb::ResponderMixin::ContentTypeAlreadySet:: +- # Content negotiation already occured, and the content_type is set. +- # +- # ==== Returns +- # Array:: List of formats passed in +- def _set_provided_formats(*formats) +- if @_content_type +- raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set" +- end +- @_provided_formats = [] +- provides(*formats) ++ @_provided_formats = [] ++ provides(*formats) ++ end ++ alias :_provided_formats= :_set_provided_formats ++ ++ # Adds formats to the list of provided formats for this particular ++ # request. Usually used to add formats to a single action. See also ++ # the controller-level provides that affects all actions in a controller. ++ # ++ # ==== Parameters ++ # *formats:: A list of formats to add to the per-action list ++ # of provided formats ++ # ++ # ==== Raises ++ # Merb::ResponderMixin::ContentTypeAlreadySet:: ++ # Content negotiation already occured, and the content_type is set. ++ # ++ # ==== Returns ++ # Array:: List of formats passed in ++ # ++ #--- ++ # @public ++ def provides(*formats) ++ if @_content_type ++ raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set" + end +- alias :_provided_formats= :_set_provided_formats +- +- # Adds formats to the list of provided formats for this particular +- # request. Usually used to add formats to a single action. See also +- # the controller-level provides that affects all actions in a controller. +- # +- # ==== Parameters +- # *formats:: A list of formats to add to the per-action list +- # of provided formats +- # +- # ==== Raises +- # Merb::ResponderMixin::ContentTypeAlreadySet:: +- # Content negotiation already occured, and the content_type is set. +- # +- # ==== Returns +- # Array:: List of formats passed in +- # +- #--- +- # @public +- def provides(*formats) +- if @_content_type +- raise ContentTypeAlreadySet, "Cannot modify provided_formats because content_type has already been set" +- end +- formats.each do |fmt| +- _provided_formats << fmt unless _provided_formats.include?(fmt) +- end ++ formats.each do |fmt| ++ _provided_formats << fmt unless _provided_formats.include?(fmt) + end ++ end + +- # Sets list of provided formats for this particular +- # request. Usually used to limit formats to a single action. See also +- # the controller-level only_provides that affects all actions +- # in a controller. +- # +- # ==== Parameters +- # *formats:: A list of formats to use as the per-action list +- # of provided formats +- # +- # ==== Returns +- # Array:: List of formats passed in +- # +- #--- +- # @public +- def only_provides(*formats) +- self._provided_formats = *formats +- end +- +- # Removes formats from the list of provided formats for this particular +- # request. Usually used to remove formats from a single action. See +- # also the controller-level does_not_provide that affects all actions in a +- # controller. +- # +- # ==== Parameters +- # *formats:: Registered mime-type +- # +- # ==== Returns +- # Array:: List of formats that remain after removing the ones not to provide +- # +- #--- +- # @public +- def does_not_provide(*formats) +- formats.flatten! +- self._provided_formats -= formats +- end +- +- # Do the content negotiation: +- # 1. if params[:format] is there, and provided, use it +- # 2. Parse the Accept header +- # 3. If it's */*, use the first provided format +- # 4. Look for one that is provided, in order of request +- # 5. Raise 406 if none found +- def _perform_content_negotiation # :nodoc: +- raise Merb::ControllerExceptions::NotAcceptable if provided_formats.empty? +- if fmt = params[:format] +- return fmt.to_sym if provided_formats.include?(fmt.to_sym) +- else +- accepts = Responder.parse(request.accept).map {|t| t.to_sym} +- return provided_formats.first if accepts.include?(:all) +- return accepts.each { |type| break type if provided_formats.include?(type) } +- end +- raise Merb::ControllerExceptions::NotAcceptable ++ # Sets list of provided formats for this particular ++ # request. Usually used to limit formats to a single action. See also ++ # the controller-level only_provides that affects all actions ++ # in a controller. ++ # ++ # ==== Parameters ++ # *formats:: A list of formats to use as the per-action list ++ # of provided formats ++ # ++ # ==== Returns ++ # Array:: List of formats passed in ++ # ++ #--- ++ # @public ++ def only_provides(*formats) ++ self._provided_formats = *formats ++ end ++ ++ # Removes formats from the list of provided formats for this particular ++ # request. Usually used to remove formats from a single action. See ++ # also the controller-level does_not_provide that affects all actions in a ++ # controller. ++ # ++ # ==== Parameters ++ # *formats:: Registered mime-type ++ # ++ # ==== Returns ++ # Array:: List of formats that remain after removing the ones not to provide ++ # ++ #--- ++ # @public ++ def does_not_provide(*formats) ++ formats.flatten! ++ self._provided_formats -= formats ++ end ++ ++ # Do the content negotiation: ++ # 1. if params[:format] is there, and provided, use it ++ # 2. Parse the Accept header ++ # 3. If it's */*, use the first provided format ++ # 4. Look for one that is provided, in order of request ++ # 5. Raise 406 if none found ++ def _perform_content_negotiation # :nodoc: ++ raise Merb::ControllerExceptions::NotAcceptable if _provided_formats.empty? ++ if fmt = params[:format] && _provided_formats.include?(fmt.to_sym) ++ return fmt.to_sym + end ++ accepts = Responder.parse(request.accept).map {|t| t.to_sym} ++ return _provided_formats.first if accepts.include?(:all) ++ (accepts & _provided_formats).first || (raise Merb::ControllerExceptions::NotAcceptable) ++ end + +- # Returns the output format for this request, based on the +- # provided formats, params[:format] and the client's HTTP +- # Accept header. +- # +- # The first time this is called, it triggers content negotiation +- # and caches the value. Once you call +content_type+ you can +- # not set or change the list of provided formats. +- # +- # Called automatically by +render+, so you should only call it if +- # you need the value, not to trigger content negotiation. +- # +- # ==== Parameters +- # fmt:: +- # An optional format to use instead of performing content negotiation. +- # This can be used to pass in the values of opts[:format] from the +- # render function to short-circuit content-negotiation when it's not +- # necessary. This optional parameter should not be considered part +- # of the public API. +- # +- # ==== Returns +- # Symbol:: The content-type that will be used for this controller. +- # +- #--- +- # @public +- def content_type(fmt = nil) +- self.content_type = (fmt || _perform_content_negotiation) unless @_content_type +- @_content_type ++ # Returns the output format for this request, based on the ++ # provided formats, params[:format] and the client's HTTP ++ # Accept header. ++ # ++ # The first time this is called, it triggers content negotiation ++ # and caches the value. Once you call +content_type+ you can ++ # not set or change the list of provided formats. ++ # ++ # Called automatically by +render+, so you should only call it if ++ # you need the value, not to trigger content negotiation. ++ # ++ # ==== Parameters ++ # fmt:: ++ # An optional format to use instead of performing content negotiation. ++ # This can be used to pass in the values of opts[:format] from the ++ # render function to short-circuit content-negotiation when it's not ++ # necessary. This optional parameter should not be considered part ++ # of the public API. ++ # ++ # ==== Returns ++ # Symbol:: The content-type that will be used for this controller. ++ # ++ #--- ++ # @public ++ def content_type(fmt = nil) ++ @_content_type = (fmt || _perform_content_negotiation) unless @_content_type ++ @_content_type ++ end ++ ++ # Sets the content type of the current response to a value based on ++ # a passed in key. The Content-Type header will be set to the first ++ # registered header for the mime-type. ++ # ++ # ==== Parameters ++ # type:: A type that is in the list of registered mime-types. ++ # ++ # ==== Raises ++ # ArgumentError:: "type" is not in the list of registered mime-types. ++ # ++ # ==== Returns ++ # Symbol:: The content-type that was passed in. ++ # ++ #--- ++ # @semipublic ++ def content_type=(type) ++ unless Merb.available_mime_types.has_key?(type) ++ raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{type}") ++ end ++ headers['Content-Type'] = Merb.available_mime_types[type].first ++ @_content_type = type ++ end ++ ++ end ++ ++ class Responder ++ ++ protected ++ def self.parse(accept_header) ++ # parse the raw accept header into a unique, sorted array of AcceptType objects ++ list = accept_header.to_s.split(/,/).enum_for(:each_with_index).map do |entry,index| ++ AcceptType.new(entry,index += 1) ++ end.sort.uniq ++ # firefox (and possibly other browsers) send broken default accept headers. ++ # fix them up by sorting alternate xml forms (namely application/xhtml+xml) ++ # ahead of pure xml types (application/xml,text/xml). ++ if app_xml = list.detect{|e| e.super_range == 'application/xml'} ++ list.select{|e| e.to_s =~ /\+xml/}.each { |acc_type| ++ list[list.index(acc_type)],list[list.index(app_xml)] = ++ list[list.index(app_xml)],list[list.index(acc_type)] } + end +- +- # Sets the content type of the current response to a value based on +- # a passed in key. The Content-Type header will be set to the first +- # registered header for the mime-type. +- # +- # ==== Parameters +- # type:: A type that is in the list of registered mime-types. +- # +- # ==== Raises +- # ArgumentError:: "type" is not in the list of registered mime-types. +- # +- # ==== Returns +- # Symbol:: The content-type that was passed in. +- # +- #--- +- # @semipublic +- def content_type=(type) +- unless Merb.available_mime_types.has_key?(type) +- raise Merb::ControllerExceptions::NotAcceptable.new("Unknown content_type for response: #{type}") +- end +- headers['Content-Type'] = Merb.available_mime_types[type].first +- @_content_type = type ++ list ++ end ++ ++ public ++ def self.params_to_query_string(value, prefix = nil) ++ case value ++ when Array ++ value.map { |v| ++ params_to_query_string(v, "#{prefix}[]") ++ } * "&" ++ when Hash ++ value.map { |k, v| ++ params_to_query_string(v, prefix ? "#{prefix}[#{Merb::Request.escape(k)}]" : Merb::Request.escape(k)) ++ } * "&" ++ else ++ "#{prefix}=#{Merb::Request.escape(value)}" + end ++ end + +- end ++ end ++ ++ class AcceptType ++ ++ attr_reader :media_range, :quality, :index, :type, :sub_type + ++ def initialize(entry,index) ++ @index = index ++ @media_range, quality = entry.split(/;\s*q=/).map{|a| a.strip } ++ @type, @sub_type = @media_range.split(/\//) ++ quality ||= 0.0 if @media_range == '*/*' ++ @quality = ((quality || 1.0).to_f * 100).to_i ++ end ++ ++ def <=>(entry) ++ c = entry.quality <=> quality ++ c = index <=> entry.index if c == 0 ++ c ++ end ++ ++ def eql?(entry) ++ synonyms.include?(entry.media_range) ++ end ++ ++ def ==(entry); eql?(entry); end ++ ++ def hash; super_range.hash; end ++ ++ def synonyms ++ @syns ||= Merb.available_mime_types.values.map do |e| ++ e[:request_headers] if e[:request_headers].include?(@media_range) ++ end.compact.flatten ++ end ++ ++ def super_range ++ synonyms.first || @media_range ++ end ++ ++ def to_sym ++ Merb.available_mime_types.select{|k,v| ++ v[:request_headers] == synonyms || v[:request_headers][0] == synonyms[0]}.flatten.first ++ end ++ ++ def to_s ++ @media_range ++ end ++ + end ++ + + end +\ No newline at end of file +diff --git a/lib/merb_core/dispatch/dispatcher.rb b/lib/merb_core/dispatch/dispatcher.rb +index c458c9f9ad454d3b0c3055d6b2a8e88b17712b44..f7fed0f539a20f9cce08b72c551725ad0563bf37 100644 +--- a/lib/merb_core/dispatch/dispatcher.rb ++++ b/lib/merb_core/dispatch/dispatcher.rb +@@ -33,10 +33,10 @@ class Merb::Dispatcher + + # this is the custom dispatch_exception; it allows failures to still be dispatched + # to the error controller +- rescue => exception +- Merb.logger.error(Merb.exception(exception)) +- exception = controller_exception(exception) +- dispatch_exception(request, response, exception) ++ # rescue => exception ++ # Merb.logger.error(Merb.exception(exception)) ++ # exception = controller_exception(exception) ++ # dispatch_exception(request, response, exception) + end + + private +@@ -49,10 +49,10 @@ class Merb::Dispatcher + def dispatch_action(klass, action, request, response, status=200) + # build controller + controller = klass.build(request, response, status) +- if @@use_mutex +- @@mutex.synchronize { controller.dispatch(action) } ++ if use_mutex ++ @@mutex.synchronize { controller._dispatch(action) } + else +- controller.dispatch(action) ++ controller._dispatch(action) + end + [controller, action] + end +diff --git a/lib/merb_core/rack/adapter.rb b/lib/merb_core/rack/adapter.rb +index ffc7117e9733e83b0567bbe4a43fac7663800b7d..217399a5382d0b3878aaea3d3e302173c5b5f119 100644 +--- a/lib/merb_core/rack/adapter.rb ++++ b/lib/merb_core/rack/adapter.rb +@@ -40,7 +40,7 @@ module Merb + begin + controller, action = ::Merb::Dispatcher.handle(request, response) + rescue Object => e +- return [500, {"Content-Type"=>"text/html"}, "Internal Server Error"] ++ return [500, {"Content-Type"=>"text/html"}, e.message + "
" + e.backtrace.join("
")] + end + [controller.status, controller.headers, controller.body] + end +diff --git a/lib/merb_core/test/request_helper.rb b/lib/merb_core/test/request_helper.rb +index 10a9fb3ace56eaf1db0fa300df3fb2ab88a7118a..f302a3b71539182ba142cd208fe6d6aae171b1a1 100644 +--- a/lib/merb_core/test/request_helper.rb ++++ b/lib/merb_core/test/request_helper.rb +@@ -26,8 +26,10 @@ module Merb::Test::RequestHelper + Merb::Test::FakeRequest.new(env, StringIO.new(req)) + end + +- def dispatch_to(controller_klass, action, env = {}, opt = {}, &blk) +- request = fake_request(env, opt) ++ def dispatch_to(controller_klass, action, params = {}, env = {}, &blk) ++ request = fake_request(env, ++ :query_string => Merb::Responder.params_to_query_string(params)) ++ + controller = controller_klass.build(request) + controller.instance_eval(&blk) if block_given? + controller._dispatch(action) +diff --git a/spec/public/abstract_controller/spec_helper.rb b/spec/public/abstract_controller/spec_helper.rb +index df759008d14e7572b5c44de24f77f828f83f1682..694cee2592a210a5c1fa40ca7846beeaa09725fe 100644 +--- a/spec/public/abstract_controller/spec_helper.rb ++++ b/spec/public/abstract_controller/spec_helper.rb +@@ -1,12 +1,10 @@ + __DIR__ = File.dirname(__FILE__) + require File.join(__DIR__, "..", "..", "spec_helper") + +-# The framework structure *must* be set up before loading in framework +-# files. + require File.join(__DIR__, "controllers", "filters") + require File.join(__DIR__, "controllers", "render") + +-Merb::BootLoader::Templates.new.run ++Merb::BootLoader::Templates.run + + module Merb::Test::Behaviors + def dispatch_should_make_body(klass, body, action = :index) +diff --git a/spec/public/controller/base_spec.rb b/spec/public/controller/base_spec.rb +index 1709e612629ed2c2b6af4579a8b89684aca9aa3c..5bcdb59948cc22592639b1aee9bd233ff2c306fa 100644 +--- a/spec/public/controller/base_spec.rb ++++ b/spec/public/controller/base_spec.rb +@@ -10,11 +10,11 @@ describe Merb::Controller, " callable actions" do + end + + it "should dispatch to callable actions" do +- dispatch_to(Merb::Test::Fixtures::TestFoo, :index).body.should == "index" ++ dispatch_to(Merb::Test::Fixtures::TestBase, :index).body.should == "index" + end + + it "should not dispatch to hidden actions" do +- calling { dispatch_to(Merb::Test::Fixtures::TestFoo, :hidden) }. ++ calling { dispatch_to(Merb::Test::Fixtures::TestBase, :hidden) }. + should raise_error(Merb::ControllerExceptions::ActionNotFound) + end + +diff --git a/spec/public/controller/controllers/base.rb b/spec/public/controller/controllers/base.rb +index a1b3beb27899df781d943427d9b23945f02e14de..c4b69a440a9da3c3486208d2cb95ccb8bdb974b9 100644 +--- a/spec/public/controller/controllers/base.rb ++++ b/spec/public/controller/controllers/base.rb +@@ -3,7 +3,7 @@ module Merb::Test::Fixtures + self._template_root = File.dirname(__FILE__) / "views" + end + +- class TestFoo < ControllerTesting ++ class TestBase < ControllerTesting + def index + "index" + end +diff --git a/spec/public/controller/controllers/responder.rb b/spec/public/controller/controllers/responder.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..867192e8f6e995a43fd5cd3daffa0ec11b3d31e5 +--- /dev/null ++++ b/spec/public/controller/controllers/responder.rb +@@ -0,0 +1,25 @@ ++module Merb::Test::Fixtures ++ class ControllerTesting < Merb::Controller ++ self._template_root = File.dirname(__FILE__) / "views" ++ end ++ ++ class TestResponder < ControllerTesting ++ def index ++ render ++ end ++ end ++ ++ class TestHtmlDefault < TestResponder; end ++ ++ class TestClassProvides < TestResponder; ++ provides :xml ++ end ++ ++ class TestLocalProvides < TestResponder; ++ def index ++ provides :xml ++ render ++ end ++ end ++ ++end +\ No newline at end of file +diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.html.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.html.erb +new file mode 100644 +index 0000000000000000000000000000000000000000..1bfb77d4a44c444bba6888ae7740f7df4b074c58 +--- /dev/null ++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.html.erb +@@ -0,0 +1 @@ ++This should not be rendered +\ No newline at end of file +diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.xml.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.xml.erb +new file mode 100644 +index 0000000000000000000000000000000000000000..7c91f633987348e87e5e34e1d9e87d9dd0e5100c +--- /dev/null ++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_class_provides/index.xml.erb +@@ -0,0 +1 @@ ++ +\ No newline at end of file +diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_html_default/index.html.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_html_default/index.html.erb +new file mode 100644 +index 0000000000000000000000000000000000000000..eb4b52bf5a7aaba8f1706de419f42789c05684a2 +--- /dev/null ++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_html_default/index.html.erb +@@ -0,0 +1 @@ ++HTML: Default +\ No newline at end of file +diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.html.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.html.erb +new file mode 100644 +index 0000000000000000000000000000000000000000..a3a841a89c62e6174038935a42da9cd24ff54413 +--- /dev/null ++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.html.erb +@@ -0,0 +1 @@ ++This should not render +\ No newline at end of file +diff --git a/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.xml.erb b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.xml.erb +new file mode 100644 +index 0000000000000000000000000000000000000000..c1384ec6af0357b585cc367035d1bc3a30347ade +--- /dev/null ++++ b/spec/public/controller/controllers/views/merb/test/fixtures/test_local_provides/index.xml.erb +@@ -0,0 +1 @@ ++ +\ No newline at end of file +diff --git a/spec/public/controller/responder_spec.rb b/spec/public/controller/responder_spec.rb +index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bcf18532442e5965cf6ca8501770d7b7a1eb2429 100644 +--- a/spec/public/controller/responder_spec.rb ++++ b/spec/public/controller/responder_spec.rb +@@ -0,0 +1,31 @@ ++require File.join(File.dirname(__FILE__), "spec_helper") ++ ++describe Merb::Controller, " responds" do ++ ++ before do ++ Merb.push_path(:layout, File.dirname(__FILE__) / "controllers" / "views" / "layouts") ++ Merb::Router.prepare do |r| ++ r.default_routes ++ end ++ end ++ ++ it "should default the mime-type to HTML" do ++ dispatch_to(Merb::Test::Fixtures::TestHtmlDefault, :index).body.should == "HTML: Default" ++ end ++ ++ it "should use other mime-types if they are provided on the class level" do ++ controller = dispatch_to(Merb::Test::Fixtures::TestClassProvides, :index, {}, :http_accept => "application/xml") ++ controller.body.should == "" ++ end ++ ++ it "should fail if none of the acceptable mime-types are available" do ++ calling { dispatch_to(Merb::Test::Fixtures::TestClassProvides, :index, {}, :http_accept => "application/json") }. ++ should raise_error(Merb::ControllerExceptions::NotAcceptable) ++ end ++ ++ it "should use mime-types that are provided at the local level" do ++ controller = dispatch_to(Merb::Test::Fixtures::TestLocalProvides, :index, {}, :http_accept => "application/xml") ++ controller.body.should == "" ++ end ++ ++end +\ No newline at end of file +diff --git a/spec/public/controller/spec_helper.rb b/spec/public/controller/spec_helper.rb +index f68628a63740f4ce0235a15d71c5889e55ecaf78..e360194c1fbaf72c3298c61543c2d3a19b512b41 100644 +--- a/spec/public/controller/spec_helper.rb ++++ b/spec/public/controller/spec_helper.rb +@@ -1,4 +1,10 @@ + __DIR__ = File.dirname(__FILE__) ++require 'ruby-debug' ++ + require File.join(__DIR__, "..", "..", "spec_helper") + +-require File.join(__DIR__, "controllers", "base") +\ No newline at end of file ++require File.join(__DIR__, "controllers", "base") ++require File.join(__DIR__, "controllers", "responder") ++ ++Merb::BootLoader::Templates.run ++Merb::BootLoader::MimeTypes.run +\ No newline at end of file diff --git a/tests/fixtures/diff_new_mode b/tests/fixtures/diff_new_mode new file mode 100644 index 0000000..663c909 --- /dev/null +++ b/tests/fixtures/diff_new_mode @@ -0,0 +1,14 @@ +diff --git a/conf/global_settings.py b/conf/global_settings.py +old mode 100644 +new mode 100755 +index 9ec1bac..1c4f83b +--- a/conf/global_settings.py ++++ b/conf/global_settings.py +@@ -58,6 +58,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( + ) + + MIDDLEWARE_CLASSES = ( ++ "django.middleware.cache.CacheMiddleware", + "django.middleware.common.CommonMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", diff --git a/tests/fixtures/diff_numstat b/tests/fixtures/diff_numstat new file mode 100644 index 0000000..44c6ca2 --- /dev/null +++ b/tests/fixtures/diff_numstat @@ -0,0 +1,2 @@ +29 18 a.txt +0 5 b.txt diff --git a/tests/fixtures/diff_p b/tests/fixtures/diff_p new file mode 100644 index 0000000..af4759e --- /dev/null +++ b/tests/fixtures/diff_p @@ -0,0 +1,610 @@ +diff --git a/.gitignore b/.gitignore +index 4ebc8aea50e0a67e000ba29a30809d0a7b9b2666..2dd02534615434d88c51307beb0f0092f21fd103 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1 +1,2 @@ + coverage ++pkg +diff --git a/Manifest.txt b/Manifest.txt +index 641972d82c6d1b51122274ae8f6a0ecdfb56ee22..38bf80c54a526e76d74820a0f48606fe1ca7b1be 100644 +--- a/Manifest.txt ++++ b/Manifest.txt +@@ -4,4 +4,31 @@ README.txt + Rakefile + bin/grit + lib/grit.rb +-test/test_grit.rb +\ No newline at end of file ++lib/grit/actor.rb ++lib/grit/blob.rb ++lib/grit/commit.rb ++lib/grit/errors.rb ++lib/grit/git.rb ++lib/grit/head.rb ++lib/grit/lazy.rb ++lib/grit/repo.rb ++lib/grit/tree.rb ++test/fixtures/blame ++test/fixtures/cat_file_blob ++test/fixtures/cat_file_blob_size ++test/fixtures/for_each_ref ++test/fixtures/ls_tree_a ++test/fixtures/ls_tree_b ++test/fixtures/rev_list ++test/fixtures/rev_list_single ++test/helper.rb ++test/profile.rb ++test/suite.rb ++test/test_actor.rb ++test/test_blob.rb ++test/test_commit.rb ++test/test_git.rb ++test/test_head.rb ++test/test_reality.rb ++test/test_repo.rb ++test/test_tree.rb +diff --git a/README.txt b/README.txt +index 8b1e02c0fb554eed2ce2ef737a68bb369d7527df..fca94f84afd7d749c62626011f972a509f6a5ac6 100644 +--- a/README.txt ++++ b/README.txt +@@ -1,32 +1,185 @@ + grit +- by FIX (your name) +- FIX (url) ++ by Tom Preston-Werner ++ grit.rubyforge.org + + == DESCRIPTION: ++ ++Grit is a Ruby library for extracting information from a git repository in and ++object oriented manner. ++ ++== REQUIREMENTS: ++ ++* git (http://git.or.cz) tested with 1.5.3.4 ++ ++== INSTALL: ++ ++sudo gem install grit ++ ++== 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 GitPython.Repo object to represent your repo. I ++include the Grit module so reduce typing. ++ ++ include Grit ++ repo = Repo.new("/Users/tom/dev/grit") + +-FIX (describe your package) ++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. + +-== FEATURES/PROBLEMS: ++ repo = Repo.new("/var/git/grit.git") + +-* FIX (list of features or problems) ++= Getting a list of commits + +-== SYNOPSIS: ++From the Repo object, you can get a list of commits as an array of Commit ++objects. + +- FIX (code sample of usage) ++ repo.commits ++ # => [#, ++ #, ++ #, ++ #, ++ #] ++ ++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. + +-== REQUIREMENTS: ++ repo.commits('mybranch') ++ repo.commits('40d3057d09a7a4d61059bca9dca5ae698de58cbe') ++ repo.commits('v0.1') ++ ++You can specify the maximum number of commits to return. + +-* FIX (list of requirements) ++ repo.commits('master', 100) ++ ++If you need paging, you can specify a number of commits to skip. + +-== INSTALL: ++ 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 ++ # => [#] ++ ++ head.tree ++ # => # ++ ++ head.author ++ # => #"> ++ ++ head.authored_date ++ # => Wed Oct 24 22:02:31 -0700 2007 ++ ++ head.committer ++ # => #"> ++ ++ 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 ++ # => # ++ ++ tree.id ++ # => "3536eb9abac69c3e4db583ad38f3d30f8db4771f" ++ ++Once you have a tree, you can get the contents. ++ ++ contents = tree.contents ++ # => [#, ++ #, ++ #, ++ #] ++ ++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" ++ # => # ++ ++You can also get a tree directly from the repo if you know its name. ++ ++ repo.tree ++ # => # ++ ++ repo.tree("91169e1f5fa4de2eaea3f176461f5dc784796769") ++ # => # ++ ++= The Blob object ++ ++A blob represents a file. Trees often contain blobs. ++ ++ blob = tree.contents.first ++ # => # ++ ++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. + +-* FIX (sudo gem install, anything else) ++ repo.blob("4ebc8aea50e0a67e000ba29a30809d0a7b9b2666") ++ # => # + + == LICENSE: + + (The MIT License) + +-Copyright (c) 2007 FIX ++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 +diff --git a/Rakefile b/Rakefile +index 5bfb62163af455ca54422fd0b2e723ba1021ad12..72fde8c9ca87a1c992ce992bab13c3c4f13cddb9 100644 +--- a/Rakefile ++++ b/Rakefile +@@ -4,11 +4,11 @@ require './lib/grit.rb' + + Hoe.new('grit', GitPython.VERSION) do |p| + p.rubyforge_name = 'grit' +- # p.author = 'FIX' +- # p.email = 'FIX' +- # p.summary = 'FIX' +- # p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n") +- # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1] ++ p.author = 'Tom Preston-Werner' ++ p.email = 'tom@rubyisawesome.com' ++ p.summary = 'Object model interface to a git repo' ++ p.description = p.paragraphs_of('README.txt', 2..2).join("\n\n") ++ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[2..-1].map { |u| u.strip } + p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n") + end + +diff --git a/lib/grit.rb b/lib/grit.rb +index ae0792ae39d4891ebc1af996102a4f9df703394d..ae55fd7961ac49233f6ca515622a61e90d516044 100644 +--- a/lib/grit.rb ++++ b/lib/grit.rb +@@ -1,4 +1,4 @@ +-$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed ++$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed + + # core + +@@ -12,6 +12,8 @@ require 'grit/head' + require 'grit/commit' + require 'grit/tree' + require 'grit/blob' ++require 'grit/actor' ++require 'grit/diff' + require 'grit/repo' + + module Grit +@@ -21,5 +23,5 @@ module Grit + + self.debug = false + +- VERSION = '1.0.0' ++ VERSION = '0.1.0' + end +\ No newline at end of file +diff --git a/lib/grit/actor.rb b/lib/grit/actor.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..f733bce6b57c0e5e353206e692b0e3105c2527f4 +--- /dev/null ++++ b/lib/grit/actor.rb +@@ -0,0 +1,35 @@ ++module Grit ++ ++ class Actor ++ attr_reader :name ++ attr_reader :email ++ ++ def initialize(name, email) ++ @name = name ++ @email = email ++ end ++ ++ # Create an Actor from a string. ++ # +str+ is the string, which is expected to be in regular git format ++ # ++ # Format ++ # John Doe ++ # ++ # Returns Actor ++ def self.from_string(str) ++ case str ++ when /<.+>/ ++ m, name, email = *str.match(/(.*) <(.+?)>/) ++ return self.new(name, email) ++ else ++ return self.new(str, nil) ++ end ++ end ++ ++ # Pretty object inspection ++ def inspect ++ %Q{#">} ++ end ++ end # Actor ++ ++end # Grit +\ No newline at end of file +diff --git a/lib/grit/blob.rb b/lib/grit/blob.rb +index c863646d4278bfee2a7bcb64caace6b31f89ef03..87d43fab37844afdc2f8814dba3abdaa791f1370 100644 +--- a/lib/grit/blob.rb ++++ b/lib/grit/blob.rb +@@ -81,9 +81,9 @@ module Grit + c = commits[info[:id]] + unless c + c = Commit.create(repo, :id => info[:id], +- :author => info[:author], ++ :author => Actor.from_string(info[:author] + ' ' + info[:author_email]), + :authored_date => info[:author_date], +- :committer => info[:committer], ++ :committer => Actor.from_string(info[:committer] + ' ' + info[:committer_email]), + :committed_date => info[:committer_date], + :message => info[:summary]) + commits[info[:id]] = c +@@ -102,11 +102,6 @@ module Grit + def inspect + %Q{#} + end +- +- # private +- +- def self.read_ +- end + end # Blob + + end # Grit +\ No newline at end of file +diff --git a/lib/grit/commit.rb b/lib/grit/commit.rb +index c2a9e2f81657b19925fe9bab4bc5d7ac130e5880..cd9c3e3184c97e83a8982fab9499cad3aec339f6 100644 +--- a/lib/grit/commit.rb ++++ b/lib/grit/commit.rb +@@ -136,6 +136,11 @@ module Grit + commits + end + ++ def self.diff(repo, id) ++ text = repo.git.diff({:full_index => true}, id) ++ Diff.list_from_string(repo, text) ++ end ++ + # Convert this Commit to a String which is just the SHA1 id + def to_s + @id +@@ -153,7 +158,7 @@ module Grit + # Returns [String (actor name and email), Time (acted at time)] + def self.actor(line) + m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/) +- [actor, Time.at(epoch.to_i)] ++ [Actor.from_string(actor), Time.at(epoch.to_i)] + end + end # Commit + +diff --git a/lib/grit/git.rb b/lib/grit/git.rb +index 1d5251d40fb65ac89184ec662a3e1b04d0c24861..98eeddda5ed2b0e215e21128112393bdc9bc9039 100644 +--- a/lib/grit/git.rb ++++ b/lib/grit/git.rb +@@ -13,17 +13,6 @@ module Grit + self.git_dir = git_dir + end + +- # Converstion hash from Ruby style options to git command line +- # style options +- TRANSFORM = {:max_count => "--max-count=", +- :skip => "--skip=", +- :pretty => "--pretty=", +- :sort => "--sort=", +- :format => "--format=", +- :since => "--since=", +- :p => "-p", +- :s => "-s"} +- + # Run the given git command with the specified arguments and return + # the result as a String + # +cmd+ is the command +@@ -52,12 +41,19 @@ module Grit + def transform_options(options) + args = [] + options.keys.each do |opt| +- if TRANSFORM[opt] ++ if opt.to_s.size == 1 ++ if options[opt] == true ++ args << "-#{opt}" ++ else ++ val = options.delete(opt) ++ args << "-#{opt.to_s} #{val}" ++ end ++ else + if options[opt] == true +- args << TRANSFORM[opt] ++ args << "--#{opt.to_s.gsub(/_/, '-')}" + else + val = options.delete(opt) +- args << TRANSFORM[opt] + val.to_s ++ args << "--#{opt.to_s.gsub(/_/, '-')}=#{val}" + end + end + end +diff --git a/lib/grit/repo.rb b/lib/grit/repo.rb +index 624991d07e240ae66ff2a0dc55e2f2b5e262c75b..63bf03b839374c96a3d42a07d56681a797f52a71 100644 +--- a/lib/grit/repo.rb ++++ b/lib/grit/repo.rb +@@ -93,6 +93,17 @@ module Grit + def blob(id) + Blob.create(self, :id => id) + end ++ ++ # The commit log for a treeish ++ # ++ # Returns GitPython.Commit[] ++ def log(commit = 'master', path = nil, options = {}) ++ default_options = {:pretty => "raw"} ++ actual_options = default_options.merge(options) ++ arg = path ? "#{commit} -- #{path}" : commit ++ commits = self.git.log(actual_options, arg) ++ Commit.list_from_string(self, commits) ++ end + + # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s) + # +a+ is the base commit +@@ -121,4 +132,4 @@ module Grit + end + end # Repo + +-end # Grit +\ No newline at end of file ++end # Grit +diff --git a/test/test_actor.rb b/test/test_actor.rb +new file mode 100644 +index 0000000000000000000000000000000000000000..08391f12336831d048122c8d13bc8404f27e6b91 +--- /dev/null ++++ b/test/test_actor.rb +@@ -0,0 +1,28 @@ ++require File.dirname(__FILE__) + '/helper' ++ ++class TestActor < Test::Unit::TestCase ++ def setup ++ ++ end ++ ++ # from_string ++ ++ def test_from_string_should_separate_name_and_email ++ a = Actor.from_string("Tom Werner ") ++ assert_equal "Tom Werner", a.name ++ assert_equal "tom@example.com", a.email ++ end ++ ++ def test_from_string_should_handle_just_name ++ a = Actor.from_string("Tom Werner") ++ assert_equal "Tom Werner", a.name ++ assert_equal nil, a.email ++ end ++ ++ # inspect ++ ++ def test_inspect ++ a = Actor.from_string("Tom Werner ") ++ assert_equal %Q{#">}, a.inspect ++ end ++end +\ No newline at end of file +diff --git a/test/test_blob.rb b/test/test_blob.rb +index 6fa087d785661843034d03c7e0b917a8a80d5d8c..9ef84cc14266141b070771706b8aeebc3dfbef82 100644 +--- a/test/test_blob.rb ++++ b/test/test_blob.rb +@@ -40,9 +40,11 @@ class TestBlob < Test::Unit::TestCase + c = b.first.first + c.expects(:__bake__).times(0) + assert_equal '634396b2f541a9f2d58b00be1a07f0c358b999b3', c.id +- assert_equal 'Tom Preston-Werner', c.author ++ assert_equal 'Tom Preston-Werner', c.author.name ++ assert_equal 'tom@mojombo.com', c.author.email + assert_equal Time.at(1191997100), c.authored_date +- assert_equal 'Tom Preston-Werner', c.committer ++ assert_equal 'Tom Preston-Werner', c.committer.name ++ assert_equal 'tom@mojombo.com', c.committer.email + assert_equal Time.at(1191997100), c.committed_date + assert_equal 'initial grit setup', c.message + # c.expects(:__bake__).times(1) +diff --git a/test/test_commit.rb b/test/test_commit.rb +index 3bd6af75deda05725900eb7fd06e8107df14c655..0936c90e5b29ede2b5214d6dc26d256a8c6646f4 100644 +--- a/test/test_commit.rb ++++ b/test/test_commit.rb +@@ -10,9 +10,28 @@ class TestCommit < Test::Unit::TestCase + def test_bake + Git.any_instance.expects(:rev_list).returns(fixture('rev_list_single')) + @c = Commit.create(@r, :id => '4c8124ffcf4039d292442eeccabdeca5af5c5017') +- @c.author # cause bake-age ++ @c.author # bake + +- assert_equal "Tom Preston-Werner ", @c.author ++ assert_equal "Tom Preston-Werner", @c.author.name ++ assert_equal "tom@mojombo.com", @c.author.email ++ end ++ ++ # diff ++ ++ def test_diff ++ Git.any_instance.expects(:diff).returns(fixture('diff_p')) ++ diffs = Commit.diff(@r, 'master') ++ ++ assert_equal 15, diffs.size ++ ++ assert_equal '.gitignore', diffs.first.a_path ++ assert_equal '.gitignore', diffs.first.b_path ++ assert_equal '4ebc8ae', diffs.first.a_commit ++ assert_equal '2dd0253', diffs.first.b_commit ++ assert_equal '100644', diffs.first.mode ++ assert_equal false, diffs.first.new_file ++ assert_equal false, diffs.first.deleted_file ++ assert_equal "--- a/.gitignore\n+++ b/.gitignore\n@@ -1 +1,2 @@\n coverage\n+pkg", diffs.first.diff + end + + # to_s +diff --git a/test/test_git.rb b/test/test_git.rb +index e615a035d096b6cbc984e2f4213c06d0ac785321..72a18ec424f078f6daee75dbc62265c02ba7a892 100644 +--- a/test/test_git.rb ++++ b/test/test_git.rb +@@ -10,6 +10,12 @@ class TestGit < Test::Unit::TestCase + end + + def test_transform_options ++ assert_equal ["-s"], @git.transform_options({:s => true}) ++ assert_equal ["-s 5"], @git.transform_options({:s => 5}) ++ ++ assert_equal ["--max-count"], @git.transform_options({:max_count => true}) + assert_equal ["--max-count=5"], @git.transform_options({:max_count => 5}) ++ ++ assert_equal ["-t", "-s"], @git.transform_options({:s => true, :t => true}) + end + end +\ No newline at end of file +diff --git a/test/test_repo.rb b/test/test_repo.rb +index d53476a51e3286be270c7b515ec1d65e5c1716e0..114a4464fa248550be10cc4abe0735d6025b5fca 100644 +--- a/test/test_repo.rb ++++ b/test/test_repo.rb +@@ -59,9 +59,11 @@ class TestRepo < Test::Unit::TestCase + assert_equal '4c8124ffcf4039d292442eeccabdeca5af5c5017', c.id + assert_equal ["634396b2f541a9f2d58b00be1a07f0c358b999b3"], c.parents.map { |p| p.id } + assert_equal "672eca9b7f9e09c22dcb128c283e8c3c8d7697a4", c.tree.id +- assert_equal "Tom Preston-Werner ", c.author ++ assert_equal "Tom Preston-Werner", c.author.name ++ assert_equal "tom@mojombo.com", c.author.email + assert_equal Time.at(1191999972), c.authored_date +- assert_equal "Tom Preston-Werner ", c.committer ++ assert_equal "Tom Preston-Werner", c.committer.name ++ assert_equal "tom@mojombo.com", c.committer.email + assert_equal Time.at(1191999972), c.committed_date + assert_equal "implement Grit#heads", c.message + +@@ -125,4 +127,18 @@ class TestRepo < Test::Unit::TestCase + def test_inspect + assert_equal %Q{#}, @r.inspect + end +-end +\ No newline at end of file ++ ++ # log ++ ++ def test_log ++ Git.any_instance.expects(:log).times(2).with({:pretty => 'raw'}, 'master').returns(fixture('rev_list')) ++ ++ assert_equal '4c8124ffcf4039d292442eeccabdeca5af5c5017', @r.log.first.id ++ assert_equal 'ab25fd8483882c3bda8a458ad2965d2248654335', @r.log.last.id ++ end ++ ++ def test_log_with_path_and_options ++ Git.any_instance.expects(:log).with({:pretty => 'raw', :max_count => 1}, 'master -- file.rb').returns(fixture('rev_list')) ++ @r.log('master', 'file.rb', :max_count => 1) ++ end ++end diff --git a/tests/fixtures/for_each_ref b/tests/fixtures/for_each_ref new file mode 100644 index 0000000000000000000000000000000000000000..e56f5262621ebe2a5a903dde14e149845c963930 GIT binary patch literal 58 zcwP<1!4be92n4`%B#N3`IS^s|67?vU7ahVYw=dIz{_Zl5~*dO K+v(*~0LmL58WA%9 literal 0 HcwPel00001 diff --git a/tests/fixtures/for_each_ref_tags b/tests/fixtures/for_each_ref_tags new file mode 100644 index 0000000000000000000000000000000000000000..c4df85c6216036926df44a108f9689094f095533 GIT binary patch literal 58 zcwQ??O)J(fNlY)+FEh|H*E3`=Gd3}{G)po{Gc_?xv`jNfF||lCFi1)@Of)c0Ge|ZD LaxE<_lZ?3l>0b}U literal 0 HcwPel00001 diff --git a/tests/fixtures/ls_tree_a b/tests/fixtures/ls_tree_a new file mode 100644 index 0000000..69b76f4 --- /dev/null +++ b/tests/fixtures/ls_tree_a @@ -0,0 +1,7 @@ +100644 blob 81d2c27608b352814cbe979a6acd678d30219678 History.txt +100644 blob 641972d82c6d1b51122274ae8f6a0ecdfb56ee22 Manifest.txt +100644 blob 8b1e02c0fb554eed2ce2ef737a68bb369d7527df README.txt +100644 blob 735d7338b7cb208563aa282f0376c5c4049453a7 Rakefile +040000 tree c3d07b0083f01a6e1ac969a0f32b8d06f20c62e5 bin +040000 tree aa06ba24b4e3f463b3c4a85469d0fb9e5b421cf8 lib +040000 tree 650fa3f0c17f1edb4ae53d8dcca4ac59d86e6c44 test diff --git a/tests/fixtures/ls_tree_b b/tests/fixtures/ls_tree_b new file mode 100644 index 0000000..329aff3 --- /dev/null +++ b/tests/fixtures/ls_tree_b @@ -0,0 +1,2 @@ +100644 blob aa94e396335d2957ca92606f909e53e7beaf3fbb grit.rb +040000 tree 34868e6e7384cb5ee51c543a8187fdff2675b5a7 grit diff --git a/tests/fixtures/ls_tree_commit b/tests/fixtures/ls_tree_commit new file mode 100644 index 0000000..d97aca0 --- /dev/null +++ b/tests/fixtures/ls_tree_commit @@ -0,0 +1,3 @@ +040000 tree 2afb47bcedf21663580d5e6d2f406f08f3f65f19 foo +160000 commit d35b34c6e931b9da8f6941007a92c9c9a9b0141a bar +040000 tree f623ee576a09ca491c4a27e48c0dfe04be5f4a2e baz diff --git a/tests/fixtures/rev_list b/tests/fixtures/rev_list new file mode 100644 index 0000000..95a1ebf --- /dev/null +++ b/tests/fixtures/rev_list @@ -0,0 +1,24 @@ +commit 4c8124ffcf4039d292442eeccabdeca5af5c5017 +tree 672eca9b7f9e09c22dcb128c283e8c3c8d7697a4 +parent 634396b2f541a9f2d58b00be1a07f0c358b999b3 +author Tom Preston-Werner 1191999972 -0700 +committer Tom Preston-Werner 1191999972 -0700 + + implement Grit#heads + +commit 634396b2f541a9f2d58b00be1a07f0c358b999b3 +tree b35b4bf642d667fdd613eebcfe4e17efd420fb8a +author Tom Preston-Werner 1191997100 -0700 +committer Tom Preston-Werner 1191997100 -0700 + + initial grit setup + +commit ab25fd8483882c3bda8a458ad2965d2248654335 +tree c20b5ec543bde1e43a931449b196052c06ed8acc +parent 6e64c55896aabb9a7d8e9f8f296f426d21a78c2c +parent 7f874954efb9ba35210445be456c74e037ba6af2 +author Tom Preston-Werner 1182645538 -0700 +committer Tom Preston-Werner 1182645538 -0700 + + Merge branch 'site' + Some other stuff diff --git a/tests/fixtures/rev_list_commit_diffs b/tests/fixtures/rev_list_commit_diffs new file mode 100644 index 0000000..20397e2 --- /dev/null +++ b/tests/fixtures/rev_list_commit_diffs @@ -0,0 +1,8 @@ +commit 91169e1f5fa4de2eaea3f176461f5dc784796769 +tree 802ed53edbf6f02ad664af3f7e5900f514024b2f +parent 038af8c329ef7c1bae4568b98bd5c58510465493 +author Tom Preston-Werner 1193200199 -0700 +committer Tom Preston-Werner 1193200199 -0700 + + fix some initialization warnings + diff --git a/tests/fixtures/rev_list_commit_idabbrev b/tests/fixtures/rev_list_commit_idabbrev new file mode 100644 index 0000000..9385ba7 --- /dev/null +++ b/tests/fixtures/rev_list_commit_idabbrev @@ -0,0 +1,8 @@ +commit 80f136f500dfdb8c3e8abf4ae716f875f0a1b57f +tree 3fffd0fce0655433c945e6bdc5e9f338b087b211 +parent 44f82e5ac93ba322161019dce44b78c5bd1fdce2 +author tom 1195608462 -0800 +committer tom 1195608462 -0800 + + fix tests on other machines + diff --git a/tests/fixtures/rev_list_commit_stats b/tests/fixtures/rev_list_commit_stats new file mode 100644 index 0000000..60aa8cf --- /dev/null +++ b/tests/fixtures/rev_list_commit_stats @@ -0,0 +1,7 @@ +commit 634396b2f541a9f2d58b00be1a07f0c358b999b3 +tree b35b4bf642d667fdd613eebcfe4e17efd420fb8a +author Tom Preston-Werner 1191997100 -0700 +committer Tom Preston-Werner 1191997100 -0700 + + initial grit setup + diff --git a/tests/fixtures/rev_list_count b/tests/fixtures/rev_list_count new file mode 100644 index 0000000..a802c13 --- /dev/null +++ b/tests/fixtures/rev_list_count @@ -0,0 +1,655 @@ +72223ed47d7792924083f1966e550694a0259d36 +f7cd338ee316482c478805aa8b636a33df3e4299 +994566139b90fffdc449c3f1104f42626e90f89f +e34590b7a2d186b3bb9a1170d02d52b36c791c78 +8977833d74f8681aa0d9a5e84b0dd3d81519774d +6f5561530cb3a94e4c86454e84732197325be172 +ee419e04a961543444be6db66aef52e6e37936d6 +d845de9d438e1a249a0c2fcb778e8ea3b7e06cef +0bba4a6c10060405a94d52533af2f9bdacd4f29c +77711c0722964ead965e0ba2ee9ed4a03cb3d292 +501d23cac6dd911511f15d091ee031a15b90ebde +07c9bd0abcd47cf9ca68af5d2403e28de33154f1 +103ca320fc8bd48fed16e074df6ace6562bed4b5 +55544624fb9be54a3b9f9e2ec85ef59e08bd0376 +e5c8246dec64eccad0c095c67f5a8bbea7f11aca +1b54d9f82ee6f3f2129294c85fad910178bef185 +36062a1634fb25de2c4b8f6b406ae3643805baf5 +0896bb9b8d2217163e78b5f1f75022a330d9ddc8 +6646dfce607b043ab7bbe36e51321422673b7c56 +f0bad592abc255fabe6c6d6c62b604b3de5cdce2 +5705e15c71f9e10ca617c0a234b37609cfb74d23 +b006d8b73912eb028355c49e7bfe53a29f97ce7c +b21eb6173dbe07cac63f4571e353188dde46f049 +a3256f3150ccec73c50b61b85d54e30e39a65848 +c5a32e937166d65757f3dd4c1b6fd4f5ecc10970 +1e90e2c654aab0d81088f615c090d6d46f03ca4c +924e7685fcd62d83aac19480837e4edd9c4bae5e +489e1468aea658a333481132957069708127c69f +970b6b13726889f6883e4347e34d8f9d66deb7c9 +df74c45e8fdb4d29a7de265ac08d0bff76b78a83 +936aa0b946155b2d47528073433fc08b17a4c7cc +3b6a5e8f12b6269a0a3e0eaeede81abfb3fc4896 +8e0f306dae96d78aa1ea9a08e82647fd95fc1a74 +5eb099e5e274be44c0fd27ce8928d2dc8483dab7 +050fbed693d4806ac6c03460103777b2a4befcf8 +c5d4b6dac74e323d474fa8878a7ea0c233d57019 +8e5daf911943d5ef025025c137fcf97164467141 +bcdf7c2421302b15f4ee4ebbdeae7b644a4518e7 +e2874a42835cbb2fe8856a398f5c4b49a9cd8d30 +f50ea97159e4ae7132e057fbf5ea1e75ec961282 +5dbd614c20e9473240082239894d99c24de42282 +0490e1ac1ffafcb9117029286b224ab39671a015 +ad3620d47f0ea96f24904227d3c7a7f9548c34dd +fd37e7191ae3d312ced0877a1492cd2ea4578275 +b7f8cc51c9056a469006b5601a4993b67c07e099 +1d849af5083073b8864487938a9a2a8e21d71529 +26d0bb4c9ee3d8591fe291c86f495b2d1900bf9b +7a25e3056a7225c1ff8542c2c2c1cf6f3a8e13d4 +d0e0de0b13b9c81d2bcf9d54eecdb20591fd6d2f +0bf82343ade1e07c0aebd14ee66df688a4cc0e87 +d81de0fb6a19342a90cdba9a664659da66296162 +9105667175797bbadea80879e98a5cf849a40065 +12f5af2a169c658cfae1677ceafd527d3775873f +00ae94689600b5949bd0fcf68205f31f95a36aa4 +8f5d34224e4620c51c16c01578786e76567d025d +3385eb31651c84384b4c7e93d82bc5b592edf9fb +eda9179b9af0275d62c4074480e7a0103d356435 +982c2d1e55165fddb4f4c69065e2c4ac39542c84 +7117495ef012719769582939ea59a5533077fc8f +b7dae75dab5b59a320b8df8a67060d238fed3a8e +37c684e1a46599fe4d34d1601875685a70b1b879 +0a694fa0cb044a31bb844564776b490c151ac317 +e77c6b459f01ce078aa59183189226a6d48fdf38 +dd0c0eaefdebc38610bb1986e51324a0392e829a +d8bc2414e9504172da640f29db1b2d29d834a94b +a9f1119667dd0f5aa9413dec23965a747d1dac05 +f52775f6bc21d999382f4b9b38b108b814114ea1 +e82c77ac679887140867e471a9f47fd3b2d95039 +2db3fff5673bbd4bfcc8139d8398727d243c9efd +c1805c000c6233a20ac0824cad21c1fe40f93100 +83f7807585cf70018a9a06179af9d89d4a8204b2 +730c326beb29cc6d2624915b125633792a40ca36 +bea422b653d74dd03ec068dcce938169149aa822 +586a57666618299464519c450033eecc3ce89664 +82fba8cf4796f2adbec5ad336bd750ad60a075fd +9d9b899f836a199fe854075e99091d1ef083de24 +4670357c662596aa2c2922d826de84abd9f877ea +9b562567430544c74009ea4a6173f44ddb4a44e5 +013d51fbb5f3a60bc748449b1ab73158da9a3203 +3fe67cb90fca9ea76292deb793cb480f4eb5e8d6 +91c80e489fee08e71a79bfbea79fcc28e1aa27f2 +dd9104095bdb08fe399af46d91b334e760986ddd +a9198904586546a038f855bc6fc0e7cc413722fb +574a7bad1017d9ed466474881e1f068f892207f4 +f95acec9297b7816284d8b24e984cd5c82104c89 +3907dac65a125b7759172a8eae959b0e70220299 +e5b44576eb2182b16c7b6770fab5977eedbc03c6 +9f4aad9833d0f9a609dd2556e7db784ba813d8fa +579309c96651a1fed75fdd18f80019db8e6624ec +5e1a9a48e6c96099d6a0c3aff1e31c9be16b7b8d +cae4b811038f4e0dd4a8e68122c3db955e10ae81 +fccee1c818f5af5fce593de0949f5a8ecd35b443 +d4187d5a5f9ffe1f882c74f6ced7e0ba1c260ff2 +02ff197aa41d892e623dc668b0055806294bd6c0 +3f81af24214761a6ed77fd4dcd6e45a651dd8f84 +5cb08c5232a669a881606a6d8c4a4cd23aad6755 +5212b25869e0b9aff485af6f5371a159e89f8f07 +a778322bb60f8438a68112a73df78e05a97093ff +b55c30c3992a766628dfc4a7e22db4d8d9e46b5f +1d3e4a32e0407f16f641be316c745c1a48f16e2b +7f35ca3333944165e0ec82a3a95c185f67fba904 +ef6c5bbe2dffe76e4a9698df03b8ee08af702033 +aeb90405ed696c1efcb29d0664b43a52a2bf824f +e0b8bebd78172229666dfd425933f9bc21863928 +2a71a55154edf75ab51dcb4f2f7dc63592410e16 +a5d25352d326c77d083a2e089c2d80b4ea923624 +a3fbc38b9f1b86bb5f5e6540048083fc1dc6062b +bbe67e1bdadf4aff186769145a40727f78e39e01 +a02a58c6c6d04001873ba91ac3dc902275879d0f +eb5281d4f40e18b0e21d275ee5c5964bbbcc855c +19e9939a098b9cb93c8c1d0d069d46861afb507a +7a72471f9a4587cc4a7d37da0d26122b0eadaddc +c6a043eb057cd544130b77bf99f39b7738e0a204 +723b6223726c6772e034d9f4ba5c710e66a1991f +25b4ff1a26dd3694a98c1ef2eba04a5a500c0b28 +7c571ac2c35a7e1f399651242e904596c93beeb0 +0c90015733521720688bfcb59ad2a3978b2fbbc3 +d6b99183122a97a590e4e54f4617b58f13b90df8 +6b663271af39d69082422866e61ff7801c2b3fa7 +2e9e6ab7651e4c215110eb381678e0ea2bc0f7d8 +967b91e045661c9b6d2a5f011ec152da391db7ec +7fda8d15bdb3d3d61fce49413153a216849721f9 +f7d7e83ee1cec103a768ddc9f68b6d5075849894 +925953da542a9c21a3fde1ab0891175fb6212a12 +ea2f54455427506583437391cbaf470a1ef4edeb +f0bdb2cdddefb3a391ec2e3fa9b78ed06d7c874a +8d289566fa92a96a83ff3c2e24c1f3d12b1718ed +7fb102615532952c6833e87389668831b37a13d6 +7f7bbe8473158ab606a89ad66d607ffd0e5ba1f7 +a98ea5a00d19406f3e644448039f13db496cefd5 +39f03072d9d84d622ae974b09dd11cf7a2515a7c +e2050a1c488fff4b114614d7f75096dd0a149f5b +d2851f113530fbe211b3e948b6181152d30d1fa3 +1eef0fe740f6db35a91e790fe77d4ba1c9065e99 +9608403b012908cc58223db44962553704cab8af +4911a005ea6b55f34f8b0f504a6a0934c0df896a +a4400fb8e7d0f1261634dbb89588da86b8b6c93f +f310729583f6733ee60f534a9732b7a3a9e414d4 +49e78793487ce4d8d7e624b5245fca8a9cc1ba66 +2f2501ce5d28e5ada6018504ee8dcecbbee70428 +f1e127253e1eb07b537b221e9cc96beb16333790 +8bf1684ca9b5a37d91671dd0d63d0ac59bea987a +24838a6042a134b11fe945bbaa5ab1b2b3fc6eb0 +f53c57af21fded3735fd479b3785fcf7adf80268 +aa8d0a63d61d13524b1395972563b516cd263f05 +16803d64332412a66121ef3fd10cd0d88598d3be +5f2715ed4d9416fa4940c2cd29b5ca18b6a79b8c +851ede1f8dceef7d681f35e4769e5693160c0a04 +5264588c6c20c38d54394059eef0a854683aa3fc +111800d8e66ff86f0757df7eb6533fc62040a22f +b04de89d31003e468c191cd08dd2a4629d99c38e +6aef629094e9ee6b4fac2431897844c4dddf2f57 +d1168c999fdae7d1eaac8c163b2b1190afb1815c +6afc3257929528d9f4de964e8828822d2fa2c93a +436f30ce1b562efe4f34696def45b0145eb98304 +9afbf904be0e6154f6c424377ad596e86ea38807 +a3cf657305d9283525711e867e03684a2e4b39cc +5813b4d04b25c385359af4437027b4fe763cd2ba +0fa594594c97a0d3579312f4ec073304c1436106 +cb7b36c28adb38b1e597fa3f3b5c24c988a25b0e +5b0c867cbda81ce34df1b5fb67557b556ea24e9e +44090e9c550c7c5ded01dc2a681a7c934ba901b6 +9ccc89b61736c4a9c02faaa679e97a9ec063dd29 +7828d6d18115b0720888a45e3b547b697910c59e +618497e48e46fdc00dee67c07cd0f40860e819f9 +69a14ed4f36d880e8322a530d8c5bfd9888a8c13 +0a0cd655e40903abff4840c23b57628fb1a88122 +cb262098646f47e1d80a89662f1480c216bfd81b +d60e59fce6f698a8bb97e2b4a935c069584621b1 +ca77ba0d6d301cee1d45edb24742dc5cdabd4b83 +17b598510967922690f5181903f20ddae5758e86 +30ad3d9f3164966afb2974640f772387fb796b7d +48964c5dcc94234dea1737d7fa23220f9eab0fb7 +0fe241f4db12f455c2f5976c6bf6497cc485f503 +04953aca41bd372d990da7f68cc795f4a8b78d94 +2dc9a061595a291d8c53168c42da8d14da52d610 +68b15d34903038e3f2e327f03f0486b2d38784bc +30ceaaf39b10f9f9c7b4362505144d1650035a40 +e75891a5760f6a51f54a33b671951c16fbce1558 +b2a35989ad3392f26e070b158f89d1d8b75327f2 +8468830b8b37f7c1cdda926a966c0aba2893a7c0 +6a6112e8cde1bafebfa12e4c863dab5834c38e12 +eafcd2ffc25d17fce41eff2afd5c4521730a23ab +f7eda0752f45c3a4eb13e075b24b86d7e7dd5016 +b634d0d48d0a113bc060a47972b10c9353621428 +49f95235a174f0a056e44bb5c704fea4ab345353 +6eec70a31a6376ffd7d6b143be0968a195ad59d6 +7c9ae1a71aa39efe28a678c18c8a03d759deabed +a19fd6f13c16720dc38a1f572eebf960022015ad +87052ac2cbaec195094e1d1a2bad4ac480bd111e +2cde1b0e69f97a8a67bb47d729c53af3ba8e5700 +91a06d1a4efb6376959c3b444a536fe6b4fd4d6b +07f73b465b6c509b337c2776fe7a73b56ee178ec +15218bab55236d62fb8b911c2ae1ee05dde1ee60 +900180ff2aa70e7d857403412753df6384553d26 +a9c43cbeb0542cf6538fe8025edc8863d2526c68 +d7d8f0c9b7d56f554d5a5cf5759f30cc3d36771c +d703e5d9ac82b8601b8f4bfe402567b5ce3ebbf9 +3905a12ad511ffe157cb893e7769f79335e64181 +73a933454b09ee48ffc873b0ee767e0229f2d609 +c2c91403aa9d95efa09843bffe83ace4d52d3446 +c90f480010097efa3fb7046abe7fac3c9b8b3975 +13e888d5624e8087ea63638de7f4375f5c13ac55 +19344e551c8c5e56e91de14253b4f08ca05f9e69 +b1b8f098bb1e2f0f08cf82805d7bd29d2931f63e +3a3e025bbb2f3e49facac00e270d8afa5d31b962 +195116405307f7bd9575f9621fd93344304341d1 +31252094210748399f7e43e7b6149190990f4e8c +357e549bf43126e371a1f85c393d2560597cb32d +df1f8ab23f915420e9c48744decbc45375f180d3 +f96c2eedf6800b8fc31032a02caf0d2b469ba9ec +73405f0505813ec1bd25f03f2825315f3520bcca +7e2447536c35ae67e3737a031fa1ac57026475a0 +970d4c4854dbcc3b0bf9b16edf1d47eabf3be242 +3c73519e6b54d3559555ffac40072957041f62d4 +46d461676fc1fb16fd7dee027065441d9a8b87d5 +f11f64bb55240dcc1767a1ec823aecd3531f1d20 +038e91a424078c5d81cba6c820cd981f0be6086b +157d6e98ba894cba5582caeb15b429ca0dcbf2d9 +2c768cf9d1bdb6d3d84f662a847966b69c898f59 +4fd0f29459ec3ea65625b943b147df85e5826cd9 +c7e90c64e580ce5f95147eb4e117b56b5cda254b +cd4f2496b274b0d55b7c48388c2ec0365d9bc266 +68b5e288a29ebbcd65e6d0a8eed47702ee4e689c +22abd4a7ed7b061364e002f1fe08857850a309ad +4c3b38be6fda8ba32fe6f29226539e03bd0c55ce +355e946ca8b8a5e4c17317446b12fc374399810a +1fc5c0122fffdada1630febc1f2e42952cdc7e2e +8db042e1faef7be24d62b9287fd3b9add7a1b4cb +1cbea023ce354939ae9082a62810b46f38ab1cd8 +f5edf5b99d1bae09314b9680e58766a4e3c1bbc0 +58a5ef79958b58736603f47cf211494fe5819601 +8f2038bec169ae6d62885f522202d8171e3f5f5c +5488e29e68684648b4d733e90c6e3188d3bd5bad +84c88e813117db46c6ac68b16a7739018eb99e24 +789c3655197585ba8771ce68c0117cbdd41ea390 +0510404a3c0d337763e90e5315548043bac65b06 +2a665d7c6cab59ea8e3bb7fc65249ee947e51fec +d53423de534d3b5e68a7644d4218d835a8bfe6ce +73f2a3f332f23579a29e090f70825dcf84dcdbac +f79ae7f27e750c97c139cdbdd7c3223b39ed1a70 +c84a75f7a4b274c5c133b1df3648a5a24ed9f687 +cdf8e5a49192b81bcd39d9f4e39aa4812b58b80c +1180461f564674e373222fec3b4fe8c2861ea6a6 +150d93bd910597b85500e74b97b96e7eb4bce2f6 +ec3b819ffe3392bf193483fea94d4404c88966b1 +729fc8ffd38c02a9576640b56376c36b49edf52e +2ee31128fbd86244d547e3ff66b802dda699210f +f87f28c563ad602cba605e84bee95693b77b8840 +9e92c5fb59af58867acf5512e95138fc368f7dae +76b1489042e1bb45909832f7064f9a5437b68b18 +66f5d86face564c095b3c95848f070f50fe4688a +f9b2b3ec52b88dbd68b2f2c6b246bf07f632b40c +14f689f05c4fae52ac8bb95762ff43b9f7f4e567 +5ca84af5f7a3f4533b353c43a332b552cb2fc5e4 +c5f33e9eb55201c41691e14fff0d45e32c989a42 +9f83cf471949164a6352cb9e3a201b8bb317b89a +5532c7b06a2f02e9cafd6673d5099798c4144690 +0d28c20ab4f03b5d8579132048c060affc36c466 +cddce1dfd9d4d7f1fc49003aa211f018bf8fad2a +169617e3672bac804a271c0aaff9cdbac7b4b45e +fdebb28d6ae398ccba88f3e2e63ef6d7f10f62f4 +0651bfe384a8d5865d6cab808ca0ce803af93878 +de89eb007459fd5400cd344dddf240fc33fd0b65 +c6a14beb887170d8c901e522f2f4dce3bf0b9ed8 +13dd0647b3ee39fae1140f8eff2b15d7f63ee546 +9f89105c1462f2a80e620ada1b95c3d08a121c3e +1ed6496751273cf472538779266dcc3dc9797192 +4e8dffa66fc7be8f864cb48cf26abcef4cc32379 +5543fce145ff28a1c424b730b376fd4e3cfa0956 +bd951a4a8574baac21b7e1f3a09d1265aa51850a +3fd1c12fa880ee45b0ff7b794238a8894306a790 +830ec14bc9edbd2c6522ff46ed0acfe477e7e32a +e68c3109a709e2b732d0945f860495de161754a2 +1e0f4fda735167ff6d27c76a67b8b4a4ab31aaf6 +c6c40dd0ff4420708c2e0f5a0e0dadde93eae336 +baf0c18ac24acb9ac3d1a7c0030ad5675eeb64d7 +8d30906e9f2f68024eb716be9f482de5cec5b302 +ec9fce551828795e1dace26a11f57f9aaf1af37a +28fb918d7e9840a7118b7aa0b6151b496f1fc1f0 +b9e58c5b98f7c89054ed5c0a0226066ba9d93c8d +0c5db457cdd3852182ce70b96cb376337b8ad7ad +36a48168274cbb6f31c35777a74ee16c06e1a853 +07ef3ad40bb01bc7798b241c88fda2eaba7aad19 +02aa9f2ba871e9639891986a97618e0917955fc8 +5f776d3c74ad532f36ab75a71bcbece6a62c831d +f31ea9eeea91106481e1b2d30026b601555b6699 +c3d7f6bac18fbf8041662fbdda4f04e3f3b25e3a +6280c4bcf1195c011d7a7abb5bf689df11d66419 +45fc4ef9adaf514bbe21f496cdea8869a147c81b +fa1160786e34c057cf1212efd59a72c3931eb2a3 +09b285cc7d7c8768917c7d4e5513e3e73d752b68 +a8da5db6094c887f1087162c5ddfddf601560523 +b6134a31d236c376193e969a2df65c8427d280a0 +793e0d19fef38f8a151622257b13edf6786e469e +e40e6a17b4df5be46a2cafaa3fca5f4c3cec5001 +4d82e160cb874da6dbddc27af7dfd1036772b8f6 +745ee8e3e74dc0674dd8018999707f025a9634f5 +f507baf298549096f08dc33de22f7301e9799814 +bd7ebd663da867692f2316b94db73c42c0f9a5d1 +697f07726d209cac519b528018559f8957c56069 +2297b5172c0c1c83f2d78fc726fac0803be6eeb9 +91e3543f82039a446c5be8293d5a79ec767d1444 +e997169214440256b5b759f6e7e255a302838c97 +77d174ae14afbc6e212eb7d957b11a231a036d96 +3e81ee29892006f16d5f1f26d9d6b341a8958fb1 +59957e1d84f8fe8117d9697154c3951ba2959480 +96c6fa03962edb98a9b6aa7793be4ff54e79bfd5 +068a293fd6b4fcf216fb84ca982699095613af37 +b3b1804ffad1b7d274bc3f8f5aa11b15049ac030 +63e394c13a50de0d9f6cef55a8c91830200c3dac +e7ed33eba96d590bbc7179fd26db707c910d1dc5 +6b2084340a988f4123e71c6e30817806ec4cf3a3 +da721d3f48f821faa90d1a4778d77b03fd3dcdeb +a433cb8d56a4fcd50bfc74b0204c916e08c9d5e6 +067fae6fd778d5b1d6b6436aedc0d25db58334d1 +e34c192a5aef80c7e83c78c2372602830671ca5a +861a44dc56a983262caebf909be96c62254930cf +417ed493a824863e30922deda64b9729b1c6d6e7 +2df6a0d803ac21f0d20ae9fce0a970b35b3663ec +44bedcfc59292d3ff6b36759b324812fcb779b2f +c620f7e60c8ce4ddee8fc1072b2a161fee862545 +82ce5a39b422aec7572d9a773f85be8eaecb1618 +dc0ea6defad83a0569896a9c23f11f5052a48107 +e1c15f1da71a3aabdd43a8ad669d2a755f315c77 +c78ee1aaa5c499019948c9a3dfca3aaa2f897860 +e66d0d34c541c6588da3ae06c6aff7e7c9ef5745 +c24d513d46b3db5b4c53b36b7e43ce5fdfd5a2e5 +a75d0a4bf6d2e1a9c4026586cb707f254691eb3b +41e98ebf4526e78d78ab16182b503f237e77fbd7 +2182dce8c27c33f9452e7c910f59750d1e58b1e3 +6b7aa9fdbdd0160ec29b6b3b591169c627fd0f01 +b39470063e41ee5773f47de325a845666d0721ce +c7941bdc8822ae1842d2a2f42924f31d2d37e864 +fad6e836009429e88c788ab7e7a679d422d8cae1 +a478917ebcf70c5dd6f56c7cd139832108696189 +4101b1ae3b17e229c1a80d9c302b74d215d98f04 +b051ec4e69a99e26d6a6e5d7a393014f841eed6a +5298ce551a104605b7d5d9872387f3eb704fe5e9 +b14a12bed26d53eaccd1a2c172ca4a38773e1d45 +4ae0790397d05a758013e0496ba2c2b23363361f +431f01bf3aea6f8baaf06669172561a3ec9e82db +12476263aa193c7e921ad4b183fc648bd73d2a1e +8e937050fb12a62e99b0cf685578213552774cc4 +b85a487787454f6dac84be59f905b8c929f0ee94 +dbb2116e0f03fe6d84d2158d67ddd02761938bda +57186ad57242ad0bcd737c4ca4ecb7c063979a95 +cdb4a295593cb3ad424b4ab86d74154d7bbb97bd +8e9e1ae0edb776f0a490005b838f8ef82b368be7 +73f8f21a69a03cdc2b1031bca214a6b84f4c867c +b913c6d878ac5cf570e6f8ba9b5ff022ed601a8b +b98879530fd51f328441d33c64c6c5f311097e15 +325b21b5370a0c179b40fd596b9daea00b3615c3 +6e722a5c5393dda24172de6f8e08138bcbfb10b8 +44b396caab82c97a6270eb7391d6f96502c9fcf9 +4e6ed6d22079b68551bbb83e5dd797517796a438 +b611fe79daa20893683475cc459dff98b2d4892b +017d40f9b23f4a4379c74ceeb89ce7b4bccb7460 +a31b0a7fc7190218136d6ff6ffb3ee6af3244135 +861bd42abb90a61ed757728e1fef7cee2d6aa081 +6e9ff586de744d166a9f6f213b609e7386692472 +a790ca7384982e872092766c036d6faa86bff71a +13485c50ca4dfd885d516154421bdb23cb034230 +c5471e696f3166942a245e77796bcddafe6a607c +600308daf62d0d651fcfc874110e7bd4f5de648a +bada607744ec7f37ba9d05c09bb8f41e7fc3d06a +d3b230b209fd7c3f4a39db965b239ab600fac1fe +6d730b7ae0b662b1f987101e8ccf9c1828554d69 +f0757668fcd3f8d1f2fe83ce9f0e2355b6be75f9 +40819d9a5631a184a17d38e36240d1171a6fc923 +8a6847ca68ec998df0543c4b5bd5c709c05d5f12 +d8eb0646ae1360b5b984ba7d99bc64e00dd67016 +761bf1cc1e2b86437e71c9a106fc9c341097c3bd +3b620d960d29fa7719f95cf945163b04e43d2dad +6be8590f72c2ef158202486e75f273d8598be6bc +d7f22a15d66139efd65bae28ba780b0bf8d1a914 +e1ebbf612cf9d49cb08d0e0770ac1678ce1436ab +4db9912f07ce63e4519053f52dbe521ec95c0fba +b9fd4f4760ef65934b5d38e8b7c0eb2f77822861 +0e0178ecfacd553526afd221734607971b6911f1 +8cd4823a8ac9f846930408ad1759da4496384f9d +e96cf22a972cc3185739ef1c1ce74a978ab71d11 +a9d63829aa54049801d37429b597eb04c9e1412d +2519d617e18fa35974e20d10414f1262013501bb +d02fc8d8483903871d9f65261b32c6acd2e4362d +569456505d5c97934344d4f989a08fdcdb522de9 +f56d4c60ffb8df8fc1516d32a0512def0b6f8296 +745e899452ec746d3ffbb7b082995b7939a85387 +8c11f9ca2433bf9381840696218c245ec700666c +bc2a868d1ba12b485a6eac460cefee67bd9ee899 +e628b072d054d982ecbbd7aa7fec628e0d9ee8d6 +e3390afd65e721dd8ef228f48fd4244228de2986 +35102507bd653296eaaa5e7d475405cc1feafbe3 +e2e5342f92148238391665fba101b1ca7dad5582 +621f4743f0165c6ca3f1571773867d2e0da67961 +5f558819695a49bbb92d5d1e07b9f12072874024 +eb45e9da84875e2d1325b78157d2f9e96374bdae +bc0ab7e4f643e779cf9554f03e567d4f4708bd4d +fd55e896d6df035cba49a20e26ed6ddd2d7b6024 +dcb9d95840c9a0514f8fd0a7b3b17cc228950c7e +0bedc3d7a01f9819171c0b664e16900d9965c3ae +94f6e372fd90e96cfd9a393a5952aa850485de66 +0b889a9cf37997c58a9f8979850da1f4bc84de9b +b70ec5facdea7fc681c2a10dfb14ca0d8fed6f1d +03e0192fb34134f25784a2b14791fbfdf69461bf +9266cc52df3725107edf513aea4a02c131aa153c +0820a412fdc9941567d86cba02793ca6a6378275 +f1a72254956f63393f6039a7d5da5fca943fcd2c +abeb9e16d924c1a87c5b525ed12c43031ef1cb2f +d5813fad322c97bc31d7dd37f838c7442aa68f35 +428b26fdca0ba98a3a01e89629bdb778dec9e8ab +19ff672db65a7ee25ee0d48baa3f9bbf2d145ecd +d1eb6283ece7d9c814b0d3d5223207b905d3d720 +9ba934b83a40d26ebc5e8d7304ff29c32541e82d +9b600cbf0209ad6079d00dd5d6a5270d858d5929 +0f22868d790bfab8a41894fc7eca161256ab6854 +fba092070b6e03432f6d47154f5ae4734e935a05 +11b1bf011fc24c2ca6dd8c81206c7338ac2b2915 +d93c82b17d7416e4c57ed036d6b75a323859d837 +27f762e8d3f1ed8bd0254800c121d0f16e914c2e +e252d9d270330072e9e5e91257e90f255e7e968d +e55c3c30785eb50b5dc36f9568e6b6ae39e6de11 +63491807090d814bd7ffccfe44cb05795830eb3b +8111dbaeb71c53132229c4064c34247746a3769e +8fe37ca0d79dd1f8132e9add06aa206d371964e3 +eb32fae4665b9f11ffd06a342e763b9d212e1353 +4e923698ee5566143fc6d32fdcc6fb46fcda2d23 +2e3910e29142382f9bbd1705ab9c605d1937a1ae +533cc5f884885f771d3f6df4164fbfa29bae0e6d +3fea0404fc58822cfc60d4f10ca404e3223f82a4 +733404a081eda804707c3dde1d6b8161e7a34b3d +d2be0ea2923344abed57aa21f13dc816d4537eda +7884465bb9da51c8b6e95a1cbc9888ed696ff68d +6e63e5a03bfbba52dc3b4f504e6bc41951f56707 +44a9d3ed75c44e817a6e4b56e30be06a15f453c9 +91e12aff0f988bf414e64b97a8c20b9699440309 +008119e510f6a7f8714e63d2ec33ca7cd7776ea2 +27822b01ad020374ff6169428649fd667abf7f8b +0c972fb8903c656cb7e750b1d5c1ea1f26bd8c50 +3d8f3e1fae697a905e87250aa5c0ae1f6c60ad66 +744421b6f1d3aa30c7558570da8aa1d52f11d39d +ac017796cd3a5558dd78f73ecb82a6b961d8a3ec +e11f534a2fd666ecd841f657faf0751d5fe02034 +eca5d275376911916c3e018c2d163cb8eb914263 +a3144ddce360b6ac1b55fc27d19a318be1f224c4 +84fc7d68bf3a309b3687da768f0dc206e647e653 +fd5132bf8e99230a9074ce9bb3d950cd26b3d25b +720ceb5e566d26803db85af3ef69fc4fa14d355e +e97f338a79e2248afd3a2b9077d8ac1c334cdf38 +0173ccf8d04014bcc4cc53df4d6574540f4231e4 +52da09b8812d96c14d3e57a77784d56e5749a8ae +5169648c7429788c777947e21527e121d35aebe9 +41c8c94cdd1c646296946a00dc72dff8fcb6556f +9b341f77b72b55674a030ad0209ac297e41c5570 +6aacd7b9b8fc571e930d18da63efc8be46e31bdc +9875e5d15c0750b6ee4c41b0e1321e1dc0bb7810 +fd60909d92b0e124957aa0783ea03471c73fd732 +2f299011d707ffd8502e5a597f38f0d25ab3099b +6c10423816abd3b0f327863c9b8fcf55cd6265bf +14cc60568455ac2210f00ccb238ae41ddb473fcb +74cf0e9a42bf241d3f76f25aaed46e4b6550d842 +9a0eacdab0398ced7d729f5c7a9b173eada2dcaf +3057f2e5ac8cd11cd018780c062da7c2bb11d2f7 +dab224a6b259d9d7e16af4cf7e2718af8ba4a74c +fe6dc165cde8c826a3935b536c8cfd1c10ba7d62 +7f3572bca7fd48b66649d761a054412b8369deba +2ea30dde468795a3ccb307343cd50eb7041f5ee3 +5d4099ededa31d823a355d4ef0e53bed6b833539 +69eb5257143b2de63c8c7471216ba6f025b6d7ef +e4c7387b32e314cca7e0ee2b1df197340272fad1 +01f14dd38700098d97f933008327c8456c75af34 +94040e25d5aacae0e55c3e9a91fe24d7daaaaaca +cd64f093886bf092b8d88c75ccd2e2f9118d3ba9 +ceb96f9512f80188fafc61ec8d8d61c93d51a5c2 +9a4e9bf98bd371cee2b69ef62a1189c24cd8baa4 +dd861f56b65404a625538978d50819924f384a60 +b2960c129e39d30f446d27e38f726975bef7b4f0 +8351c6b1293bb0cc4a2e1235995c16433c84c463 +008ba61116504d01558fe8afea0d5b3e90944b76 +cce20d2824a877ffed6a912e3f22d7db3d8e5043 +5e02e12edf58e1dfe37ed770fb32171e64993a81 +7966a56b3a3c9c9ac6db5b9355ba5e96558ea7b6 +5dea2f86730665894cf03f2b1fac98c1217a9fb4 +451a4d8118d2c9c746c687efceaacac799e67ad9 +059dfb5adcde569a19a9260c2ff85c7b47f8c516 +da7449db2898c567fcfb40c595c0c21536c901b8 +db97ce996b09b15049a9f818ce27a680e585bd11 +e1f95b9a8fe2394e1cfb41fe83f130bdb68fe6b4 +fc2c03e29a331cafc8b08abd5eade336904f40dc +385b11a95469f7477bdcf5b9c743982c4a866c65 +d7e31d19b9ed766048ccf9129723ebe36b4842dc +9c9af56fb29f510ef75221a39964c128448526bd +83e3c642af5648aaaa119cce34dfef6ef3c560bc +a831fc506ca30a11c9d9b33c9cb2c43f6f01a446 +62c5ebf183a0cc2332f04c1ee3323005a9878438 +6bb31edda343bbbc4410e2f780c432129e610b47 +846ef94e8af8f09340a740d11c93157c81079bc0 +47aec581139d8a3ab4f2969b481868c1485e2ac6 +e3f68d2cd84e15063c4f73c8420a444f9fb64a7a +3db1240470361a7314ea096f63c0fde74810caba +ae951371c666cc605ef69b5ca3f5f31d0cd30298 +8ec035e739f01aeaa09742a92154f02ab3dbfe93 +4737a65f7c1e125ba37ef35acbc6e99c4db2bed6 +7005d4cae81a16a5a860fcd3c259d6ec07597072 +d98807cb107ad2e9bf95138ee4bfb566bf75cb50 +1e8cbd548f12e1ec861f3aed5fa9f080cf2782c4 +25c2b2cad9cf873edc80747cd2df5874034282aa +676749cf8f76eadb469289b1d918cb5e485cd56b +8cba76ab8a5034ee21e95a99196f257b7e527b49 +0151aa85f5a178da21ddf7d5e81398fff87604dc +f881500552171b5a8a8c3ec7a2dc06e493a1ebbe +8d39edf2ae13ed33d0529164d4e172bd4d060d7a +b5c3f29c81e524e860e5f9ebefdc573f83fc600d +b686bc7a882e461987ffb7bf1a25bdc6f82ccdd3 +ebc1f42a059e7863adb57890562878f652922b56 +b30835cea58d0b827cb56aaf9e4d5f6e673a1bf1 +a2cf1028df49cbf53c57d0f599083fec59cc38b7 +6efa045dbdfb4272f075255411f54fe436c31b8a +0c3f085a4044e9231287c11e34504624b04ee7cf +b8e628fdc2a7627283e0601ebfe8e978e91dfc00 +d84e30103d59d6bace53223fc0d5787f03d7f028 +2e0e70d0466bde79d134a215a399b20c2a9d0981 +142de640101e2bee71fe2dc98e567d688c7e3aa7 +8b02a5e91092f7363443a1cf96933dc445f0ce51 +753c065260b1659c0d8d247b62f6b0fbe986c7b2 +1113b6978475c9941be9b140e8cd6bc267469657 +0a01d10b21c039484410c7898250afc4079db28d +b9bd23fa584a8f1900ada4addb96eeb750ef0a68 +5ecc9b675c4cc5c1bdcd8f84e1a52457ad30144d +d91b0a31122b251998915b4eb274350fd42a841e +a829cb9c850cc75546547aa95fa3ca6100ce16f7 +4b9bba5d1063d986be6463e4c5740eb18befc7b6 +ffb2f17926143e242efc18b32ee0c630b5447687 +3feb18fbff52f17a541abb1ebbb4894beec18d55 +4acbde9bdb24bd802ba5bb0ebe19d71c8d753240 +c9dba689c67ad7b16c8f6b1bf1bd382369fdec4e +ff956cafd71e4787e9ef7b64725142fe8838a65a +e2c090f1ca171b51d08e6ecbb74b27410bdfa7eb +73aa4812a2effb88bb64a42f93713a54a88e1ccb +8e0e0c69b0adb9a65098b18a7b96d6ed3a43940a +5dc8620cb17c3e606b635f8f95ecebdd66af04ee +18f8afd6fc87b3731145f61818f23b4b766da703 +0d2d0bd0680557dc28f4f7b23562495cdbb3afc0 +94da53667213590ad9767b335a9f2e51fe1e2c5d +c6cb97a42dcea5461a2931b097ddfd53b9cc5870 +62a3d5192232ed847f3c7810344c43607a361e68 +aa6992567e763a0b081e6bce753cc42bc287e9d3 +1d67358d33250d456040091d8b29083b1b47d9bb +65d399a4ac7dc36df20b8b2bc773bbc6fa67f43b +acf7ea014fd1b7eb351dc6946b199ad2cc98f845 +7e4dcbb7f0fc2b051e33b555c4fdc67796dbbab9 +a07916245a9c21f3874a7b8c898638ca3b65df42 +bb7368d9b07b02aecfbca6d01788a7327743ffed +60454c29275aac27c450323f0141d60ea8202842 +c4d0ff10c85ca4c12ddfda1830cee475408205d5 +a5da3671524fb761552a4eb5c1e27dd433f80fe4 +43142e711f392ae1bcdade749dbaa9dd98664228 +7aa0bdd118c78d8929e737392457d14f87d625ae +be921331245c4e04ef9f0ff7e359907e2d101cac +d6f654de1b8c27f84e34fbff12aadffb30342465 +fef2680b335ffd861021ceff2a2637f5a360f037 +79de53d3b87469e21d510ed6ddb33d809c05a3f6 +475b10017d25db725e73eef11ca789ad7dfcf4ac +d14f3734dc27ecccfdb4683cf7ef3334a5a70b3f +f0c394dd6a109b97ba4a9ab16cc71b789d9ee38b +a57cd5c8278e1fd6fae6f02947c13880be4f3b62 +83c6e4b636f3bf115955b6eeb3f91a5689e7f00b +b881752a8cc16f49ca605bf6a35af106e7e19c9a +8362e4bcc30e73460ae1b9731bc545fd2b12d8f0 +b01216229149bed7c110221551353b54ff8e4704 +10ad0e68785b27bab975868b83bc463b9c9c9153 +7a66612abaa223ef0410fae66727a8abac3add03 +8a7bdba957536b078f0421faf5dfaf8d65ff5add +defd6d03526345a410437eda15cbd067124f9c2c +f7e6c29aa4d1f7a607e0c87ea20105afeee0372a +751363e461257a4036a8f2aa740195401883c1ea +a8d66b5855eda5abf699ebf9c6dd721928007fb8 +35ca716114bdf87a89857f2d633be3f4b13cbc70 +cf319abfba8fc1b33de4c6a6f99e21864cc72563 +4fd36e634e762ff2f94e9d66f24ceabe164f9e26 +d0364113a1b57ed5017dbea6126b0cc5a5c2886d +9b3d7bf551d20acae4ee943a86c3cf898b6280ba +b35351d566efdde005747503c7f121d49e864848 +57b1dc2b20f2e67c3313f0c6127b05041d125fb4 +fadcdf4c98e9167f8f06a45dafa08d3acce7a741 +3bcfcb7717bfc0e50c5b8f5c7beaed9f3ddf5478 +b8388b7b5973dd3e84902c25c5378f9a412d6147 +814f07ea363eb0464380ccfce7b4cf5209f1dcb2 +b33315c8551bede3fb867efb3fdb1134cdff5115 +c7bade1e7cc239e8fceb2c0b06f880e60eb8ebec +bb193f4f0f5b1b8bdc9cc72967f8fa6387faf7c4 +b727e8d9f4a4987cbad41c75c630cfdb445c37a0 +a2103d7fe328871d8231f8e07ba5dc9182f637b3 +e36d269d16660db5bba028746564b5699721def5 +35f9c486cc26bdff903241f4ab2b1dac2536059f +cd5314af7e8e120bceda896a3c17daa8eeedd528 +200e09df8f0f7b94eb8941136482cf7c60fffb0d +17a618f241a6236c93af5ba2e09238369fc7d784 +15aeb2bb0401d428cb7058e1d6554e20369ed352 +40b0a406cc23467af8bb63d9a62378fa871e2031 +7abd7f4cb237ef33b9e019f4529b6fb05b84284f +ac614b7506e820457417c3ea15ba99fbc8146155 +8afd5a714da3f45389e0e4edeb64f49576c57c76 +77d10571047d8b4153180e7a89d5c9aae6a84060 +35479ce1706725f73bfe99428c43e8fe2e3f9157 +360a0864ece712571d3df95e86251d6883bcdf7d +b5cd910848f592e33efb6de3226c07ae545a2aad +f5a9c28ca029ec5d1c5d3c594afa09374adf04e5 +b9fce5928a1c5056f66706b67c01cd564e6c0a90 +e5a2250e35706127304cd5ed86b81575f2636b5b +f30cffe4cef93aa190bcb1caf407ca0767107d06 +45535f6e0af6785676531c81b4a2a3c480a98e70 +740bd201b23beded9ade92a93301cddc67c4d106 +70460e9e601171276dd6844cd6addd8db5eb2465 +44dff2c35acb4736b183cef9e46155386f579716 +46ea31f673bc9365fcca558f15c862ef6a899018 +34556caf76c2422a76be3d1cecd223fcf435d93c +fea67ed9483b5cc76dc55eb4dd6f52baf445394d +31b1897ece6222826f379c1aebda891384b4b63a +80dcf3713b85b78979d4eb443fce9e992675b5c0 +11993c742658321c0c5c200f48231583216d636c +7b5a089ed3007252e61df0aee3fc17c14d051745 +890881c9a552c22f4be01dee16ee902c88f6700f +401ba79da09dded82a73996c8e0609a87cbd728b +e06313f41971de730085dcddf640a4549fc54fc3 +054d52e86a954a615ed1f5add7f9d6842737d965 +d8a60982c456a9cae3de745a37dc3f5985814f7f +2b39f575a510cf581aa828df494e633cc76fafa6 +e11d353191175b329b3c9f9af7fa33e3ef9f837d +32ac2659ce98765aaae9c10cc7216d1f1faf155e +5f7f801227868c7abcce7e58dee3eff855011955 +a013eaf0fe38d8689e27278bddd4ebf87ac5476b +401b3f3d2d96fa785c5321bb64c97cfb17c509e3 +1fa4fd4321fa708b3db5cfb514e2192b00672aff +77976b24ff839c59c3b20d80cb28351ccb5e59a8 +09b76d2966e2370a78ed37a31c2f7c23d08609c3 +7000b24511618a21d40b39ee213d397e1d29497d +c2a6adfcd18c0d95dbed6ea62ac9c9a912d18123 +6ba3609953d5c46a76ca1d0d3d83018be61454e6 +3dff6074fe205e36fae219f277ef87aab097e236 +1cdc8437fa6c621d96c4dfa5f6370c8fdb9cbc3d +d471720bc8f7ce7109276b49dd9c76b6163007d9 +a67b1bdd027629dfc38601b21dc564272e28712c +20125a6d37d5c1614ffe1de94ca064095968e7f0 +2b642751ef86265a1c953186810e118740f8bd2d +e562c1d74e2b6744572184e66a0673e55f9ba0b8 +ba9687b5d746dda28d4a19c5c96d0679d7c77b15 +f39d7d293c3e342b4f447bb440a9b6f72d2d20cc +95750ad9e700efd15d137963ba0dc443e6c9b6b0 +0f76d8445048dc0bfcaf05e30b61b338a08f0e48 +1a9a4c61d6a371d9e95eaef44fa2452d17a09d22 +912b41aad5983d9735379d322eae8f6d40d8bdca +eea0b559472874ff48c34f16bb805108967e6489 +ad4e7ba4032e6b1c047230b3144848dbcf66a127 +b6d93107393dee6eebb05376a67f2e4dfcb44311 diff --git a/tests/fixtures/rev_list_delta_a b/tests/fixtures/rev_list_delta_a new file mode 100644 index 0000000..023c551 --- /dev/null +++ b/tests/fixtures/rev_list_delta_a @@ -0,0 +1,8 @@ +e34590b7a2d186b3bb9a1170d02d52b36c791c78 +8977833d74f8681aa0d9a5e84b0dd3d81519774d +6f5561530cb3a94e4c86454e84732197325be172 +ee419e04a961543444be6db66aef52e6e37936d6 +d845de9d438e1a249a0c2fcb778e8ea3b7e06cef +0bba4a6c10060405a94d52533af2f9bdacd4f29c +77711c0722964ead965e0ba2ee9ed4a03cb3d292 +501d23cac6dd911511f15d091ee031a15b90ebde diff --git a/tests/fixtures/rev_list_delta_b b/tests/fixtures/rev_list_delta_b new file mode 100644 index 0000000..aea7187 --- /dev/null +++ b/tests/fixtures/rev_list_delta_b @@ -0,0 +1,11 @@ +4c8124ffcf4039d292442eeccabdeca5af5c5017 +634396b2f541a9f2d58b00be1a07f0c358b999b3 +ab25fd8483882c3bda8a458ad2965d2248654335 +e34590b7a2d186b3bb9a1170d02d52b36c791c78 +8977833d74f8681aa0d9a5e84b0dd3d81519774d +6f5561530cb3a94e4c86454e84732197325be172 +ee419e04a961543444be6db66aef52e6e37936d6 +d845de9d438e1a249a0c2fcb778e8ea3b7e06cef +0bba4a6c10060405a94d52533af2f9bdacd4f29c +77711c0722964ead965e0ba2ee9ed4a03cb3d292 +501d23cac6dd911511f15d091ee031a15b90ebde diff --git a/tests/fixtures/rev_list_single b/tests/fixtures/rev_list_single new file mode 100644 index 0000000..d8c6431 --- /dev/null +++ b/tests/fixtures/rev_list_single @@ -0,0 +1,7 @@ +commit 4c8124ffcf4039d292442eeccabdeca5af5c5017 +tree 672eca9b7f9e09c22dcb128c283e8c3c8d7697a4 +parent 634396b2f541a9f2d58b00be1a07f0c358b999b3 +author Tom Preston-Werner 1191999972 -0700 +committer Tom Preston-Werner 1191999972 -0700 + + implement Grit#heads diff --git a/tests/fixtures/rev_parse b/tests/fixtures/rev_parse new file mode 100644 index 0000000..a639d89 --- /dev/null +++ b/tests/fixtures/rev_parse @@ -0,0 +1 @@ +80f136f diff --git a/tests/fixtures/show_empty_commit b/tests/fixtures/show_empty_commit new file mode 100644 index 0000000..ea25e32 --- /dev/null +++ b/tests/fixtures/show_empty_commit @@ -0,0 +1,6 @@ +commit 1e3824339762bd48316fe87bfafc853732d43264 +tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 +author Tom Preston-Werner 1157392833 +0000 +committer Tom Preston-Werner 1157392833 +0000 + + initial directory structure diff --git a/tests/index.php b/tests/index.php index 9ba843b..487c0ee 100644 --- a/tests/index.php +++ b/tests/index.php @@ -1,17 +1,28 @@ createSuiteFromClasses('PHPGit Tests', array( - 'GitTest' + 'GitTest', 'Git_TreeTest', )); $result = $suite->run(new DefaultReporter()); if (SimpleReporter::inCli()) { -- 2.11.4.GIT