libdolt 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +56 -0
  3. data/Rakefile +10 -0
  4. data/Readme.md +99 -0
  5. data/lib/libdolt/async/when.rb +128 -0
  6. data/lib/libdolt/disk_repo_resolver.rb +39 -0
  7. data/lib/libdolt/git/blame.rb +112 -0
  8. data/lib/libdolt/git/commit.rb +73 -0
  9. data/lib/libdolt/git/repository.rb +139 -0
  10. data/lib/libdolt/git/submodule.rb +35 -0
  11. data/lib/libdolt/git/tree.rb +42 -0
  12. data/lib/libdolt/repo_actions.rb +95 -0
  13. data/lib/libdolt/version.rb +21 -0
  14. data/lib/libdolt/view/binary_blob_embedder.rb +41 -0
  15. data/lib/libdolt/view/blame.rb +57 -0
  16. data/lib/libdolt/view/blob.rb +97 -0
  17. data/lib/libdolt/view/breadcrumb.rb +47 -0
  18. data/lib/libdolt/view/commit.rb +27 -0
  19. data/lib/libdolt/view/gravatar.rb +29 -0
  20. data/lib/libdolt/view/markup.rb +44 -0
  21. data/lib/libdolt/view/multi_repository.rb +27 -0
  22. data/lib/libdolt/view/object.rb +44 -0
  23. data/lib/libdolt/view/single_repository.rb +27 -0
  24. data/lib/libdolt/view/smart_blob_renderer.rb +33 -0
  25. data/lib/libdolt/view/syntax_highlight.rb +40 -0
  26. data/lib/libdolt/view/tab_width.rb +30 -0
  27. data/lib/libdolt/view/tree.rb +100 -0
  28. data/lib/libdolt/view.rb +23 -0
  29. data/lib/libdolt.rb +29 -0
  30. data/libdolt.gemspec +35 -0
  31. data/test/libdolt/async/when_test.rb +112 -0
  32. data/test/libdolt/git/blame_test.rb +128 -0
  33. data/test/libdolt/git/commit_test.rb +89 -0
  34. data/test/libdolt/git/repository_test.rb +186 -0
  35. data/test/libdolt/repo_actions_test.rb +236 -0
  36. data/test/libdolt/templates/blame_test.rb +54 -0
  37. data/test/libdolt/templates/blob_test.rb +116 -0
  38. data/test/libdolt/templates/commits_test.rb +59 -0
  39. data/test/libdolt/templates/raw_test.rb +39 -0
  40. data/test/libdolt/templates/refs_test.rb +36 -0
  41. data/test/libdolt/templates/tree_history_test.rb +91 -0
  42. data/test/libdolt/templates/tree_test.rb +63 -0
  43. data/test/libdolt/view/binary_blob_embedder_test.rb +49 -0
  44. data/test/libdolt/view/blame_test.rb +122 -0
  45. data/test/libdolt/view/blob_test.rb +116 -0
  46. data/test/libdolt/view/breadcrumb_test.rb +46 -0
  47. data/test/libdolt/view/commit_test.rb +31 -0
  48. data/test/libdolt/view/gravatar_test.rb +30 -0
  49. data/test/libdolt/view/markup_test.rb +70 -0
  50. data/test/libdolt/view/multi_repository_test.rb +35 -0
  51. data/test/libdolt/view/object_test.rb +83 -0
  52. data/test/libdolt/view/single_repository_test.rb +30 -0
  53. data/test/libdolt/view/smart_blob_renderer_test.rb +38 -0
  54. data/test/libdolt/view/syntax_highlight_test.rb +80 -0
  55. data/test/libdolt/view/tab_width_test.rb +40 -0
  56. data/test/libdolt/view/tree_test.rb +196 -0
  57. data/test/test_helper.rb +50 -0
  58. data/views/500.erb +22 -0
  59. data/views/blame.erb +42 -0
  60. data/views/blob.erb +31 -0
  61. data/views/commits.erb +26 -0
  62. data/views/index.erb +25 -0
  63. data/views/layout.erb +45 -0
  64. data/views/raw.erb +19 -0
  65. data/views/refs.erb +22 -0
  66. data/views/tree.erb +50 -0
  67. data/views/tree_history.erb +19 -0
  68. metadata +326 -0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ libdolt (0.6.0)
