mercurial-ruby 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +32 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +86 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/mercurial-ruby.rb +66 -0
- data/lib/mercurial-ruby/branch.rb +45 -0
- data/lib/mercurial-ruby/changed_file.rb +51 -0
- data/lib/mercurial-ruby/command.rb +77 -0
- data/lib/mercurial-ruby/commit.rb +152 -0
- data/lib/mercurial-ruby/config_file.rb +119 -0
- data/lib/mercurial-ruby/configuration.rb +14 -0
- data/lib/mercurial-ruby/diff.rb +50 -0
- data/lib/mercurial-ruby/factories/branch_factory.rb +91 -0
- data/lib/mercurial-ruby/factories/changed_file_factory.rb +50 -0
- data/lib/mercurial-ruby/factories/commit_factory.rb +154 -0
- data/lib/mercurial-ruby/factories/diff_factory.rb +63 -0
- data/lib/mercurial-ruby/factories/hook_factory.rb +45 -0
- data/lib/mercurial-ruby/factories/node_factory.rb +111 -0
- data/lib/mercurial-ruby/factories/tag_factory.rb +48 -0
- data/lib/mercurial-ruby/file_index.rb +209 -0
- data/lib/mercurial-ruby/helper.rb +23 -0
- data/lib/mercurial-ruby/hook.rb +23 -0
- data/lib/mercurial-ruby/manifest.rb +58 -0
- data/lib/mercurial-ruby/node.rb +100 -0
- data/lib/mercurial-ruby/repository.rb +94 -0
- data/lib/mercurial-ruby/root_node.rb +27 -0
- data/lib/mercurial-ruby/shell.rb +64 -0
- data/lib/mercurial-ruby/style.rb +23 -0
- data/lib/mercurial-ruby/tag.rb +33 -0
- data/lib/stdlib_exts/string.rb +12 -0
- data/lib/styles/changeset.style +5 -0
- data/lib/styles/file_index.style +3 -0
- data/mercurial-ruby.gemspec +227 -0
- data/test/fixtures.rb +28 -0
- data/test/fixtures/test-repo/.DotFile +1 -0
- data/test/fixtures/test-repo/.hg/00changelog.i +0 -0
- data/test/fixtures/test-repo/.hg/branch +1 -0
- data/test/fixtures/test-repo/.hg/cache/branchheads +6 -0
- data/test/fixtures/test-repo/.hg/cache/tags +7 -0
- data/test/fixtures/test-repo/.hg/dirstate +0 -0
- data/test/fixtures/test-repo/.hg/hgrc +3 -0
- data/test/fixtures/test-repo/.hg/last-message.txt +1 -0
- data/test/fixtures/test-repo/.hg/requires +4 -0
- data/test/fixtures/test-repo/.hg/store/00changelog.i +0 -0
- data/test/fixtures/test-repo/.hg/store/00manifest.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_file _with _whitespace.pdf.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_l_i_c_e_n_s_e.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_l_i_c_e_n_s_e2.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_l_i_c_e_n_s_e3.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_l_i_c_e_n_s_e4.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_r_e_a_d_m_e.markdown.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_r_e_a_d_m_e.markup.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_rakefile.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_rakefile2.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/_rakefile3.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/check ~5c this ~5c out ~22 now.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/minitest__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/options.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/rdoc__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/rspec__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/shindo__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/shoulda__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/_gemfile.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/_l_i_c_e_n_s_e.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/_r_e_a_d_m_e.rdoc.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/_rakefile.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/bacon/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/bacon/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/bundler__setup.erb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/features/default.feature.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/features/support/env.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/jeweler__tasks.erb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/micronaut/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/micronaut/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/minitest/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/minitest/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/other__tasks.erb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/riot/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/riot/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/rspec/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/rspec/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/rspec/~2erspec.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/shindo/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/shindo/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/shoulda/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/shoulda/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/testspec/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/testspec/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/testunit/flunking.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/testunit/helper.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/~2edocument.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/templates/~2egitignore.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory two/~2e_d_s___store.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory__1/rubygems__dot__org__tasks.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory__1/rubygems__tasks.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory__1/specification.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory__1/tasks.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/directory__1/~2e_d_s___store.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/empty-file.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/goose.png.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/goose/pretty-thing.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-directory/another-boring-file.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-directory/something.csv.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-directory/subdirectory/_e_u_l_a5seat___chin___sim02.03.04.pdf.d +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-directory/subdirectory/_e_u_l_a5seat___chin___sim02.03.04.pdf.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-directory/subdirectory/_file _with _whitespace.pdf.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-directory/subdirectory/beansprout.png.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/new-file.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/old-directory/minitest__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/old-directory/options.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/old-directory/rspec__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/old-directory/shindo__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/old-directory/testunit__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/old-directory/yard__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/riot__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/riot__mixin__copy.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/style.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/super-cow.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/superman.txt.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/testspec__mixin.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/testspec__mixin__new.rb.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/~2e_dot_file.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/~2ehgignore.i +0 -0
- data/test/fixtures/test-repo/.hg/store/data/~2ehgtags.i +0 -0
- data/test/fixtures/test-repo/.hg/store/fncache +79 -0
- data/test/fixtures/test-repo/.hg/store/undo +0 -0
- data/test/fixtures/test-repo/.hg/undo.bookmarks +0 -0
- data/test/fixtures/test-repo/.hg/undo.branch +1 -0
- data/test/fixtures/test-repo/.hg/undo.desc +2 -0
- data/test/fixtures/test-repo/.hg/undo.dirstate +0 -0
- data/test/fixtures/test-repo/.hgignore +1 -0
- data/test/fixtures/test-repo/.hgtags +1 -0
- data/test/fixtures/test-repo/LICENSE3.txt +15 -0
- data/test/fixtures/test-repo/LICENSE4.txt +17 -0
- data/test/fixtures/test-repo/README.markup +218 -0
- data/test/fixtures/test-repo/Rakefile3 +83 -0
- data/test/fixtures/test-repo/check // this // out /" now" "b/data/test/fixtures/test-repo/check // this // out / → now +0 -0
- data/test/fixtures/test-repo/empty-file +0 -0
- data/test/fixtures/test-repo/goose.png +0 -0
- data/test/fixtures/test-repo/goose/pretty-thing.txt +0 -0
- data/test/fixtures/test-repo/new-directory/another-boring-file +78 -0
- data/test/fixtures/test-repo/new-directory/something.csv +1 -0
- data/test/fixtures/test-repo/new-directory/subdirectory/EULA5seat_Chin_Sim02.03.04.pdf +0 -0
- data/test/fixtures/test-repo/new-directory/subdirectory/File With Whitespace.pdf b/data/test/fixtures/test-repo/new-directory/subdirectory/File With → Whitespace.pdf +0 -0
- data/test/fixtures/test-repo/new-directory/subdirectory/beansprout.png +0 -0
- data/test/fixtures/test-repo/riot_mixin.rb +45 -0
- data/test/fixtures/test-repo/style +4 -0
- data/test/fixtures/test-repo/superman.txt +1 -0
- data/test/fixtures/test-repo/testspec_mixin_new.rb +44 -0
- data/test/helper.rb +41 -0
- data/test/test_branch_factory.rb +46 -0
- data/test/test_changed_file.rb +46 -0
- data/test/test_changed_file_factory.rb +16 -0
- data/test/test_command.rb +62 -0
- data/test/test_commit.rb +66 -0
- data/test/test_commit_factory.rb +101 -0
- data/test/test_config_file.rb +105 -0
- data/test/test_configuration.rb +26 -0
- data/test/test_diff.rb +39 -0
- data/test/test_diff_factory.rb +38 -0
- data/test/test_file_index.rb +113 -0
- data/test/test_hook.rb +39 -0
- data/test/test_hook_factory.rb +40 -0
- data/test/test_manifest.rb +39 -0
- data/test/test_node.rb +34 -0
- data/test/test_node_factory.rb +125 -0
- data/test/test_repository.rb +58 -0
- data/test/test_shell.rb +33 -0
- data/test/test_tag_factory.rb +27 -0
- metadata +328 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
module Mercurial
|
2
|
+
|
3
|
+
#
|
4
|
+
# This class represents a factory for {Mercurial::Commit Commit} instances.
|
5
|
+
#
|
6
|
+
class CommitFactory
|
7
|
+
include Mercurial::Helper
|
8
|
+
|
9
|
+
# Instance of {Mercurial::Repository Repository}.
|
10
|
+
attr_reader :repository
|
11
|
+
|
12
|
+
def initialize(repository) #:nodoc:
|
13
|
+
@repository = repository
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return an array of {Mercurial::Commit Commit} instances for all changesets in the repository.
|
17
|
+
#
|
18
|
+
# == Example:
|
19
|
+
# repository.commits.all
|
20
|
+
#
|
21
|
+
def all
|
22
|
+
hg_to_array ["log --style ?", style], changeset_separator do |line|
|
23
|
+
build(line)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Run a block for every {Mercurial::Commit Commit} instance of all changesets in the repository.
|
28
|
+
#
|
29
|
+
# == Example:
|
30
|
+
# repository.commits.each {|commit| ... }
|
31
|
+
#
|
32
|
+
def each(&block)
|
33
|
+
all.each do |commit|
|
34
|
+
block.call(commit)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Count all changesets in the repository.
|
39
|
+
#
|
40
|
+
# == Example:
|
41
|
+
# repository.commits.count
|
42
|
+
#
|
43
|
+
def count
|
44
|
+
hg_to_array %Q[log --template "{node}\n"], "\n" do |line|
|
45
|
+
line
|
46
|
+
end.size
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return an array of {Mercurial::Commit Commit} instances for changesets in a specific branch.
|
50
|
+
#
|
51
|
+
# == Example:
|
52
|
+
# repository.commits.by_branch('brancname')
|
53
|
+
#
|
54
|
+
def by_branch(branch)
|
55
|
+
hg_to_array ["log -b ? --style ?", branch, style], changeset_separator do |line|
|
56
|
+
build(line)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return an instance of {Mercurial::Commit Commit} for a changeset with a specified id.
|
61
|
+
#
|
62
|
+
# == Example:
|
63
|
+
# repository.commits.by_hash_id('291a498f04e9')
|
64
|
+
#
|
65
|
+
def by_hash_id(hash)
|
66
|
+
build do
|
67
|
+
hg(["log -r ? --style ?", hash, style])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return an array of {Mercurial::Commit Commit} instances for changesets with specified ids.
|
72
|
+
#
|
73
|
+
# == Example:
|
74
|
+
# repository.commits.by_hash_ids('291a498f04e9', '63f70b2314ed')
|
75
|
+
#
|
76
|
+
def by_hash_ids(*args)
|
77
|
+
if args.size == 1 && args.first.kind_of?(Array)
|
78
|
+
array = args.first
|
79
|
+
else
|
80
|
+
array = args
|
81
|
+
end
|
82
|
+
return [] if array.empty?
|
83
|
+
|
84
|
+
args = array.map{|hash| " -r#{ hash }"}
|
85
|
+
hg_to_array ["log#{ args } --style ?", style], changeset_separator do |line|
|
86
|
+
build(line)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return an array of {Mercurial::Commit Commit} instances for a specified range of changeset ids.
|
91
|
+
#
|
92
|
+
# == Example:
|
93
|
+
# repository.commits.for_range('bf6386c0a0cc', '63f70b2314ed')
|
94
|
+
#
|
95
|
+
def for_range(hash_a, hash_b)
|
96
|
+
hg_to_array ["log -r ?:? --style ?", hash_a, hash_b, style], changeset_separator do |line|
|
97
|
+
build(line)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return an instance of {Mercurial::Commit Commit} for a repository's tip changeset (latest).
|
102
|
+
#
|
103
|
+
# == Example:
|
104
|
+
# repository.commits.tip
|
105
|
+
#
|
106
|
+
def tip
|
107
|
+
build do
|
108
|
+
hg(["tip --style ?", style])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
alias :latest :tip
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
def changeset_separator
|
116
|
+
Mercurial::Style::CHANGESET_SEPARATOR
|
117
|
+
end
|
118
|
+
|
119
|
+
def field_separator
|
120
|
+
Mercurial::Style::FIELD_SEPARATOR
|
121
|
+
end
|
122
|
+
|
123
|
+
def build(data=nil, &block)
|
124
|
+
data ||= block.call
|
125
|
+
return if data.empty?
|
126
|
+
data = data.gsub(/#{ Regexp.escape(changeset_separator) }$/, '')
|
127
|
+
data = data.split(field_separator)
|
128
|
+
commit = Mercurial::Commit.new(
|
129
|
+
repository,
|
130
|
+
:hash_id => data[0],
|
131
|
+
:author => data[1],
|
132
|
+
:author_email => data[2],
|
133
|
+
:date => data[3],
|
134
|
+
:message => data[4],
|
135
|
+
:changed_files => [data[5], data[6], data[7], data[8]],
|
136
|
+
:branches_names => data[9],
|
137
|
+
:tags_names => data[10],
|
138
|
+
:parents => data[11]
|
139
|
+
)
|
140
|
+
|
141
|
+
if commit.blank?
|
142
|
+
nil
|
143
|
+
else
|
144
|
+
commit
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def style
|
149
|
+
Mercurial::Style.changeset
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Mercurial
|
2
|
+
|
3
|
+
#
|
4
|
+
# This class represents a factory for {Mercurial::Diff Diff} instances.
|
5
|
+
#
|
6
|
+
class DiffFactory
|
7
|
+
include Mercurial::Helper
|
8
|
+
|
9
|
+
# Instance of {Mercurial::Repository Repository}.
|
10
|
+
attr_reader :repository
|
11
|
+
|
12
|
+
def initialize(repository)
|
13
|
+
@repository = repository
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns an array of {Mercurial::Diff Diff} instances for a specified
|
17
|
+
# instance of {Mercurial::Commit Commit}. Represents changeset's diffs.
|
18
|
+
#
|
19
|
+
# == Example:
|
20
|
+
# commit = repository.commits.by_hash_id('291a498f04e9')
|
21
|
+
# repository.diffs.for_commit(commit)
|
22
|
+
#
|
23
|
+
def for_commit(commit)
|
24
|
+
[].tap do |returning|
|
25
|
+
data = hg(["diff -c ?", commit.hash_id])
|
26
|
+
chunks = data.split(/^diff/)[1..-1]
|
27
|
+
unless chunks.nil?
|
28
|
+
chunks.map do |piece|
|
29
|
+
piece = "diff" << piece
|
30
|
+
returning << build(commit, piece)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def build(commit, data)
|
39
|
+
return if data.empty?
|
40
|
+
hash_a, hash_b = *data.scan(/^diff -r (\w+) -r (\w+)/).first
|
41
|
+
|
42
|
+
if binary_file = data.scan(/^Binary file (.+) has changed/).flatten.first
|
43
|
+
file_a = binary_file
|
44
|
+
body = 'Binary files differ'
|
45
|
+
else
|
46
|
+
file_a = data.scan(/^--- (?:a\/(.+)|\/dev\/null)\t/).flatten.first
|
47
|
+
file_b = data.scan(/^\+\+\+ (?:b\/(.+)|\/dev\/null)\t/).flatten.first
|
48
|
+
body = data[data.index("\n")+1..-1]
|
49
|
+
end
|
50
|
+
|
51
|
+
Mercurial::Diff.new(commit,
|
52
|
+
:hash_a => hash_a,
|
53
|
+
:hash_b => hash_b,
|
54
|
+
:file_a => file_a,
|
55
|
+
:file_b => file_b,
|
56
|
+
:body => body,
|
57
|
+
:binary => !!binary_file
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Mercurial
|
2
|
+
|
3
|
+
class HookFactory
|
4
|
+
|
5
|
+
attr_reader :repository
|
6
|
+
|
7
|
+
def initialize(repository)
|
8
|
+
@repository = repository
|
9
|
+
end
|
10
|
+
|
11
|
+
def all
|
12
|
+
[].tap do |returning|
|
13
|
+
repository.config.find_header('hooks').each_pair do |name, value|
|
14
|
+
returning << build(name, value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def by_name(name)
|
20
|
+
all.find do |h|
|
21
|
+
h.name == name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(name, value)
|
26
|
+
build(name, value).tap do |hook|
|
27
|
+
hook.save
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove(name)
|
32
|
+
if hook = by_name(name)
|
33
|
+
hook.destroy!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def build(name, value)
|
40
|
+
Mercurial::Hook.new(repository, name, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Mercurial
|
2
|
+
class NodeMissing < Error; end
|
3
|
+
|
4
|
+
#
|
5
|
+
# This class represents a factory for {Mercurial::Node Node} instances.
|
6
|
+
#
|
7
|
+
class NodeFactory
|
8
|
+
include Mercurial::Helper
|
9
|
+
|
10
|
+
# Instance of {Mercurial::Repository Repository}.
|
11
|
+
attr_reader :repository
|
12
|
+
|
13
|
+
def initialize(repository)
|
14
|
+
@repository = repository
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finds a specified file or a directory in the repository at a specified revision.
|
18
|
+
# Returns an instance of {Mercurial::Node Node}.
|
19
|
+
#
|
20
|
+
# Will find node in the latest version of repo if revision is ommitted.
|
21
|
+
# Will return nil if node wasn't found.
|
22
|
+
#
|
23
|
+
# == Example:
|
24
|
+
# repository.nodes.find('/')
|
25
|
+
# repository.nodes.find('some-fancy-directory/Weird File Name.pdf', '291a498f04e9')
|
26
|
+
# repository.nodes.find('some-fancy-directory/subdirectory/', '291a498f04e9')
|
27
|
+
#
|
28
|
+
def find(path, revision=nil)
|
29
|
+
revision ||= 'tip'
|
30
|
+
return RootNode.new(:repository => repository, :revision => revision) if path == '/'
|
31
|
+
entry = repository.manifest.scan_for_path(path, revision).first
|
32
|
+
return unless entry
|
33
|
+
if exact_path = entry[3].scan(/^(#{ Regexp.escape(path.without_trailing_slash) }\/)/).flatten.first
|
34
|
+
name = exact_path.split('/').last + '/'
|
35
|
+
build(
|
36
|
+
:path => exact_path,
|
37
|
+
:name => name,
|
38
|
+
:revision => revision
|
39
|
+
)
|
40
|
+
else
|
41
|
+
build(
|
42
|
+
:path => entry[3],
|
43
|
+
:name => entry[3].split('/').last,
|
44
|
+
:revision => revision,
|
45
|
+
:nodeid => entry[0],
|
46
|
+
:fmode => entry[1],
|
47
|
+
:exec => entry[2]
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Same as +find+ but will raise a NodeMissing exception if node wasn't found.
|
53
|
+
def find!(path, revision=nil)
|
54
|
+
find(path, revision) || raise(NodeMissing, "#{ path } at revision #{ revision }")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Find all entries (files and directories) inside a specified path and revision.
|
58
|
+
# Returns an array of {Mercurial::Node Node} instances.
|
59
|
+
#
|
60
|
+
# Will find node in the latest version of repo if revision is ommitted.
|
61
|
+
#
|
62
|
+
# == Example:
|
63
|
+
# repository.nodes.entries_for('/')
|
64
|
+
# repository.nodes.entries_for('some-fancy-directory/subdirectory/', '291a498f04e9')
|
65
|
+
#
|
66
|
+
def entries_for(path, revision=nil, parent=nil)
|
67
|
+
revision ||= 'tip'
|
68
|
+
entries = []
|
69
|
+
manifest_entries = repository.manifest.scan_for_path(path, revision)
|
70
|
+
manifest_entries.each do |me|
|
71
|
+
path_without_source = me[3].gsub(/^#{ Regexp.escape(path.without_trailing_slash) }\//, '')
|
72
|
+
entry_name = path_without_source.split('/').first
|
73
|
+
entry_path = File.join(path, entry_name).gsub(/^\//, '')
|
74
|
+
dir = me[3].scan(/^(#{ Regexp.escape(entry_path) }\/)/).flatten.first ? true : false
|
75
|
+
entry_name << '/' if dir
|
76
|
+
|
77
|
+
entries << build(
|
78
|
+
:path => entry_path,
|
79
|
+
:name => entry_name,
|
80
|
+
:revision => revision,
|
81
|
+
:nodeid => (me[0] unless dir),
|
82
|
+
:fmode => dir ? nil : me[1],
|
83
|
+
:exec => dir ? nil : me[2],
|
84
|
+
:parent => parent
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
entries = entries.inject({}) do |hash,item|
|
89
|
+
hash[item.name] ||= item
|
90
|
+
hash
|
91
|
+
end.values
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def build(opts={})
|
97
|
+
Mercurial::Node.new(
|
98
|
+
:repository => repository,
|
99
|
+
:path => opts[:path],
|
100
|
+
:name => opts[:name],
|
101
|
+
:revision => opts[:revision],
|
102
|
+
:nodeid => opts[:nodeid],
|
103
|
+
:fmode => opts[:fmode],
|
104
|
+
:executable => opts[:exec],
|
105
|
+
:parent => opts[:parent]
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Mercurial
|
2
|
+
|
3
|
+
#
|
4
|
+
# This class represents a factory for {Mercurial::Tag Tag} instances.
|
5
|
+
#
|
6
|
+
class TagFactory
|
7
|
+
include Mercurial::Helper
|
8
|
+
|
9
|
+
# Instance of {Mercurial::Repository Repository}.
|
10
|
+
attr_reader :repository
|
11
|
+
|
12
|
+
def initialize(repository)
|
13
|
+
@repository = repository
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return an array of {Mercurial::Tag Tag} instances for all tags in the repository.
|
17
|
+
#
|
18
|
+
# == Example:
|
19
|
+
# repository.tags.all
|
20
|
+
#
|
21
|
+
def all
|
22
|
+
hg_to_array "tags" do |line|
|
23
|
+
build(line)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return a {Mercurial::Tag Tag} instance for a tag with a specified name.
|
28
|
+
#
|
29
|
+
# == Example:
|
30
|
+
# repository.tags.by_name('tagname')
|
31
|
+
#
|
32
|
+
def by_name(name)
|
33
|
+
all.find do |b|
|
34
|
+
b.name == name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build(data)
|
41
|
+
name, hash_id = *data.scan(/([\w-]+)\s+\d+:(\w+)\s*/).first
|
42
|
+
return if name == 'tip'
|
43
|
+
Mercurial::Tag.new(repository, name, hash_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module Mercurial
|
2
|
+
|
3
|
+
# This class was ported from grit.
|
4
|
+
#
|
5
|
+
# This implements a file-based 'file index', an simple index of
|
6
|
+
# all of the reachable commits in a repo, along with the parents
|
7
|
+
# and which files were modified during each commit.
|
8
|
+
#
|
9
|
+
# This class creates and reads a file named '[.hg]/file-index'.
|
10
|
+
#
|
11
|
+
class FileIndex
|
12
|
+
include Mercurial::Helper
|
13
|
+
|
14
|
+
class IndexFileNotFound < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class UnsupportedRef < StandardError
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_accessor :max_file_size
|
22
|
+
end
|
23
|
+
|
24
|
+
self.max_file_size = 10_000_000 # ~10M
|
25
|
+
|
26
|
+
attr_reader :repository
|
27
|
+
|
28
|
+
# initializes index given repository
|
29
|
+
def initialize(repository)
|
30
|
+
@repository = repository
|
31
|
+
end
|
32
|
+
|
33
|
+
def reload
|
34
|
+
@_read_complete = false
|
35
|
+
end
|
36
|
+
|
37
|
+
# updates file index
|
38
|
+
def update(oldrev=nil, newrev=nil)
|
39
|
+
if index_file_exists? && oldrev != "0"*40
|
40
|
+
hg([
|
41
|
+
"log --debug -r ?:? --style ? >> ?",
|
42
|
+
oldrev, newrev, Style.file_index, path
|
43
|
+
])
|
44
|
+
else
|
45
|
+
hg(["log --debug -r : --style ? > ?", Style.file_index, path])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# returns count of all commits
|
50
|
+
def count_all
|
51
|
+
read_if_needed
|
52
|
+
@sha_count
|
53
|
+
end
|
54
|
+
|
55
|
+
# returns count of all commits reachable from SHA
|
56
|
+
# note: originally did this recursively, but ruby gets pissed about that on
|
57
|
+
# really big repos where the stack level gets 'too deep' (thats what she said)
|
58
|
+
def count(commit_sha)
|
59
|
+
read_if_needed
|
60
|
+
commits_from(commit_sha).size
|
61
|
+
end
|
62
|
+
|
63
|
+
# builds a list of all commits reachable from a single commit
|
64
|
+
def commits_from(commit_sha)
|
65
|
+
raise UnsupportedRef if commit_sha.is_a? Array
|
66
|
+
read_if_needed
|
67
|
+
|
68
|
+
already = {}
|
69
|
+
final = []
|
70
|
+
left_to_do = [commit_sha]
|
71
|
+
|
72
|
+
while commit_sha = left_to_do.shift
|
73
|
+
next if already[commit_sha]
|
74
|
+
|
75
|
+
final << commit_sha
|
76
|
+
already[commit_sha] = true
|
77
|
+
|
78
|
+
commit = @commit_index[commit_sha]
|
79
|
+
commit[:parents].each do |sha|
|
80
|
+
left_to_do << sha
|
81
|
+
end if commit
|
82
|
+
end
|
83
|
+
|
84
|
+
sort_commits(final)
|
85
|
+
end
|
86
|
+
|
87
|
+
# returns files changed at commit sha
|
88
|
+
def files(commit_sha)
|
89
|
+
read_if_needed
|
90
|
+
if commit = @commit_index[commit_sha]
|
91
|
+
commit[:files]
|
92
|
+
else
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# returns all commits for a file
|
98
|
+
def commits_for(file)
|
99
|
+
read_if_needed
|
100
|
+
@all_files[file] || []
|
101
|
+
end
|
102
|
+
|
103
|
+
# returns the shas of the last commits for all
|
104
|
+
# the files in [] from commit_sha
|
105
|
+
# files_matcher can be a regexp or an array
|
106
|
+
def last_commits(commit_sha, files_matcher)
|
107
|
+
read_if_needed
|
108
|
+
acceptable = commits_from(commit_sha)
|
109
|
+
|
110
|
+
matches = {}
|
111
|
+
|
112
|
+
if files_matcher.is_a? Regexp
|
113
|
+
files = @all_files.keys.select { |file| file =~ files_matcher }
|
114
|
+
files_matcher = files
|
115
|
+
end
|
116
|
+
|
117
|
+
if files_matcher.is_a? Array
|
118
|
+
# find the last commit for each file in the array
|
119
|
+
files_matcher.each do |f|
|
120
|
+
@all_files[f].each do |try|
|
121
|
+
if acceptable.include?(try)
|
122
|
+
matches[f] = try
|
123
|
+
break
|
124
|
+
end
|
125
|
+
end if @all_files[f]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
matches
|
130
|
+
end
|
131
|
+
|
132
|
+
def destroy!
|
133
|
+
FileUtils.rm_f(path)
|
134
|
+
end
|
135
|
+
|
136
|
+
def path
|
137
|
+
File.join(repository.dothg_path, 'file-index')
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def index_file_exists?
|
143
|
+
FileTest.exists?(path)
|
144
|
+
end
|
145
|
+
|
146
|
+
def index_file_valid?
|
147
|
+
File.file?(path) && (File.size(path) < Mercurial::FileIndex.max_file_size)
|
148
|
+
end
|
149
|
+
|
150
|
+
def sort_commits(sha_array)
|
151
|
+
read_if_needed
|
152
|
+
sha_array.sort { |a, b| @commit_order[b].to_i <=> @commit_order[a].to_i }
|
153
|
+
end
|
154
|
+
|
155
|
+
def read_if_needed
|
156
|
+
if @_read_complete
|
157
|
+
true
|
158
|
+
else
|
159
|
+
begin
|
160
|
+
read_index
|
161
|
+
rescue IndexFileNotFound => e
|
162
|
+
if @_tried_updating
|
163
|
+
raise e
|
164
|
+
else
|
165
|
+
@_tried_updating = true
|
166
|
+
update
|
167
|
+
retry
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def read_index
|
174
|
+
raise IndexFileNotFound unless index_file_valid?
|
175
|
+
f = File.new(path)
|
176
|
+
@sha_count = 0
|
177
|
+
@commit_index = {}
|
178
|
+
@commit_order = {}
|
179
|
+
@all_files = {}
|
180
|
+
while line = f.gets
|
181
|
+
if /^(\w{40})/.match(line)
|
182
|
+
shas = line.scan(/(\w{40})/)
|
183
|
+
current_sha = shas.shift.first
|
184
|
+
parents = shas.map { |sha| sha.first }
|
185
|
+
parents = parents.delete_if { |sha| sha == '0'*40 }
|
186
|
+
@commit_index[current_sha] = {:files => [], :parents => parents }
|
187
|
+
@commit_order[current_sha] = @sha_count
|
188
|
+
@sha_count += 1
|
189
|
+
else
|
190
|
+
file_name = line.chomp
|
191
|
+
unless file_name.empty?
|
192
|
+
tree = ''
|
193
|
+
File.dirname(file_name).split('/').each do |part|
|
194
|
+
next if part == '.'
|
195
|
+
tree += part + '/'
|
196
|
+
@all_files[tree] ||= []
|
197
|
+
@all_files[tree].unshift(current_sha)
|
198
|
+
end
|
199
|
+
@all_files[file_name] ||= []
|
200
|
+
@all_files[file_name].unshift(current_sha)
|
201
|
+
@commit_index[current_sha][:files] << file_name
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
@_read_complete = true
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|