amp 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/.hgignore +3 -0
- data/AUTHORS +1 -1
- data/Manifest.txt +99 -38
- data/README.md +3 -3
- data/Rakefile +53 -18
- data/SCHEDULE.markdown +5 -1
- data/TODO.markdown +120 -149
- data/ampfile.rb +3 -1
- data/bin/amp +4 -1
- data/ext/amp/bz2/extconf.rb +1 -1
- data/ext/amp/mercurial_patch/extconf.rb +1 -1
- data/ext/amp/mercurial_patch/mpatch.c +4 -3
- data/ext/amp/priority_queue/extconf.rb +1 -1
- data/ext/amp/support/extconf.rb +1 -1
- data/ext/amp/support/support.c +1 -1
- data/lib/amp.rb +125 -67
- data/lib/amp/commands/command.rb +12 -10
- data/lib/amp/commands/command_support.rb +8 -1
- data/lib/amp/commands/commands/help.rb +2 -20
- data/lib/amp/commands/commands/init.rb +14 -2
- data/lib/amp/commands/commands/templates.rb +6 -4
- data/lib/amp/commands/commands/version.rb +15 -1
- data/lib/amp/commands/commands/workflow.rb +3 -3
- data/lib/amp/commands/commands/workflows/git/add.rb +3 -3
- data/lib/amp/commands/commands/workflows/git/copy.rb +1 -1
- data/lib/amp/commands/commands/workflows/git/rm.rb +4 -2
- data/lib/amp/commands/commands/workflows/hg/add.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/addremove.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/annotate.rb +8 -2
- data/lib/amp/commands/commands/workflows/hg/bisect.rb +253 -0
- data/lib/amp/commands/commands/workflows/hg/branch.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/branches.rb +3 -3
- data/lib/amp/commands/commands/workflows/hg/bundle.rb +3 -3
- data/lib/amp/commands/commands/workflows/hg/clone.rb +4 -5
- data/lib/amp/commands/commands/workflows/hg/commit.rb +37 -1
- data/lib/amp/commands/commands/workflows/hg/copy.rb +2 -1
- data/lib/amp/commands/commands/workflows/hg/debug/index.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/diff.rb +3 -8
- data/lib/amp/commands/commands/workflows/hg/forget.rb +5 -4
- data/lib/amp/commands/commands/workflows/hg/identify.rb +6 -6
- data/lib/amp/commands/commands/workflows/hg/import.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/incoming.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/log.rb +5 -4
- data/lib/amp/commands/commands/workflows/hg/merge.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/move.rb +5 -3
- data/lib/amp/commands/commands/workflows/hg/outgoing.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/push.rb +6 -7
- data/lib/amp/commands/commands/workflows/hg/remove.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/resolve.rb +6 -23
- data/lib/amp/commands/commands/workflows/hg/root.rb +1 -2
- data/lib/amp/commands/commands/workflows/hg/status.rb +21 -12
- data/lib/amp/commands/commands/workflows/hg/tag.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/untrack.rb +12 -0
- data/lib/amp/commands/commands/workflows/hg/verify.rb +13 -3
- data/lib/amp/commands/commands/workflows/hg/what_changed.rb +18 -0
- data/lib/amp/commands/dispatch.rb +12 -13
- data/lib/amp/dependencies/amp_support.rb +1 -1
- data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +1 -0
- data/lib/amp/dependencies/maruku.rb +136 -0
- data/lib/amp/dependencies/maruku/attributes.rb +227 -0
- data/lib/amp/dependencies/maruku/defaults.rb +71 -0
- data/lib/amp/dependencies/maruku/errors_management.rb +92 -0
- data/lib/amp/dependencies/maruku/helpers.rb +260 -0
- data/lib/amp/dependencies/maruku/input/charsource.rb +326 -0
- data/lib/amp/dependencies/maruku/input/extensions.rb +69 -0
- data/lib/amp/dependencies/maruku/input/html_helper.rb +189 -0
- data/lib/amp/dependencies/maruku/input/linesource.rb +111 -0
- data/lib/amp/dependencies/maruku/input/parse_block.rb +615 -0
- data/lib/amp/dependencies/maruku/input/parse_doc.rb +234 -0
- data/lib/amp/dependencies/maruku/input/parse_span_better.rb +746 -0
- data/lib/amp/dependencies/maruku/input/rubypants.rb +225 -0
- data/lib/amp/dependencies/maruku/input/type_detection.rb +147 -0
- data/lib/amp/dependencies/maruku/input_textile2/t2_parser.rb +163 -0
- data/lib/amp/dependencies/maruku/maruku.rb +33 -0
- data/lib/amp/dependencies/maruku/output/to_ansi.rb +223 -0
- data/lib/amp/dependencies/maruku/output/to_html.rb +991 -0
- data/lib/amp/dependencies/maruku/output/to_markdown.rb +164 -0
- data/lib/amp/dependencies/maruku/output/to_s.rb +56 -0
- data/lib/amp/dependencies/maruku/string_utils.rb +191 -0
- data/lib/amp/dependencies/maruku/structures.rb +167 -0
- data/lib/amp/dependencies/maruku/structures_inspect.rb +87 -0
- data/lib/amp/dependencies/maruku/structures_iterators.rb +61 -0
- data/lib/amp/dependencies/maruku/textile2.rb +1 -0
- data/lib/amp/dependencies/maruku/toc.rb +199 -0
- data/lib/amp/dependencies/maruku/usage/example1.rb +33 -0
- data/lib/amp/dependencies/maruku/version.rb +40 -0
- data/lib/amp/dependencies/priority_queue.rb +2 -1
- data/lib/amp/dependencies/python_config.rb +2 -1
- data/lib/amp/graphs/ancestor.rb +2 -1
- data/lib/amp/graphs/copies.rb +236 -233
- data/lib/amp/help/entries/__default__.erb +31 -0
- data/lib/amp/help/entries/commands.erb +6 -0
- data/lib/amp/help/entries/mdtest.md +35 -0
- data/lib/amp/help/entries/silly +3 -0
- data/lib/amp/help/help.rb +288 -0
- data/lib/amp/profiling_hacks.rb +5 -3
- data/lib/amp/repository/abstract/abstract_changeset.rb +97 -0
- data/lib/amp/repository/abstract/abstract_local_repo.rb +181 -0
- data/lib/amp/repository/abstract/abstract_staging_area.rb +180 -0
- data/lib/amp/repository/abstract/abstract_versioned_file.rb +100 -0
- data/lib/amp/repository/abstract/common_methods/changeset.rb +75 -0
- data/lib/amp/repository/abstract/common_methods/local_repo.rb +277 -0
- data/lib/amp/repository/abstract/common_methods/staging_area.rb +233 -0
- data/lib/amp/repository/abstract/common_methods/versioned_file.rb +71 -0
- data/lib/amp/repository/generic_repo_picker.rb +78 -0
- data/lib/amp/repository/git/repo_format/changeset.rb +336 -0
- data/lib/amp/repository/git/repo_format/staging_area.rb +192 -0
- data/lib/amp/repository/git/repo_format/versioned_file.rb +119 -0
- data/lib/amp/repository/git/repositories/local_repository.rb +164 -0
- data/lib/amp/repository/git/repository.rb +41 -0
- data/lib/amp/repository/mercurial/encoding/mercurial_diff.rb +382 -0
- data/lib/amp/repository/mercurial/encoding/mercurial_patch.rb +1 -0
- data/lib/amp/repository/mercurial/encoding/patch.rb +294 -0
- data/lib/amp/repository/mercurial/encoding/pure_ruby/ruby_mercurial_patch.rb +124 -0
- data/lib/amp/repository/mercurial/merging/merge_ui.rb +327 -0
- data/lib/amp/repository/mercurial/merging/simple_merge.rb +452 -0
- data/lib/amp/repository/mercurial/repo_format/branch_manager.rb +266 -0
- data/lib/amp/repository/mercurial/repo_format/changeset.rb +768 -0
- data/lib/amp/repository/mercurial/repo_format/dir_state.rb +716 -0
- data/lib/amp/repository/mercurial/repo_format/journal.rb +218 -0
- data/lib/amp/repository/mercurial/repo_format/lock.rb +210 -0
- data/lib/amp/repository/mercurial/repo_format/merge_state.rb +228 -0
- data/lib/amp/repository/mercurial/repo_format/staging_area.rb +367 -0
- data/lib/amp/repository/mercurial/repo_format/store.rb +487 -0
- data/lib/amp/repository/mercurial/repo_format/tag_manager.rb +322 -0
- data/lib/amp/repository/mercurial/repo_format/updatable.rb +543 -0
- data/lib/amp/repository/mercurial/repo_format/updater.rb +848 -0
- data/lib/amp/repository/mercurial/repo_format/verification.rb +433 -0
- data/lib/amp/repository/mercurial/repositories/bundle_repository.rb +216 -0
- data/lib/amp/repository/mercurial/repositories/http_repository.rb +386 -0
- data/lib/amp/repository/mercurial/repositories/local_repository.rb +2034 -0
- data/lib/amp/repository/mercurial/repository.rb +119 -0
- data/lib/amp/repository/mercurial/revlogs/bundle_revlogs.rb +249 -0
- data/lib/amp/repository/mercurial/revlogs/changegroup.rb +217 -0
- data/lib/amp/repository/mercurial/revlogs/changelog.rb +339 -0
- data/lib/amp/repository/mercurial/revlogs/file_log.rb +152 -0
- data/lib/amp/repository/mercurial/revlogs/index.rb +500 -0
- data/lib/amp/repository/mercurial/revlogs/manifest.rb +201 -0
- data/lib/amp/repository/mercurial/revlogs/node.rb +20 -0
- data/lib/amp/repository/mercurial/revlogs/revlog.rb +1026 -0
- data/lib/amp/repository/mercurial/revlogs/revlog_support.rb +129 -0
- data/lib/amp/repository/mercurial/revlogs/versioned_file.rb +597 -0
- data/lib/amp/repository/repository.rb +11 -88
- data/lib/amp/server/extension/amp_extension.rb +3 -3
- data/lib/amp/server/fancy_http_server.rb +1 -1
- data/lib/amp/server/fancy_views/_browser.haml +1 -1
- data/lib/amp/server/fancy_views/_diff_file.haml +1 -8
- data/lib/amp/server/fancy_views/changeset.haml +2 -2
- data/lib/amp/server/fancy_views/file.haml +1 -1
- data/lib/amp/server/fancy_views/file_diff.haml +1 -1
- data/lib/amp/support/amp_ui.rb +13 -29
- data/lib/amp/support/generator.rb +1 -1
- data/lib/amp/support/loaders.rb +1 -2
- data/lib/amp/support/logger.rb +10 -16
- data/lib/amp/support/match.rb +18 -4
- data/lib/amp/support/mercurial/ignore.rb +151 -0
- data/lib/amp/support/openers.rb +8 -3
- data/lib/amp/support/support.rb +91 -46
- data/lib/amp/templates/{blank.commit.erb → mercurial/blank.commit.erb} +0 -0
- data/lib/amp/templates/{blank.log.erb → mercurial/blank.log.erb} +0 -0
- data/lib/amp/templates/{default.commit.erb → mercurial/default.commit.erb} +0 -0
- data/lib/amp/templates/{default.log.erb → mercurial/default.log.erb} +0 -0
- data/lib/amp/templates/template.rb +18 -18
- data/man/amp.1 +51 -0
- data/site/src/about/commands.haml +1 -1
- data/site/src/css/amp.css +1 -1
- data/site/src/index.haml +3 -3
- data/tasks/man.rake +39 -0
- data/tasks/stats.rake +1 -10
- data/tasks/yard.rake +1 -50
- data/test/dirstate_tests/test_dir_state.rb +10 -8
- data/test/functional_tests/annotate.out +31 -0
- data/test/functional_tests/test_functional.rb +155 -63
- data/test/localrepo_tests/ampfile.rb +12 -0
- data/test/localrepo_tests/test_local_repo.rb +56 -57
- data/test/manifest_tests/test_manifest.rb +3 -5
- data/test/merge_tests/test_merge.rb +3 -3
- data/test/revlog_tests/test_revlog.rb +14 -6
- data/test/store_tests/test_fncache_store.rb +19 -19
- data/test/test_19_compatibility.rb +46 -0
- data/test/test_base85.rb +2 -2
- data/test/test_bdiff.rb +2 -2
- data/test/test_changegroup.rb +59 -0
- data/test/test_commands.rb +2 -2
- data/test/test_difflib.rb +2 -2
- data/test/test_generator.rb +34 -0
- data/test/test_ignore.rb +203 -0
- data/test/test_journal.rb +18 -13
- data/test/test_match.rb +2 -2
- data/test/test_mdiff.rb +3 -3
- data/test/test_mpatch.rb +3 -3
- data/test/test_multi_io.rb +40 -0
- data/test/test_support.rb +18 -2
- data/test/test_templates.rb +38 -0
- data/test/test_ui.rb +79 -0
- data/test/testutilities.rb +56 -0
- metadata +168 -49
- data/ext/amp/bz2/mkmf.log +0 -38
- data/lib/amp/encoding/mercurial_diff.rb +0 -378
- data/lib/amp/encoding/mercurial_patch.rb +0 -1
- data/lib/amp/encoding/patch.rb +0 -292
- data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +0 -123
- data/lib/amp/merges/merge_state.rb +0 -164
- data/lib/amp/merges/merge_ui.rb +0 -322
- data/lib/amp/merges/simple_merge.rb +0 -450
- data/lib/amp/repository/branch_manager.rb +0 -234
- data/lib/amp/repository/dir_state.rb +0 -950
- data/lib/amp/repository/journal.rb +0 -203
- data/lib/amp/repository/lock.rb +0 -207
- data/lib/amp/repository/repositories/bundle_repository.rb +0 -214
- data/lib/amp/repository/repositories/http_repository.rb +0 -377
- data/lib/amp/repository/repositories/local_repository.rb +0 -2661
- data/lib/amp/repository/store.rb +0 -485
- data/lib/amp/repository/tag_manager.rb +0 -319
- data/lib/amp/repository/updatable.rb +0 -532
- data/lib/amp/repository/verification.rb +0 -431
- data/lib/amp/repository/versioned_file.rb +0 -475
- data/lib/amp/revlogs/bundle_revlogs.rb +0 -246
- data/lib/amp/revlogs/changegroup.rb +0 -217
- data/lib/amp/revlogs/changelog.rb +0 -338
- data/lib/amp/revlogs/changeset.rb +0 -521
- data/lib/amp/revlogs/file_log.rb +0 -165
- data/lib/amp/revlogs/index.rb +0 -493
- data/lib/amp/revlogs/manifest.rb +0 -195
- data/lib/amp/revlogs/node.rb +0 -18
- data/lib/amp/revlogs/revlog.rb +0 -1045
- data/lib/amp/revlogs/revlog_support.rb +0 -126
- data/lib/amp/support/ignore.rb +0 -144
- data/site/Rakefile +0 -38
- data/test/test_amp.rb +0 -9
- data/test/test_helper.rb +0 -15
@@ -1,203 +0,0 @@
|
|
1
|
-
module Amp
|
2
|
-
class StandardErrorReporter
|
3
|
-
def self.report str
|
4
|
-
STDERR.puts str
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
##
|
9
|
-
# Provides a journal interface so when a large number of transactions
|
10
|
-
# are occurring, and any one could fail, we can rollback the changes.
|
11
|
-
class Journal
|
12
|
-
DEFAULT_OPTS = {:reporter => StandardErrorReporter, :after_close => nil}
|
13
|
-
|
14
|
-
attr_accessor :report, :journal, :after_close
|
15
|
-
|
16
|
-
##
|
17
|
-
# @return [Amp::Journal]
|
18
|
-
def self.start(file, opts=DEFAULT_OPTS)
|
19
|
-
journal = Journal.new opts[:reporter], file, &opts[:after_close]
|
20
|
-
|
21
|
-
if block_given?
|
22
|
-
begin
|
23
|
-
yield journal
|
24
|
-
ensure
|
25
|
-
journal.close
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
journal
|
30
|
-
end
|
31
|
-
|
32
|
-
##
|
33
|
-
# Initializes the journal to get ready for some transactions.
|
34
|
-
#
|
35
|
-
# @param [#report] reporter an object that will keep track of any alerts
|
36
|
-
# we have to send out. Must respond to #report.
|
37
|
-
# @param [String] journal the path to the journal file to use
|
38
|
-
# @param [Integer] createmode An octal number that sets the filemode
|
39
|
-
# of the journal file we'll be using
|
40
|
-
# @param [Proc] after_close A proc to call (with no args) after we
|
41
|
-
# close (finish) the transaction.
|
42
|
-
def initialize(reporter=StandardErrorReporter, journal="journal#{rand(10000)}", createmode=nil, &after_close)
|
43
|
-
@count = 1
|
44
|
-
@reporter = reporter
|
45
|
-
@after_close = after_close
|
46
|
-
@entries = []
|
47
|
-
@map = {}
|
48
|
-
@journal_file = journal
|
49
|
-
|
50
|
-
@file = open(@journal_file, "w")
|
51
|
-
|
52
|
-
FileUtils.chmod(createmode & 0666, @journal_file) unless createmode.nil?
|
53
|
-
end
|
54
|
-
|
55
|
-
##
|
56
|
-
# Kills the journal - used when shit goes down and we gotta give up
|
57
|
-
# on the transactions.
|
58
|
-
def delete
|
59
|
-
if @journal_file
|
60
|
-
abort if @entries.any?
|
61
|
-
@file.close
|
62
|
-
FileUtils.safe_unlink @journal_file
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
##
|
67
|
-
# Adds an entry to the journal. Since all our files are just being appended
|
68
|
-
# to all the time, all we really need is to keep track of how long the file
|
69
|
-
# was when we last knew it to be safe. In other words, if the file started
|
70
|
-
# off at 20 bytes, then an error happened, we just truncate it to 20 bytes.
|
71
|
-
#
|
72
|
-
# All params should be contained in the array
|
73
|
-
#
|
74
|
-
# @param file the name of the file we're modifying and need to track
|
75
|
-
# @param offset the length of the file we're storing
|
76
|
-
# @param data any extra data to hold onto
|
77
|
-
def add_entry(array)
|
78
|
-
file, offset, data = array[0], array[1], array[2]
|
79
|
-
return if @map[file]
|
80
|
-
@entries << {:file => file, :offset => offset, :data => data}
|
81
|
-
@map[file] = @entries.size - 1
|
82
|
-
|
83
|
-
# tell the journal how to truncate this revision
|
84
|
-
@file.write("#{file}\0#{offset}\n")
|
85
|
-
@file.flush
|
86
|
-
end
|
87
|
-
|
88
|
-
##
|
89
|
-
# Alias for {add_entry}
|
90
|
-
alias :<< :add_entry
|
91
|
-
|
92
|
-
##
|
93
|
-
# Finds the entry for a given file's path
|
94
|
-
# @param [String] file the path to the file
|
95
|
-
# @return [Hash] A hash with the values :file, :offset, and :data, as
|
96
|
-
# they were when they were stored by {add_entry} or {update}
|
97
|
-
def find_file(file)
|
98
|
-
return @entries[@map[file]] if @map[file]
|
99
|
-
nil
|
100
|
-
end
|
101
|
-
|
102
|
-
##
|
103
|
-
# Alias for {find_file}
|
104
|
-
alias :find :find_file
|
105
|
-
|
106
|
-
##
|
107
|
-
# Updates an entry's data, based on the filename. The file must already
|
108
|
-
# have been journaled.
|
109
|
-
#
|
110
|
-
# @param [String] file the file to update
|
111
|
-
# @param [Fixnum] offset the new offset to store
|
112
|
-
# @param [String] data the new data to store
|
113
|
-
def replace(file, offset, data=nil)
|
114
|
-
raise IndexError.new("journal lookup failed #{file}") unless @map[file]
|
115
|
-
index = @map[file]
|
116
|
-
@entries[index] = {:file => file, :offset => offset, :data => data}
|
117
|
-
@file.write("#{file}\0#{offset}\n")
|
118
|
-
@file.flush
|
119
|
-
end
|
120
|
-
|
121
|
-
##
|
122
|
-
# Alias for {replace}
|
123
|
-
alias :update :replace
|
124
|
-
|
125
|
-
##
|
126
|
-
# No godly idea what this is for
|
127
|
-
def nest
|
128
|
-
@count += 1
|
129
|
-
self
|
130
|
-
end
|
131
|
-
|
132
|
-
##
|
133
|
-
# Is the journal running right now?
|
134
|
-
def running?
|
135
|
-
@count > 0
|
136
|
-
end
|
137
|
-
|
138
|
-
##
|
139
|
-
# Closes up the journal. Will call the after_close proc passed
|
140
|
-
# during instantiation.
|
141
|
-
def close
|
142
|
-
@count -= 1
|
143
|
-
return if @count != 0
|
144
|
-
@file.close
|
145
|
-
@entries = []
|
146
|
-
if @after_close
|
147
|
-
@after_close.call
|
148
|
-
else
|
149
|
-
FileUtils.safe_unlink(@journal_file)
|
150
|
-
end
|
151
|
-
@journal_file = nil
|
152
|
-
end
|
153
|
-
|
154
|
-
##
|
155
|
-
# Abort, abort! abandon ship! This rolls back any changes we've made
|
156
|
-
# during the current journalling session.
|
157
|
-
def abort
|
158
|
-
return unless @entries && @entries.any?
|
159
|
-
@reporter.report "transaction abort!\n"
|
160
|
-
@entries.each do |hash|
|
161
|
-
file, offset = hash[:file], hash[:offset]
|
162
|
-
begin
|
163
|
-
fp = open(File.join(".hg","store",file), "a")
|
164
|
-
fp.truncate offset
|
165
|
-
fp.close
|
166
|
-
rescue
|
167
|
-
@reporter.report "Failed to truncate #{File.join(".hg","store",file)}\n"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
@entries = []
|
171
|
-
@reporter.report "rollback completed\n"
|
172
|
-
end
|
173
|
-
|
174
|
-
##
|
175
|
-
# If we crashed during an abort, the journal file is gonna be sitting aorund
|
176
|
-
# somewhere. So, we should rollback any changes it left lying around.
|
177
|
-
#
|
178
|
-
# @param [String] file the journal file to use during the rollback
|
179
|
-
def self.rollback(file)
|
180
|
-
files = {}
|
181
|
-
fp = open(file)
|
182
|
-
fp.each_line do |line|
|
183
|
-
file, offset = line.split("\0")
|
184
|
-
files[file] = offset.to_i
|
185
|
-
end
|
186
|
-
fp.close
|
187
|
-
files.each do |file, offset|
|
188
|
-
if o > 0
|
189
|
-
fp = open(file, "a")
|
190
|
-
fp.truncate o.to_i
|
191
|
-
fp.close
|
192
|
-
else
|
193
|
-
fp = open(f)
|
194
|
-
fn = fp.path
|
195
|
-
fp.close
|
196
|
-
FileUtils.safe_unlink fn
|
197
|
-
end
|
198
|
-
end
|
199
|
-
FileUtils.safe_unlink file
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
data/lib/amp/repository/lock.rb
DELETED
@@ -1,207 +0,0 @@
|
|
1
|
-
module Amp
|
2
|
-
module Repositories
|
3
|
-
##
|
4
|
-
# = Lock
|
5
|
-
# Manages a given lock file, indicating that the enclosing folder should not
|
6
|
-
# be modified. Typically used during destructive operations on a repo (such as
|
7
|
-
# a commit or push).
|
8
|
-
#
|
9
|
-
# We must be compatible with Mercurial's lock format, unfortunately. Doesn't life
|
10
|
-
# suck?
|
11
|
-
#####
|
12
|
-
##### From Mercurial code, explaining their format:
|
13
|
-
#####
|
14
|
-
#
|
15
|
-
# lock is symlink on platforms that support it, file on others.
|
16
|
-
#
|
17
|
-
# symlink is used because create of directory entry and contents
|
18
|
-
# are atomic even over nfs.
|
19
|
-
#
|
20
|
-
# old-style lock: symlink to pid
|
21
|
-
# new-style lock: symlink to hostname:pid
|
22
|
-
class Lock
|
23
|
-
@@host = nil
|
24
|
-
|
25
|
-
##
|
26
|
-
# Initializes the lock to a given file name, and creates the lock, effectively
|
27
|
-
# locking the containing directory.
|
28
|
-
#
|
29
|
-
# @param [String] file the path to the the lock file to create
|
30
|
-
# @param [Hash<Symbol => Object>] opts the options to use when creating the lock
|
31
|
-
# @option [Integer] options :timeout (-1) the length of time to keep trying to create the lock.
|
32
|
-
# defaults to -1 (indefinitely)
|
33
|
-
# @option [Proc, #call] options :release_fxn (nil) A proc to run when the
|
34
|
-
# lock is released
|
35
|
-
# @option [String] options :desc (nil) A description of the lock
|
36
|
-
def initialize(file, opts={:timeout => -1})
|
37
|
-
@file = file
|
38
|
-
@held = false
|
39
|
-
@timeout = opts[:timeout]
|
40
|
-
@release_fxn = opts[:release_fxn]
|
41
|
-
@description = opts[:desc]
|
42
|
-
apply_lock
|
43
|
-
end
|
44
|
-
|
45
|
-
##
|
46
|
-
# Applies the lock. Will sleep the thread for +timeout+ time trying to apply the lock before
|
47
|
-
# giving up and raising an error.
|
48
|
-
def apply_lock
|
49
|
-
timeout = @timeout
|
50
|
-
while true do
|
51
|
-
begin
|
52
|
-
# try_lock will raise of there is already a lock.
|
53
|
-
try_lock
|
54
|
-
return true
|
55
|
-
rescue LockHeld => e
|
56
|
-
# We'll put up with this exception for @timeout times, then give up.
|
57
|
-
if timeout != 0
|
58
|
-
sleep(1)
|
59
|
-
timeout > 0 && timeout -= 1
|
60
|
-
next
|
61
|
-
end
|
62
|
-
# Timeout's up? Raise an exception.
|
63
|
-
raise LockHeld.new(Errno::ETIMEDOUT::Errno, e.filename, @desc, e.locker)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
##
|
69
|
-
# Attempts to apply the lock. Raises if unsuccessful. Contains the logic for actually naming
|
70
|
-
# the lock.
|
71
|
-
def try_lock
|
72
|
-
if @@host.nil?
|
73
|
-
@@host = Socket.gethostname
|
74
|
-
end
|
75
|
-
lockname = "#{@@host}:#{Process.pid}"
|
76
|
-
while !@held
|
77
|
-
begin
|
78
|
-
make_a_lock(@file, lockname)
|
79
|
-
@held = true
|
80
|
-
rescue Errno::EEXIST
|
81
|
-
locker = test_lock
|
82
|
-
unless locker.nil?
|
83
|
-
raise LockHeld.new(Errno::EAGAIN::Errno, @file, @desc, locker)
|
84
|
-
end
|
85
|
-
rescue SystemCallError => e
|
86
|
-
raise LockUnavailable.new(e.errno, e.to_s, @file, @desc)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
##
|
92
|
-
# Creates a lock at the given location, with info about the locking process. Uses
|
93
|
-
# a symlink if possible, because even over NFS, creating a symlink is atomic. Nice.
|
94
|
-
# Otherwise, it will call make_a_lock_in_file on inferior OS's (cough windows cough)
|
95
|
-
# and put the data in there.
|
96
|
-
#
|
97
|
-
# The symlink is actually a non-working symlink - it points the filename (such as "hglock")
|
98
|
-
# to the data, even though the data is not an actual file. So hglock -> "medgar:25043" is
|
99
|
-
# a sort-of possible lock this method would create.
|
100
|
-
#
|
101
|
-
# @param [String] file the filename of the lock
|
102
|
-
# @param [String] info the info to store in the lock
|
103
|
-
def make_a_lock(file, info)
|
104
|
-
begin
|
105
|
-
File.symlink(info, file)
|
106
|
-
rescue Errno::EEXIST
|
107
|
-
raise
|
108
|
-
rescue
|
109
|
-
make_a_lock_in_file(file, info)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
##
|
114
|
-
# Creates a lock at the given location, storing the info about the locking process in
|
115
|
-
# an actual lock file. These locks are not preferred, because symlinks are atomic even
|
116
|
-
# over NFS. Anyway, very simple. Create the file, write in the info, close 'er up.
|
117
|
-
# That's 1 line in ruby, folks.
|
118
|
-
#
|
119
|
-
# @see make_a_lock
|
120
|
-
# @param [String] file the filename of the lock
|
121
|
-
# @param [String] info the info to store in the lock
|
122
|
-
def make_a_lock_in_file(file, info)
|
123
|
-
File.open(file, "w+") {|out| out.write info }
|
124
|
-
end
|
125
|
-
|
126
|
-
##
|
127
|
-
# Reads in the data associated with a lock file.
|
128
|
-
#
|
129
|
-
# @param [String] file the path to the lock file to read
|
130
|
-
# @return [String] the data in the lock. In the format "#{locking_host}:#{locking_pid}"
|
131
|
-
def read_lock(file)
|
132
|
-
begin
|
133
|
-
return File.readlink(file)
|
134
|
-
rescue Errno::EINVAL, Errno::ENOSYS
|
135
|
-
return File.read(file)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
##
|
140
|
-
# Checks to see if there is a process running with id +pid+.
|
141
|
-
#
|
142
|
-
# @param [Fixnum] pid the process ID to look up
|
143
|
-
# @return [Boolean] is there a process with the given pid?
|
144
|
-
def test_pid(pid)
|
145
|
-
return true if Platform::OS == :vms
|
146
|
-
|
147
|
-
begin
|
148
|
-
# Doesn't actually kill it
|
149
|
-
Process.kill(0, pid)
|
150
|
-
true
|
151
|
-
rescue Errno::ESRCH::Errno
|
152
|
-
true
|
153
|
-
rescue
|
154
|
-
false
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
|
159
|
-
##
|
160
|
-
# Text from mercurial code:
|
161
|
-
#
|
162
|
-
# return id of locker if lock is valid, else None.
|
163
|
-
#
|
164
|
-
# If old-style lock, we cannot tell what machine locker is on.
|
165
|
-
# with new-style lock, if locker is on this machine, we can
|
166
|
-
# see if locker is alive. If locker is on this machine but
|
167
|
-
# not alive, we can safely break lock.
|
168
|
-
#
|
169
|
-
# The lock file is only deleted when None is returned.
|
170
|
-
def test_lock
|
171
|
-
locker = read_lock(@file)
|
172
|
-
host, pid = locker.split(":", 1)
|
173
|
-
return locker if pid.nil? || host != @@host
|
174
|
-
|
175
|
-
pid = pid.to_i
|
176
|
-
return locker if pid == 0
|
177
|
-
|
178
|
-
return locker if test_pid pid
|
179
|
-
|
180
|
-
# if locker dead, break lock. must do this with another lock
|
181
|
-
# held, or can race and break valid lock.
|
182
|
-
begin
|
183
|
-
the_lock = Lock.new(@file + ".break")
|
184
|
-
the_lock.try_lock
|
185
|
-
File.unlink(@file)
|
186
|
-
the_lock.release
|
187
|
-
rescue LockError
|
188
|
-
return locker
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
##
|
193
|
-
# Releases the lock, signalling that it is now safe to modify the directory in which
|
194
|
-
# the lock is found.
|
195
|
-
def release
|
196
|
-
if @held
|
197
|
-
@held = false
|
198
|
-
@release_fxn.call if @release_fxn
|
199
|
-
|
200
|
-
File.unlink(@file) rescue ""
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
@@ -1,214 +0,0 @@
|
|
1
|
-
module Amp
|
2
|
-
module Repositories
|
3
|
-
|
4
|
-
##
|
5
|
-
# = BundleRepository
|
6
|
-
# This class represents a read-only repository that combines both local
|
7
|
-
# repository data with a bundle file. The bundle file contains un-merged-in
|
8
|
-
# changesets - this is useful for, say, previewing the results of a pull
|
9
|
-
# action.
|
10
|
-
#
|
11
|
-
# A bundle is stored in the following manner:
|
12
|
-
# - Changelog entries
|
13
|
-
# - Manifest entries
|
14
|
-
# - Modified File entry #1
|
15
|
-
# - Modified File entry #2
|
16
|
-
# - ...
|
17
|
-
# - Modified file entry #N
|
18
|
-
class BundleRepository < LocalRepository
|
19
|
-
def initialize(path="", config=nil, bundle_name="")
|
20
|
-
@temp_parent = nil
|
21
|
-
# Figure out what to do here - if there's no current local repository, that
|
22
|
-
# takes some special work.
|
23
|
-
begin
|
24
|
-
super(path, false, config) # don't create, just look for a repository
|
25
|
-
rescue
|
26
|
-
# Ok, no local repository. Let's make one really quickly.
|
27
|
-
@temp_parent = File.join(Dir.tmpdir, File.amp_make_tmpname("bundlerepo"))
|
28
|
-
File.mkdir(@temp_parent)
|
29
|
-
tmprepo = LocalRepository.new(@temp_parent, true, config) # true -> create
|
30
|
-
super(@temp_parent, false, config) # and proceed as scheduled!
|
31
|
-
end
|
32
|
-
|
33
|
-
# Set up our URL variable, if anyone asks us what it is
|
34
|
-
if path
|
35
|
-
@url = "bundle:#{path}+#{bundle_name}"
|
36
|
-
else
|
37
|
-
@url = "bundle:#{bundle_name}"
|
38
|
-
end
|
39
|
-
|
40
|
-
@temp_file = nil
|
41
|
-
@bundle_file = File.open(bundle_name, "r")
|
42
|
-
|
43
|
-
@bundle_file.seek(0, IO::SEEK_END)
|
44
|
-
Amp::UI.debug "Bundle File Size: #{@bundle_file.tell}"
|
45
|
-
@bundle_file.seek(0, IO::SEEK_SET)
|
46
|
-
|
47
|
-
# OK, now for the fun part - check the header to see if we're compressed.
|
48
|
-
header = @bundle_file.read(6)
|
49
|
-
# And switch based on that header
|
50
|
-
if !header.start_with?("HG")
|
51
|
-
# Not even an HG file. FML. Bail
|
52
|
-
raise abort("#{bundle_name}: not a Mercurial bundle file")
|
53
|
-
elsif not header.start_with?("HG10")
|
54
|
-
# Not a version we understand, bail
|
55
|
-
raise abort("#{bundle_name}: unknown bundle version")
|
56
|
-
elsif header == "HG10BZ" || header == "HG10GZ"
|
57
|
-
# Compressed! We'll have to save to a new file, because this could get messy.
|
58
|
-
temp_file = Tempfile.new("hg-bundle-hg10un", @root)
|
59
|
-
@temp_file_path = temp_file.path
|
60
|
-
# Are we BZip, or GZip?
|
61
|
-
case header
|
62
|
-
when "HG10BZ"
|
63
|
-
# fuck BZip. Seriously.
|
64
|
-
headerio = StringIO.new "BZ", (ruby_19? ? "w+:ASCII-8BIT" : "w+")
|
65
|
-
input = Amp::Support::MultiIO.new(headerio, @bundle_file)
|
66
|
-
decomp = BZ2::Reader.new(input)
|
67
|
-
when "HG10GZ"
|
68
|
-
# Gzip is much nicer.
|
69
|
-
decomp = Zlib::GzipReader.new(@bundle_file)
|
70
|
-
end
|
71
|
-
|
72
|
-
# We're writing this in an uncompressed fashion, of course.
|
73
|
-
@temp_file.write("HG10UN")
|
74
|
-
# While we can uncompressed....
|
75
|
-
while !r.eof? do
|
76
|
-
# Write the uncompressed data to our new file!
|
77
|
-
@temp_file.write decomp.read(4096)
|
78
|
-
end
|
79
|
-
# and close 'er up
|
80
|
-
@temp_file.close
|
81
|
-
|
82
|
-
# Close the compressed bundle file
|
83
|
-
@bundle_file.close
|
84
|
-
# And re-open the uncompressed bundle file!
|
85
|
-
@bundle_file = File.open(@temp_file_path, "r")
|
86
|
-
# Skip the header.
|
87
|
-
@bundle_file.seek(6)
|
88
|
-
elsif header == "HG10UN"
|
89
|
-
# uncompressed, do nothing
|
90
|
-
else
|
91
|
-
# We have no idae what's going on
|
92
|
-
raise abort("#{bundle_name}: unknown bundle compression type")
|
93
|
-
end
|
94
|
-
# This hash stores pairs of {filename => position_in_bundle_file_of_this_file}
|
95
|
-
@bundle_files_positions = {}
|
96
|
-
end
|
97
|
-
|
98
|
-
##
|
99
|
-
# Gets the changelog of the repository. This is different from {LocalRepository#changelog}
|
100
|
-
# in that it uses a {BundleChangeLog}. Also, since the manifest is stored in the bundle
|
101
|
-
# directly after the changelog, by checking our position in the bundle file, we can save
|
102
|
-
# where the bundle_file is stored.
|
103
|
-
#
|
104
|
-
# @return [BundleChangeLog] the changelog for this repository.
|
105
|
-
def changelog
|
106
|
-
@changelog ||= Bundles::BundleChangeLog.new(@store.opener, @bundle_file)
|
107
|
-
@manifest_start ||= @bundle_file.tell
|
108
|
-
@changelog
|
109
|
-
end
|
110
|
-
|
111
|
-
##
|
112
|
-
# Gets the manifest of the repository. This is different from {LocalRepository#manifest}
|
113
|
-
# in that it uses a {BundleManifest}. The file logs are stored in the bundle directly
|
114
|
-
# after the manifest, so once we load the manifest, we save where the file logs start
|
115
|
-
# when we are done loading the manifest.
|
116
|
-
#
|
117
|
-
# This has the side-effect of loading the changelog, if it hasn't been loaded already -#
|
118
|
-
# this is necessary because the manifest changesets are stored after the changelog changesets,
|
119
|
-
# and we must fully load the changelog changesets to know where to look for the manifest changesets.
|
120
|
-
#
|
121
|
-
# Don't look at me, I didn't design the file format.
|
122
|
-
#
|
123
|
-
# @return [BundleChangeLog] the changelog for this repository.
|
124
|
-
def manifest
|
125
|
-
return @manifest if @manifest
|
126
|
-
@bundle_file.seek manifest_start
|
127
|
-
@manifest ||= Bundles::BundleManifest.new @store.opener, @bundle_file, proc {|n| changelog.rev(n) }
|
128
|
-
@file_start ||= @bundle_file.tell
|
129
|
-
@manifest
|
130
|
-
end
|
131
|
-
|
132
|
-
##
|
133
|
-
# Returns the position in the bundle file where the manifest changesets are located.
|
134
|
-
# This involves loading the changelog first - see {#manifest}
|
135
|
-
#
|
136
|
-
# @return [Integer] the position in the bundle file where we can find the manifest
|
137
|
-
# changesets.
|
138
|
-
def manifest_start
|
139
|
-
changelog && @manifest_start
|
140
|
-
end
|
141
|
-
|
142
|
-
##
|
143
|
-
# Returns the position in the bundle file where the file log changesets are located.
|
144
|
-
# This involves loading the changelog and the manifest first - see {#manifest}.
|
145
|
-
#
|
146
|
-
# @return [Integer] the position in the bundle file where we can find the file-log
|
147
|
-
# changesets.
|
148
|
-
def file_start
|
149
|
-
manifest && @file_start
|
150
|
-
end
|
151
|
-
|
152
|
-
##
|
153
|
-
# Gets the file-log for the given path, so we can look at an individual
|
154
|
-
# file's history, for example. However, we need to be cognizant of files that
|
155
|
-
# traverse the local repository's history as well as the bundle file.
|
156
|
-
#
|
157
|
-
# @param [String] f the path to the file
|
158
|
-
# @return [FileLog] a filelog (a type of revision log) for the given file
|
159
|
-
def file(filename)
|
160
|
-
|
161
|
-
# Load the file-log positions now - we didn't do this in the constructor for a reason
|
162
|
-
# (if they don't ask for them, don't load them!)
|
163
|
-
if @bundle_files_positions.empty?
|
164
|
-
# Jump to the file position
|
165
|
-
@bundle_file.seek file_start
|
166
|
-
while true
|
167
|
-
# get a changegroup chunk - it'll be the filename
|
168
|
-
chunk = RevlogSupport::ChangeGroup.get_chunk @bundle_file
|
169
|
-
# no filename? bail
|
170
|
-
break if chunk.nil? || chunk.empty?
|
171
|
-
|
172
|
-
# Now that we've read the filename, we're at the start of the changelogs for that
|
173
|
-
# file. So let's save this position for later.
|
174
|
-
@bundle_files_positions[chunk] = @bundle_file.tell
|
175
|
-
# Then read chunks until we get to the next file!
|
176
|
-
RevlogSupport::ChangeGroup.each_chunk(@bundle_file) {|c|}
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# Remove leading slash
|
181
|
-
filename = filename.shift("/")
|
182
|
-
|
183
|
-
# Does this file cross local history as well as the bundle?
|
184
|
-
if @bundle_files_positions[filename]
|
185
|
-
# If so, we'll need to make a BundleFileLog. Meh.
|
186
|
-
@bundle_file.seek @bundle_files_positions[filename]
|
187
|
-
Bundles::BundleFileLog.new @store.opener, filename, @bundle_file, proc {|n| changelog.rev(n) }
|
188
|
-
else
|
189
|
-
# Nope? Make a normal FileLog!
|
190
|
-
FileLog.new(@store.opener, filename)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
##
|
195
|
-
# Gets the URL for this repository - unused, I believe.
|
196
|
-
#
|
197
|
-
# @return [String] the URL for the repository
|
198
|
-
def url; @url; end
|
199
|
-
|
200
|
-
##
|
201
|
-
# Closes the repository - in this case, it closes the bundle_file. Analogous to closing
|
202
|
-
# an SSHRepository's socket.
|
203
|
-
def close
|
204
|
-
@bundle_file.close
|
205
|
-
end
|
206
|
-
|
207
|
-
# We can't copy files. Read-only.
|
208
|
-
def can_copy?; false; end
|
209
|
-
# Gets the current working directory. Not sure why we need this.
|
210
|
-
def get_cwd; Dir.pwd; end
|
211
|
-
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|