amp 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.hgignore +26 -0
- data/AUTHORS +2 -0
- data/History.txt +6 -0
- data/LICENSE +37 -0
- data/MANIFESTO +7 -0
- data/Manifest.txt +294 -0
- data/README.md +129 -0
- data/Rakefile +102 -0
- data/SCHEDULE.markdown +12 -0
- data/STYLE +27 -0
- data/TODO.markdown +149 -0
- data/ampfile.rb +47 -0
- data/bin/amp +30 -0
- data/bin/amp1.9 +30 -0
- data/ext/amp/bz2/README.txt +39 -0
- data/ext/amp/bz2/bz2.c +1582 -0
- data/ext/amp/bz2/extconf.rb +77 -0
- data/ext/amp/bz2/mkmf.log +29 -0
- data/ext/amp/mercurial_patch/extconf.rb +5 -0
- data/ext/amp/mercurial_patch/mpatch.c +405 -0
- data/ext/amp/priority_queue/extconf.rb +5 -0
- data/ext/amp/priority_queue/priority_queue.c +947 -0
- data/ext/amp/support/extconf.rb +5 -0
- data/ext/amp/support/support.c +250 -0
- data/lib/amp.rb +200 -0
- data/lib/amp/commands/command.rb +507 -0
- data/lib/amp/commands/command_support.rb +137 -0
- data/lib/amp/commands/commands/config.rb +143 -0
- data/lib/amp/commands/commands/help.rb +29 -0
- data/lib/amp/commands/commands/init.rb +10 -0
- data/lib/amp/commands/commands/templates.rb +137 -0
- data/lib/amp/commands/commands/version.rb +7 -0
- data/lib/amp/commands/commands/workflow.rb +28 -0
- data/lib/amp/commands/commands/workflows/git/add.rb +65 -0
- data/lib/amp/commands/commands/workflows/git/copy.rb +27 -0
- data/lib/amp/commands/commands/workflows/git/mv.rb +23 -0
- data/lib/amp/commands/commands/workflows/git/rm.rb +60 -0
- data/lib/amp/commands/commands/workflows/hg/add.rb +53 -0
- data/lib/amp/commands/commands/workflows/hg/addremove.rb +86 -0
- data/lib/amp/commands/commands/workflows/hg/annotate.rb +46 -0
- data/lib/amp/commands/commands/workflows/hg/archive.rb +126 -0
- data/lib/amp/commands/commands/workflows/hg/branch.rb +28 -0
- data/lib/amp/commands/commands/workflows/hg/branches.rb +30 -0
- data/lib/amp/commands/commands/workflows/hg/bundle.rb +115 -0
- data/lib/amp/commands/commands/workflows/hg/clone.rb +95 -0
- data/lib/amp/commands/commands/workflows/hg/commit.rb +42 -0
- data/lib/amp/commands/commands/workflows/hg/copy.rb +31 -0
- data/lib/amp/commands/commands/workflows/hg/debug/dirstate.rb +32 -0
- data/lib/amp/commands/commands/workflows/hg/debug/index.rb +36 -0
- data/lib/amp/commands/commands/workflows/hg/default.rb +9 -0
- data/lib/amp/commands/commands/workflows/hg/diff.rb +30 -0
- data/lib/amp/commands/commands/workflows/hg/forget.rb +11 -0
- data/lib/amp/commands/commands/workflows/hg/heads.rb +25 -0
- data/lib/amp/commands/commands/workflows/hg/identify.rb +23 -0
- data/lib/amp/commands/commands/workflows/hg/import.rb +135 -0
- data/lib/amp/commands/commands/workflows/hg/incoming.rb +85 -0
- data/lib/amp/commands/commands/workflows/hg/info.rb +18 -0
- data/lib/amp/commands/commands/workflows/hg/log.rb +21 -0
- data/lib/amp/commands/commands/workflows/hg/manifest.rb +13 -0
- data/lib/amp/commands/commands/workflows/hg/merge.rb +53 -0
- data/lib/amp/commands/commands/workflows/hg/move.rb +28 -0
- data/lib/amp/commands/commands/workflows/hg/outgoing.rb +61 -0
- data/lib/amp/commands/commands/workflows/hg/pull.rb +74 -0
- data/lib/amp/commands/commands/workflows/hg/push.rb +20 -0
- data/lib/amp/commands/commands/workflows/hg/remove.rb +45 -0
- data/lib/amp/commands/commands/workflows/hg/resolve.rb +83 -0
- data/lib/amp/commands/commands/workflows/hg/revert.rb +53 -0
- data/lib/amp/commands/commands/workflows/hg/root.rb +13 -0
- data/lib/amp/commands/commands/workflows/hg/serve.rb +38 -0
- data/lib/amp/commands/commands/workflows/hg/status.rb +116 -0
- data/lib/amp/commands/commands/workflows/hg/tag.rb +69 -0
- data/lib/amp/commands/commands/workflows/hg/tags.rb +27 -0
- data/lib/amp/commands/commands/workflows/hg/tip.rb +13 -0
- data/lib/amp/commands/commands/workflows/hg/update.rb +27 -0
- data/lib/amp/commands/commands/workflows/hg/verify.rb +9 -0
- data/lib/amp/commands/commands/workflows/hg/view.rb +36 -0
- data/lib/amp/commands/dispatch.rb +181 -0
- data/lib/amp/commands/hooks.rb +81 -0
- data/lib/amp/dependencies/amp_support.rb +1 -0
- data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +103 -0
- data/lib/amp/dependencies/minitar.rb +979 -0
- data/lib/amp/dependencies/priority_queue.rb +18 -0
- data/lib/amp/dependencies/priority_queue/c_priority_queue.rb +1 -0
- data/lib/amp/dependencies/priority_queue/poor_priority_queue.rb +46 -0
- data/lib/amp/dependencies/priority_queue/ruby_priority_queue.rb +525 -0
- data/lib/amp/dependencies/python_config.rb +211 -0
- data/lib/amp/dependencies/trollop.rb +713 -0
- data/lib/amp/dependencies/zip/ioextras.rb +155 -0
- data/lib/amp/dependencies/zip/stdrubyext.rb +111 -0
- data/lib/amp/dependencies/zip/tempfile_bugfixed.rb +186 -0
- data/lib/amp/dependencies/zip/zip.rb +1850 -0
- data/lib/amp/dependencies/zip/zipfilesystem.rb +609 -0
- data/lib/amp/dependencies/zip/ziprequire.rb +90 -0
- data/lib/amp/encoding/base85.rb +97 -0
- data/lib/amp/encoding/binary_diff.rb +82 -0
- data/lib/amp/encoding/difflib.rb +166 -0
- data/lib/amp/encoding/mercurial_diff.rb +378 -0
- data/lib/amp/encoding/mercurial_patch.rb +1 -0
- data/lib/amp/encoding/patch.rb +292 -0
- data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +123 -0
- data/lib/amp/extensions/ditz.rb +41 -0
- data/lib/amp/extensions/lighthouse.rb +167 -0
- data/lib/amp/graphs/ancestor.rb +147 -0
- data/lib/amp/graphs/copies.rb +261 -0
- data/lib/amp/merges/merge_state.rb +164 -0
- data/lib/amp/merges/merge_ui.rb +322 -0
- data/lib/amp/merges/simple_merge.rb +450 -0
- data/lib/amp/profiling_hacks.rb +36 -0
- data/lib/amp/repository/branch_manager.rb +234 -0
- data/lib/amp/repository/dir_state.rb +950 -0
- data/lib/amp/repository/journal.rb +203 -0
- data/lib/amp/repository/lock.rb +207 -0
- data/lib/amp/repository/repositories/bundle_repository.rb +214 -0
- data/lib/amp/repository/repositories/http_repository.rb +377 -0
- data/lib/amp/repository/repositories/local_repository.rb +2661 -0
- data/lib/amp/repository/repository.rb +94 -0
- data/lib/amp/repository/store.rb +485 -0
- data/lib/amp/repository/tag_manager.rb +319 -0
- data/lib/amp/repository/updatable.rb +532 -0
- data/lib/amp/repository/verification.rb +431 -0
- data/lib/amp/repository/versioned_file.rb +475 -0
- data/lib/amp/revlogs/bundle_revlogs.rb +246 -0
- data/lib/amp/revlogs/changegroup.rb +217 -0
- data/lib/amp/revlogs/changelog.rb +338 -0
- data/lib/amp/revlogs/changeset.rb +521 -0
- data/lib/amp/revlogs/file_log.rb +165 -0
- data/lib/amp/revlogs/index.rb +493 -0
- data/lib/amp/revlogs/manifest.rb +195 -0
- data/lib/amp/revlogs/node.rb +18 -0
- data/lib/amp/revlogs/revlog.rb +1032 -0
- data/lib/amp/revlogs/revlog_support.rb +126 -0
- data/lib/amp/server/amp_user.rb +44 -0
- data/lib/amp/server/extension/amp_extension.rb +396 -0
- data/lib/amp/server/extension/authorization.rb +201 -0
- data/lib/amp/server/fancy_http_server.rb +252 -0
- data/lib/amp/server/fancy_views/_browser.haml +28 -0
- data/lib/amp/server/fancy_views/_diff_file.haml +13 -0
- data/lib/amp/server/fancy_views/_navbar.haml +17 -0
- data/lib/amp/server/fancy_views/changeset.haml +31 -0
- data/lib/amp/server/fancy_views/commits.haml +32 -0
- data/lib/amp/server/fancy_views/file.haml +35 -0
- data/lib/amp/server/fancy_views/file_diff.haml +23 -0
- data/lib/amp/server/fancy_views/harshcss/all_hallows_eve.css +72 -0
- data/lib/amp/server/fancy_views/harshcss/amy.css +147 -0
- data/lib/amp/server/fancy_views/harshcss/twilight.css +138 -0
- data/lib/amp/server/fancy_views/stylesheet.sass +175 -0
- data/lib/amp/server/http_server.rb +140 -0
- data/lib/amp/server/repo_user_management.rb +287 -0
- data/lib/amp/support/amp_config.rb +164 -0
- data/lib/amp/support/amp_ui.rb +287 -0
- data/lib/amp/support/docs.rb +54 -0
- data/lib/amp/support/generator.rb +78 -0
- data/lib/amp/support/ignore.rb +144 -0
- data/lib/amp/support/loaders.rb +93 -0
- data/lib/amp/support/logger.rb +103 -0
- data/lib/amp/support/match.rb +151 -0
- data/lib/amp/support/multi_io.rb +87 -0
- data/lib/amp/support/openers.rb +121 -0
- data/lib/amp/support/ruby_19_compatibility.rb +66 -0
- data/lib/amp/support/support.rb +1095 -0
- data/lib/amp/templates/blank.commit.erb +23 -0
- data/lib/amp/templates/blank.log.erb +18 -0
- data/lib/amp/templates/default.commit.erb +23 -0
- data/lib/amp/templates/default.log.erb +26 -0
- data/lib/amp/templates/template.rb +165 -0
- data/site/Rakefile +24 -0
- data/site/src/about/ampfile.haml +57 -0
- data/site/src/about/commands.haml +106 -0
- data/site/src/about/index.haml +33 -0
- data/site/src/about/performance.haml +31 -0
- data/site/src/about/workflows.haml +34 -0
- data/site/src/contribute/index.haml +65 -0
- data/site/src/contribute/style.haml +297 -0
- data/site/src/css/active4d.css +114 -0
- data/site/src/css/all_hallows_eve.css +72 -0
- data/site/src/css/all_themes.css +3299 -0
- data/site/src/css/amp.css +260 -0
- data/site/src/css/amy.css +147 -0
- data/site/src/css/blackboard.css +88 -0
- data/site/src/css/brilliance_black.css +605 -0
- data/site/src/css/brilliance_dull.css +599 -0
- data/site/src/css/cobalt.css +149 -0
- data/site/src/css/cur_amp.css +185 -0
- data/site/src/css/dawn.css +121 -0
- data/site/src/css/eiffel.css +121 -0
- data/site/src/css/espresso_libre.css +109 -0
- data/site/src/css/idle.css +62 -0
- data/site/src/css/iplastic.css +80 -0
- data/site/src/css/lazy.css +73 -0
- data/site/src/css/mac_classic.css +123 -0
- data/site/src/css/magicwb_amiga.css +104 -0
- data/site/src/css/pastels_on_dark.css +188 -0
- data/site/src/css/reset.css +55 -0
- data/site/src/css/slush_poppies.css +85 -0
- data/site/src/css/spacecadet.css +51 -0
- data/site/src/css/sunburst.css +180 -0
- data/site/src/css/twilight.css +137 -0
- data/site/src/css/zenburnesque.css +91 -0
- data/site/src/get/index.haml +32 -0
- data/site/src/helpers.rb +121 -0
- data/site/src/images/amp_logo.png +0 -0
- data/site/src/images/carbonica.png +0 -0
- data/site/src/images/revolution.png +0 -0
- data/site/src/images/tab-bg.png +0 -0
- data/site/src/images/tab-sliding-left.png +0 -0
- data/site/src/images/tab-sliding-right.png +0 -0
- data/site/src/include/_footer.haml +22 -0
- data/site/src/include/_header.haml +17 -0
- data/site/src/index.haml +104 -0
- data/site/src/learn/index.haml +46 -0
- data/site/src/scripts/jquery-1.3.2.min.js +19 -0
- data/site/src/scripts/jquery.cookie.js +96 -0
- data/tasks/stats.rake +155 -0
- data/tasks/yard.rake +171 -0
- data/test/dirstate_tests/dirstate +0 -0
- data/test/dirstate_tests/hgrc +5 -0
- data/test/dirstate_tests/test_dir_state.rb +192 -0
- data/test/functional_tests/resources/.hgignore +2 -0
- data/test/functional_tests/resources/STYLE.txt +25 -0
- data/test/functional_tests/resources/command.rb +372 -0
- data/test/functional_tests/resources/commands/annotate.rb +57 -0
- data/test/functional_tests/resources/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/commands/heads.rb +22 -0
- data/test/functional_tests/resources/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/commands/status.rb +90 -0
- data/test/functional_tests/resources/version2/.hgignore +5 -0
- data/test/functional_tests/resources/version2/STYLE.txt +25 -0
- data/test/functional_tests/resources/version2/command.rb +372 -0
- data/test/functional_tests/resources/version2/commands/annotate.rb +45 -0
- data/test/functional_tests/resources/version2/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version2/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version2/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version2/commands/status.rb +90 -0
- data/test/functional_tests/resources/version3/.hgignore +5 -0
- data/test/functional_tests/resources/version3/STYLE.txt +31 -0
- data/test/functional_tests/resources/version3/command.rb +376 -0
- data/test/functional_tests/resources/version3/commands/annotate.rb +45 -0
- data/test/functional_tests/resources/version3/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version3/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version3/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version3/commands/status.rb +90 -0
- data/test/functional_tests/resources/version4/.hgignore +5 -0
- data/test/functional_tests/resources/version4/STYLE.txt +31 -0
- data/test/functional_tests/resources/version4/command.rb +376 -0
- data/test/functional_tests/resources/version4/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version4/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version4/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version4/commands/stats.rb +25 -0
- data/test/functional_tests/resources/version4/commands/status.rb +90 -0
- data/test/functional_tests/resources/version5_1/.hgignore +5 -0
- data/test/functional_tests/resources/version5_1/STYLE.txt +2 -0
- data/test/functional_tests/resources/version5_1/command.rb +374 -0
- data/test/functional_tests/resources/version5_1/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version5_1/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version5_1/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version5_1/commands/stats.rb +25 -0
- data/test/functional_tests/resources/version5_1/commands/status.rb +90 -0
- data/test/functional_tests/resources/version5_2/.hgignore +5 -0
- data/test/functional_tests/resources/version5_2/STYLE.txt +14 -0
- data/test/functional_tests/resources/version5_2/command.rb +376 -0
- data/test/functional_tests/resources/version5_2/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version5_2/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version5_2/commands/newz.rb +12 -0
- data/test/functional_tests/resources/version5_2/commands/stats.rb +25 -0
- data/test/functional_tests/resources/version5_2/commands/status.rb +90 -0
- data/test/functional_tests/test_functional.rb +604 -0
- data/test/localrepo_tests/test_local_repo.rb +121 -0
- data/test/localrepo_tests/testrepo.tar.gz +0 -0
- data/test/manifest_tests/00manifest.i +0 -0
- data/test/manifest_tests/test_manifest.rb +72 -0
- data/test/merge_tests/base.txt +10 -0
- data/test/merge_tests/expected.local.txt +16 -0
- data/test/merge_tests/local.txt +11 -0
- data/test/merge_tests/remote.txt +11 -0
- data/test/merge_tests/test_merge.rb +26 -0
- data/test/revlog_tests/00changelog.i +0 -0
- data/test/revlog_tests/revision_added_changelog.i +0 -0
- data/test/revlog_tests/test_adding_index.i +0 -0
- data/test/revlog_tests/test_revlog.rb +333 -0
- data/test/revlog_tests/testindex.i +0 -0
- data/test/store_tests/store.tar.gz +0 -0
- data/test/store_tests/test_fncache_store.rb +122 -0
- data/test/test_amp.rb +9 -0
- data/test/test_base85.rb +14 -0
- data/test/test_bdiff.rb +42 -0
- data/test/test_commands.rb +122 -0
- data/test/test_difflib.rb +50 -0
- data/test/test_helper.rb +15 -0
- data/test/test_journal.rb +29 -0
- data/test/test_match.rb +134 -0
- data/test/test_mdiff.rb +74 -0
- data/test/test_mpatch.rb +14 -0
- data/test/test_support.rb +24 -0
- metadata +385 -0
@@ -0,0 +1,203 @@
|
|
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
|
+
|
@@ -0,0 +1,207 @@
|
|
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
|
@@ -0,0 +1,214 @@
|
|
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
|