5
+ em_pessimistic (~> 0.1)
6
+ em_rugged (~> 0.1.2)
7
+ eventmachine (~> 1.0)
8
+ htmlentities (~> 4.3)
9
+ json (~> 1.7)
10
+ makeup (~> 0)
11
+ mime-types (~> 1.19)
12
+ tzinfo (~> 0.3)
13
+
14
+ GEM
15
+ remote: http://rubygems.org/
16
+ specs:
17
+ em-minitest-spec (1.1.1)
18
+ eventmachine
19
+ em_pessimistic (0.1.2)
20
+ eventmachine (~> 1.0)
21
+ em_rugged (0.1.3)
22
+ eventmachine (~> 1.0)
23
+ rugged (= 0.17.0.b6)
24
+ eventmachine (1.0.0)
25
+ github-markup (0.7.4)
26
+ htmlentities (4.3.1)
27
+ json (1.7.5)
28
+ makeup (0.1.1)
29
+ github-markup (~> 0.7)
30
+ htmlentities (~> 4.3)
31
+ pygments.rb (~> 0.2)
32
+ mime-types (1.19)
33
+ minitest (2.12.1)
34
+ posix-spawn (0.3.6)
35
+ pygments.rb (0.3.1)
36
+ posix-spawn (~> 0.3.6)
37
+ yajl-ruby (~> 1.1.0)
38
+ rake (0.9.2.2)
39
+ redcarpet (2.1.1)
40
+ rugged (0.17.0.b6)
41
+ tilt (1.3.3)
42
+ tiltout (1.0.0)
43
+ tilt (~> 1.3)
44
+ tzinfo (0.3.33)
45
+ yajl-ruby (1.1.0)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ em-minitest-spec (~> 1.1)
52
+ libdolt!
53
+ minitest (~> 2.0)
54
+ rake (~> 0.9)
55
+ redcarpet
56
+ tiltout (~> 1)
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rake/testtask"
2
+ require "bundler/gem_tasks"
3
+
4
+ Rake::TestTask.new("test") do |test|
5
+ test.libs << "test"
6
+ test.pattern = "test/**/*_test.rb"
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
data/Readme.md ADDED
@@ -0,0 +1,99 @@
1
+ # Dolt - The Git project browser
2
+
3
+ Dolt is a stand-alone Git repository browser. It can be used to explore repos in
4
+ your browser of choice and features syntax highlighting with
5
+ [Pygments](http://pygments.org/),
6
+ [Markdown](http://daringfireball.net/projects/markdown/)/[org-mode](http://orgmode.org/)/[+++](https://github.com/github/markup/)
7
+ rendering, commit log and blame.
8
+
9
+ Dolt is also a library, designed to render Git trees, blobs, commit log and
10
+ blame. It can render said views with or without a layout, or you can provide
11
+ your own templates (through [Tilt](https://github.com/rtomayko/tilt/)). You can
12
+ also provide your own rendering implementation to render other formats than
13
+ templates outputting HTML.
14
+
15
+ Dolt is the implementation of the next generation repo browser to be used in the
16
+ [Gitorious](http://gitorious.org) software.
17
+
18
+ ## Installing Dolt
19
+
20
+ To install `dolt` you need Ruby, RubyGems and Python development files. The
21
+ Python development files are required to support Pygments syntax highlighting.
22
+
23
+ Note: Dolt uses [libgit2](http://libgit2.github.com) and its Ruby bindings,
24
+ [Rugged](http://github.com/libgit2/rugged) through
25
+ [em-rugged](http://gitorious.org/gitorious/em-rugged) for Git access where
26
+ feasible. Currently, ``EMRugged`` relies on a version of `Rugged` that is not
27
+ yet released, so you have to build it yourself.
28
+ [See em-rugged instructions](http://github.com/cjohansen/em-rugged).
29
+
30
+ ### Systems using apt (Debian/Ubuntu, others)
31
+
32
+ # 1) Install Ruby (skip if you already have Ruby installed)
33
+ sudo apt-get install ruby
34
+
35
+ # 2) Install Python development files
36
+ sudo apt-get install python-dev
37
+
38
+ # 3) Install dolt. This may or may not require the use of sudo, depending on
39
+ # how you installed Ruby. This step assumes that you already built and
40
+ # installed em-rugged as explained above.
41
+ sudo gem install dolt
42
+
43
+ ### Systems using yum (Fedora/CentOS/RedHat, others)
44
+
45
+ # 1) Install Ruby (skip if you already have Ruby installed)
46
+ sudo yum install ruby
47
+
48
+ # 2) Install Python development files
49
+ sudo yum install python-devel
50
+
51
+ # 3) Install dolt. This may or may not require the use of sudo, depending on
52
+ # how you installed Ruby. This step assumes that you already built and
53
+ # installed em-rugged as explained above.
54
+ sudo gem install dolt
55
+
56
+ # The Dolt CLI
57
+
58
+ The `dolt` library installs a CLI that can be used to quickly browse either a
59
+ single (typically the current) repository, or multiple repositories.
60
+
61
+ ## Browsing a single repository
62
+
63
+ In a git repository, issue the following command:
64
+
65
+ $ dolt .
66
+
67
+ Then open a browser at [http://localhost:3000](http://localhost:3000). You will
68
+ be redirected to the root tree, and can browse the repository. To view trees and
69
+ blobs at specific refs, use the URL. A branch/tag selector will be added later.
70
+
71
+ ## Browsing multiple repositories
72
+
73
+ The idea is that eventually, `dolt` should be able to serve up all Git
74
+ repositories managed by your Gitorious server. It does not yet do that, because
75
+ there currently is no "repository resolver" that understands the hashed paths
76
+ Gitorious uses.
77
+
78
+ Meanwhile, if you have a directory that contains multiple git repositories, you
79
+ can browse all of them through the same process by doing:
80
+
81
+ $ dolt /path/to/repos
82
+
83
+ Now [http://localhost:3000/repo](http://localhost:3000/repo) will allow you to
84
+ browse the `/path/repos/repo` repository. As `dolt` matures, there will be a
85
+ listing of all repositories and more.
86
+
87
+ ## Markup rendering
88
+
89
+ Dolt uses the [``GitHub::Markup``](https://github.com/github/markup/) library to
90
+ render certain markup formats as HTML. Dolt does not have a hard dependency on
91
+ any of the required gems to actually render markups, so see the
92
+ [``GitHub::Markup`` docs](https://github.com/github/markup/) for information on
93
+ what and how to install support for various languages.
94
+
95
+ # License
96
+
97
+ Dolt is free software licensed under the
98
+ [GNU Affero General Public License (AGPL)](http://www.gnu.org/licenses/agpl-3.0.html).
99
+ Dolt is developed as part of the Gitorious project.
@@ -0,0 +1,128 @@
1
+ require "eventmachine"
2
+
3
+ module When
4
+ class Promise
5
+ def initialize(deferred = EM::DefaultDeferrable.new)
6
+ @deferred = deferred
7
+ end
8
+
9
+ def callback(&block)
10
+ @deferred.callback(&block)
11
+ self
12
+ end
13
+
14
+ def errback(&block)
15
+ @deferred.errback(&block)
16
+ self
17
+ end
18
+ end
19
+
20
+ class Resolver
21
+ def initialize(deferred = EM::DefaultDeferrable.new)
22
+ @deferred = deferred
23
+ @resolved = false
24
+ end
25
+
26
+ def resolve(*args)
27
+ mark_resolved
28
+ @deferred.succeed(*args)
29
+ end
30
+
31
+ def reject(*args)
32
+ mark_resolved
33
+ @deferred.fail(*args)
34
+ end
35
+
36
+ def resolved?
37
+ @resolved
38
+ end
39
+
40
+ private
41
+ def mark_resolved
42
+ raise StandardError.new("Already resolved") if @resolved
43
+ @resolved = true
44
+ end
45
+ end
46
+
47
+ class Deferred
48
+ attr_reader :resolver, :promise
49
+
50
+ def initialize
51
+ deferred = EM::DefaultDeferrable.new
52
+ @resolver = Resolver.new(deferred)
53
+ @promise = Promise.new(deferred)
54
+ end
55
+
56
+ def resolve(*args)
57
+ @resolver.resolve(*args)
58
+ end
59
+
60
+ def reject(*args)
61
+ @resolver.reject(*args)
62
+ end
63
+
64
+ def callback(&block)
65
+ @promise.callback(&block)
66
+ end
67
+
68
+ def errback(&block)
69
+ @promise.errback(&block)
70
+ end
71
+
72
+ def resolved?
73
+ @resolver.resolved?
74
+ end
75
+
76
+ def self.resolved(value)
77
+ d = self.new
78
+ d.resolve(value)
79
+ d
80
+ end
81
+
82
+ def self.rejected(value)
83
+ d = self.new
84
+ d.reject(value)
85
+ d
86
+ end
87
+ end
88
+
89
+ def self.deferred(val)
90
+ return val if val.respond_to?(:callback) && val.respond_to?(:errback)
91
+ Deferred.resolved(val).promise
92
+ end
93
+
94
+ def self.all(promises)
95
+ raise(ArgumentError, "expected enumerable promises") if !promises.is_a?(Enumerable)
96
+ resolved = 0
97
+ results = []
98
+ d = Deferred.new
99
+
100
+ attempt_resolution = lambda do |err, res|
101
+ break if d.resolved?
102
+ if err.nil?
103
+ d.resolve(res) if promises.length == resolved
104
+ else
105
+ d.reject(err)
106
+ end
107
+ end
108
+
109
+ wait_for_all(promises) do |err, result, index|
110
+ resolved += 1
111
+ results[index] = result
112
+ attempt_resolution.call(err, results)
113
+ end
114
+
115
+ attempt_resolution.call(nil, results) if promises.length == 0
116
+ d.promise
117
+ end
118
+
119
+ private
120
+ def self.wait_for_all(promises, &block)
121
+ promises.each_with_index do |p, i|
122
+ p.callback do |result|
123
+ block.call(nil, result, i)
124
+ end
125
+ p.errback { |e| block.call(e, nil, i) }
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2012 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "libdolt/git/repository"
19
+
20
+ module Dolt
21
+ class DiskRepoResolver
22
+ def initialize(root)
23
+ @root = root
24
+ end
25
+
26
+ def resolve(repo)
27
+ Dolt::Git::Repository.new(File.join(root, repo))
28
+ end
29
+
30
+ def all
31
+ (Dir.entries(root).select do |e|
32
+ File.exists?(File.join(root, e, ".git"))
33
+ end).sort
34
+ end
35
+
36
+ private
37
+ def root; @root; end
38
+ end
39
+ end
@@ -0,0 +1,112 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2012 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "tzinfo"
19
+
20
+ module Dolt
21
+ module Git
22
+ class Blame
23
+ attr_reader :chunks
24
+
25
+ def initialize(chunks)
26
+ @chunks = chunks
27
+ end
28
+
29
+ def self.parse_porcelain(output)
30
+ self.new(Dolt::Git::Blame::PorcelainParser.new(output).parse)
31
+ end
32
+
33
+ class PorcelainParser
34
+ def initialize(output)
35
+ @output = output
36
+ @commits = {}
37
+ end
38
+
39
+ def parse
40
+ lines = @output.split("\n")
41
+ chunks = []
42
+
43
+ while lines.length > 0
44
+ chunk = extract_header(lines)
45
+ affected_lines = extract_lines(lines, chunk[:num_lines])
46
+
47
+ if chunks.last && chunk[:oid] == chunks.last[:oid]
48
+ chunks.last[:lines].concat(affected_lines)
49
+ else
50
+ chunk[:lines] = affected_lines
51
+ chunks << chunk
52
+ end
53
+ end
54
+
55
+ chunks
56
+ end
57
+
58
+ def is_header?(line)
59
+ line =~ /^[0-9a-f]{40} \d+ \d+ \d+$/
60
+ end
61
+
62
+ def extract_header(lines)
63
+ header = lines.shift
64
+ pieces = header.scan(/^([0-9a-f]{40}) (\d+) (\d+) (\d+)$/).first
65
+ header = { :oid => pieces.first, :num_lines => pieces[3].to_i }
66
+
67
+ if lines.first =~ /^author/
68
+ header[:author] = extract_hash(lines, :author)
69
+ header[:committer] = extract_hash(lines, :committer)
70
+ header[:summary] = extract(lines, "summary")
71
+ throwaway = lines.shift until throwaway =~ /^filename/
72
+ @commits[header[:oid]] = header
73
+ else
74
+ header[:author] = @commits[header[:oid]][:author]
75
+ header[:committer] = @commits[header[:oid]][:committer]
76
+ header[:summary] = @commits[header[:oid]][:summary]
77
+ end
78
+
79
+ header
80
+ end
81
+
82
+ def extract_lines(lines, num)
83
+ extracted = []
84
+
85
+ num.times do
86
+ if extracted.length > 0
87
+ line = lines.shift # Header for next line
88
+ end
89
+
90
+ content = lines.shift # Actual content
91
+ extracted.push(content[1..content.length]) # 8 spaces padding
92
+ end
93
+
94
+ extracted
95
+ end
96
+
97
+ def extract_hash(lines, type)
98
+ {
99
+ :name => extract(lines, "#{type}"),
100
+ :mail => extract(lines, "#{type}-mail").gsub(/[<>]/, ""),
101
+ :time => (Time.at(extract(lines, "#{type}-time").to_i) +
102
+ Time.zone_offset(extract(lines, "#{type}-tz")))
103
+ }
104
+ end
105
+
106
+ def extract(lines, thing)
107
+ lines.shift.split("#{thing} ")[1]
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2012 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "time"
19
+
20
+ module Dolt
21
+ module Git
22
+ class Commit
23
+ def self.parse_log(log)
24
+ commits = []
25
+ lines = log.split("\n")
26
+ commits << extract_commit(lines) while lines.length > 0
27
+ commits
28
+ end
29
+
30
+ def self.extract_commit(lines)
31
+ commit = { :oid => lines.shift.split(" ")[1] }
32
+ while (line = lines.shift) != ""
33
+ pieces = line.split(": ")
34
+ extract_property(commit, pieces[0], pieces[1])
35
+ end
36
+
37
+ commit[:summary] = extract_commit_summary(lines)
38
+ commit[:message] = extract_commit_message(lines)
39
+ commit
40
+ end
41
+
42
+ def self.extract_property(hash, name, value)
43
+ key = name.downcase.to_sym
44
+
45
+ case key
46
+ when :author
47
+ pieces = value.match(/(.*)\s<(.*)>/)
48
+ value = { :name => pieces[1], :email => pieces[2] }
49
+ when :date
50
+ value = Time.parse(value)
51
+ end
52
+
53
+ hash[key] = value
54
+ end
55
+
56
+ def self.extract_commit_summary(lines)
57
+ summary = lines.shift
58
+ lines.shift if lines.first == ""
59
+ summary.sub(/^ /, "")
60
+ end
61
+
62
+ def self.extract_commit_message(lines)
63
+ message = ""
64
+
65
+ while !lines.first.nil? && lines.first !~ /^commit [a-z0-9]{40}$/
66
+ message << lines.shift
67
+ end
68
+
69
+ message
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,139 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2012 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ require "em_rugged/repository"
19
+ require "em_pessimistic/deferrable_child_process"
20
+ require "em/deferrable"
21
+ require "libdolt/git/blame"
22
+ require "libdolt/git/commit"
23
+ require "libdolt/git/submodule"
24
+ require "libdolt/git/tree"
25
+ require "libdolt/async/when"
26
+
27
+ module Dolt
28
+ module Git
29
+ class Repository < EMRugged::Repository
30
+ def submodules(ref)
31
+ d = EventMachine::DefaultDeferrable.new
32
+ gm = rev_parse("#{ref}:.gitmodules")
33
+ gm.callback do |config|
34
+ d.succeed(Dolt::Git::Submodule.parse_config(config.content))
35
+ end
36
+ # Fails if .gitmodules cannot be found, which means no submodules
37
+ gm.errback { |err| d.succeed([]) }
38
+ d
39
+ end
40
+
41
+ def tree(ref, path)
42
+ d = EventMachine::DefaultDeferrable.new
43
+ rp = rev_parse("#{ref}:#{path}")
44
+ rp.callback do |tree|
45
+ break d.fail(StandardError.new("Not a tree")) unless tree.is_a?(Rugged::Tree)
46
+ break d.succeed(tree) if !tree.find { |e| e[:type].nil? }
47
+ annotate_submodules(ref, path, d, tree)
48
+ end
49
+ rp.errback { |err| d.fail(err) }
50
+ d
51
+ end
52
+
53
+ def blame(ref, path)
54
+ deferred_method("blame -l -t -p #{ref} #{path}") do |output, s|
55
+ Dolt::Git::Blame.parse_porcelain(output)
56
+ end
57
+ end
58
+
59
+ def log(ref, path, limit)
60
+ entry_history(ref, path, limit)
61
+ end
62
+
63
+ def tree_history(ref, path, limit = 1)
64
+ d = EventMachine::DefaultDeferrable.new
65
+ rp = rev_parse("#{ref}:#{path}")
66
+ rp.errback { |err| d.fail(err) }
67
+ rp.callback do |tree|
68
+ if tree.class != Rugged::Tree
69
+ message = "#{ref}:#{path} is not a tree (#{tree.class.to_s})"
70
+ break d.fail(Exception.new(message))
71
+ end
72
+
73
+ building = build_history(path || "./", ref, tree, limit)
74
+ building.callback { |history| d.succeed(history) }
75
+ building.errback { |err| d.fail(err) }
76
+ end
77
+ d
78
+ end
79
+
80
+ private
81
+ def entry_history(ref, entry, limit)
82
+ deferred_method("log -n #{limit} #{ref} -- #{entry}") do |out, s|
83
+ Dolt::Git::Commit.parse_log(out)
84
+ end
85
+ end
86
+
87
+ def build_history(path, ref, entries, limit)
88
+ d = EventMachine::DefaultDeferrable.new
89
+ resolve = lambda { |p| path == "" ? p : File.join(path, p) }
90
+ progress = When.all(entries.map do |e|
91
+ entry_history(ref, resolve.call(e[:name]), limit)
92
+ end)
93
+ progress.errback { |e| d.fail(e) }
94
+ progress.callback do |history|
95
+ d.succeed(entries.map { |e| e.merge({ :history => history.shift }) })
96
+ end
97
+ d
98
+ end
99
+
100
+ def annotate_submodules(ref, path, deferrable, tree)
101
+ submodules(ref).callback do |submodules|
102
+ entries = tree.entries.map do |entry|
103
+ if entry[:type].nil?
104
+ mod = path == "" ? entry[:name] : File.join(path, entry[:name])
105
+ meta = submodules.find { |s| s[:path] == mod }
106
+ if meta
107
+ entry[:type] = :submodule
108
+ entry[:url] = meta[:url]
109
+ end
110
+ end
111
+ entry
112
+ end
113
+
114
+ deferrable.succeed(Dolt::Git::Tree.new(tree.oid, entries))
115
+ end
116
+ end
117
+
118
+ def deferred_method(cmd, &block)
119
+ d = EventMachine::DefaultDeferrable.new
120
+ cmd = git(cmd)
121
+ p = EMPessimistic::DeferrableChildProcess.open(cmd)
122
+
123
+ p.callback do |output, status|
124
+ d.succeed(block.call(output, status))
125
+ end
126
+
127
+ p.errback do |stderr, status|
128
+ d.fail(stderr)
129
+ end
130
+
131
+ d
132
+ end
133
+
134
+ def git(cmd)
135
+ "git --git-dir #{subject.path} #{cmd}"
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ #--
3
+ # Copyright (C) 2012 Gitorious AS
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+
19
+ module Dolt
20
+ module Git
21
+ class Submodule
22
+ def self.parse_config(config)
23
+ config.split("\n").inject([]) do |modules, line|
24
+ if line =~ /\[submodule ".*"\]/
25
+ modules << {}
26
+ else
27
+ _, key, val = *line.match(/\s([^\s]+) = ([^\s]+)/)
28
+ modules.last[key.to_sym] = val
29
+ end
30
+ modules
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end