libdolt 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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