amp 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,126 @@
|
|
|
1
|
+
autoload :Zlib, 'zlib'
|
|
2
|
+
module Amp
|
|
3
|
+
module RevlogSupport
|
|
4
|
+
|
|
5
|
+
class RevlogError < StandardError; end
|
|
6
|
+
class LookupError < StandardError; end
|
|
7
|
+
|
|
8
|
+
module Support
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
# Old version of the revlog file format
|
|
12
|
+
REVLOG_VERSION_0 = 0
|
|
13
|
+
# Current version of the revlog file format
|
|
14
|
+
REVLOG_VERSION_NG = 1
|
|
15
|
+
# A flag marking that the data is stored with the index
|
|
16
|
+
REVLOG_NG_INLINE_DATA = (1 << 16)
|
|
17
|
+
# Default flags - always start inline (turn off inline if file is huge)
|
|
18
|
+
REVLOG_DEFAULT_FLAGS = REVLOG_NG_INLINE_DATA
|
|
19
|
+
# Default format - the most recent
|
|
20
|
+
REVLOG_DEFAULT_FORMAT = REVLOG_VERSION_NG
|
|
21
|
+
# Default version in general
|
|
22
|
+
REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# This bears some explanation.
|
|
26
|
+
#
|
|
27
|
+
# Rather than simply having a 4-byte header for the index file format, the
|
|
28
|
+
# Mercurial format takes the first entry in the index, and stores the header
|
|
29
|
+
# in its offset field. (The offset field is a 64-bit unsigned integer which
|
|
30
|
+
# stores the offset into the data or index of the associated record's data)
|
|
31
|
+
# They take advantage of the fact that the first entry's offset will always
|
|
32
|
+
# be 0. As such, its offset field is always going to be zero, so it's safe
|
|
33
|
+
# to store data there.
|
|
34
|
+
#
|
|
35
|
+
# The format is ((flags << 16) | (version)), where +flags+ is a bitmask (up to 48
|
|
36
|
+
# bits) and +version+ is a 16-bit unsigned short.
|
|
37
|
+
#
|
|
38
|
+
# The worst part is, EVERY SINGLE ENTRY has its offset shifted 16 bits to the left,
|
|
39
|
+
# apparently all because of this. It fucking baffles my mind.
|
|
40
|
+
#
|
|
41
|
+
# So yeah. offset = value >> 16.
|
|
42
|
+
def get_offset(o); o >> 16; end
|
|
43
|
+
# And yeah. version = value && 0xFFFF (last 16 bits)
|
|
44
|
+
def get_version(t); t & 0xFFFF; end
|
|
45
|
+
|
|
46
|
+
# Combine an offset and a version to spit this baby out
|
|
47
|
+
def offset_version(offset,type)
|
|
48
|
+
(offset << 16) | type
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# generate a hash from the given text and its parent hashes
|
|
53
|
+
#
|
|
54
|
+
# This hash combines both the current file contents and its history
|
|
55
|
+
# in a manner that makes it easy to distinguish nodes with the same
|
|
56
|
+
# content in the revision graph.
|
|
57
|
+
#
|
|
58
|
+
# since an entry in a revlog is pretty
|
|
59
|
+
# much [parent1, parent2, text], we use a hash of the previous entry
|
|
60
|
+
# as a reference to that previous entry. To create a reference to this
|
|
61
|
+
# entry, we make a hash of the first parent (which is just its ID), the
|
|
62
|
+
# second parent, and the text.
|
|
63
|
+
#
|
|
64
|
+
# @return [String] the digest of the two parents and the extra text
|
|
65
|
+
def history_hash(text, p1, p2)
|
|
66
|
+
list = [p1, p2].sort
|
|
67
|
+
s = list[0].sha1
|
|
68
|
+
s.update list[1]
|
|
69
|
+
s.update text
|
|
70
|
+
s.digest
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# returns the possibly-compressed version of the text, in a hash:
|
|
75
|
+
#
|
|
76
|
+
# @return [Hash] :compression => 'u' or ''
|
|
77
|
+
def compress(text)
|
|
78
|
+
return {:compression => "", :text => text} if text.empty?
|
|
79
|
+
size = text.size
|
|
80
|
+
binary = nil
|
|
81
|
+
if size < 44
|
|
82
|
+
elsif size > 1000000 #big ole file
|
|
83
|
+
deflater = Zlib::Deflate.new
|
|
84
|
+
parts = []
|
|
85
|
+
position = 0
|
|
86
|
+
while position < size
|
|
87
|
+
newposition = position + 2**20
|
|
88
|
+
p << deflater.deflate(text[position..(newposition-1)], Zlib::NO_FLUSH)
|
|
89
|
+
position = newposition
|
|
90
|
+
end
|
|
91
|
+
p << deflater.flush
|
|
92
|
+
binary = p.join if p.map {|e| e.size}.sum < size # only add it if
|
|
93
|
+
# compression made it smaller
|
|
94
|
+
else #tiny, just compress it
|
|
95
|
+
binary = Zlib::Deflate.deflate text
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if binary.nil? || binary.size > size
|
|
99
|
+
return {:compression => "", :text => text} if text[0,1] == "\0"
|
|
100
|
+
return {:compression => 'u', :text => text}
|
|
101
|
+
end
|
|
102
|
+
{:compression => "", :text => binary}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Decompresses the given binary text. The binary text could be
|
|
107
|
+
# uncompressed, in which case, we'll figure that out. Don't worry.
|
|
108
|
+
#
|
|
109
|
+
# @param [String] binary the text to (possibly) decompress
|
|
110
|
+
# @return [String] the text decompressed
|
|
111
|
+
def decompress(binary)
|
|
112
|
+
return binary if binary.empty?
|
|
113
|
+
case binary[0,1]
|
|
114
|
+
when "\0"
|
|
115
|
+
binary #we're just stored as binary
|
|
116
|
+
when "x"
|
|
117
|
+
Zlib::Inflate.inflate(binary) #we're zlibbed
|
|
118
|
+
when "u"
|
|
119
|
+
binary[1..-1] #we're uncompressed text
|
|
120
|
+
else
|
|
121
|
+
raise LookupError.new("Unknown compression type #{binary[0,1]}")
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
module Servers
|
|
3
|
+
|
|
4
|
+
##
|
|
5
|
+
# = User
|
|
6
|
+
# A single user within an Amp's server system. Just a simple struct -
|
|
7
|
+
# though they do offer a convenient constructor, taking a hash.
|
|
8
|
+
class User < Struct.new(:username, :password)
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Generates a public user - i.e., somebody who has been granted no explicit rights.
|
|
12
|
+
# @return [User] a public user with no explicit rights
|
|
13
|
+
def self.public_user
|
|
14
|
+
@@public_user ||= new('public', "")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Extra constructor - takes a hash to create a new User, instead of
|
|
19
|
+
# the Struct class's ordered parameters for its constructor.
|
|
20
|
+
#
|
|
21
|
+
# @raise [RuntimeError] raised if the user doesn't supply :password or
|
|
22
|
+
# :password_hashed
|
|
23
|
+
# @param [Hash] input the hash representing the values for the struct
|
|
24
|
+
# @option input [String] :username The username for the user
|
|
25
|
+
# @option input [String] :password The cleartext (unencrypted) password.
|
|
26
|
+
# @option input [String] :can_read Can the user read the repository?
|
|
27
|
+
# @option input [String] :can_write Can the user write to the repository?
|
|
28
|
+
def self.from_hash(input={})
|
|
29
|
+
# input checking
|
|
30
|
+
unless input[:password]
|
|
31
|
+
raise "User must have a password attribute"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# public is reserved as the username of the public user
|
|
35
|
+
if input[:username].to_s == 'public'
|
|
36
|
+
raise "User cannot have username 'public' -- reserved by Amp system"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
new input[:username], input[:password]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
require 'pp'
|
|
2
|
+
require 'sinatra/base'
|
|
3
|
+
require 'rack/contrib'
|
|
4
|
+
require 'zlib'
|
|
5
|
+
|
|
6
|
+
require 'time' # for Time.httpdate
|
|
7
|
+
require 'rack/utils'
|
|
8
|
+
|
|
9
|
+
module Sinatra
|
|
10
|
+
##
|
|
11
|
+
# = AmpExtension
|
|
12
|
+
# This module adds a single DSL method to the sinatra base class:
|
|
13
|
+
# amp_repository. This method allows you to specify an HTTP path for
|
|
14
|
+
# an amp repo. You can call this method multiple times to specify
|
|
15
|
+
# multiple repositories for your server.
|
|
16
|
+
#
|
|
17
|
+
# @example - This will start a server, serving the directory the file is in,
|
|
18
|
+
# at http://localhost:4567/
|
|
19
|
+
# require 'amp'
|
|
20
|
+
# require 'sinatra'
|
|
21
|
+
# require 'amp/server/extension/amp_extension'
|
|
22
|
+
#
|
|
23
|
+
# amp_repository "/", Amp::Repositories.pick(nil, ".")
|
|
24
|
+
#
|
|
25
|
+
module AmpExtension
|
|
26
|
+
|
|
27
|
+
def amp_repositories; @@amp_repositories ||= {}; end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# This method will specify that the sinatra application should serve the
|
|
31
|
+
# repository +repo+ using Mercurial's HTTP(S) protocol at +http_path+.
|
|
32
|
+
# You can call this method multiple times for multiple repositories on
|
|
33
|
+
# different paths.
|
|
34
|
+
#
|
|
35
|
+
# @example - This will start a server, serving the directory the file is in,
|
|
36
|
+
# at http://localhost:4567/
|
|
37
|
+
# require 'amp'
|
|
38
|
+
# require 'sinatra'
|
|
39
|
+
# require 'amp/server/extension/amp_extension'
|
|
40
|
+
#
|
|
41
|
+
# amp_repository "/", Amp::Repositories.pick(nil, ".")
|
|
42
|
+
#
|
|
43
|
+
# @param [String] http_path the URL path from which to serve the repository
|
|
44
|
+
# @param [Repository] repo the repository being served - typically a LocalRepository.
|
|
45
|
+
def amp_repository(http_path, repo)
|
|
46
|
+
amp_repositories[http_path] = repo
|
|
47
|
+
|
|
48
|
+
get http_path do
|
|
49
|
+
if ACCEPTABLE_COMMANDS.include?(params[:cmd])
|
|
50
|
+
send("amp_get_#{params[:cmd]}".to_sym, repo)
|
|
51
|
+
else
|
|
52
|
+
pass
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# All the commands we are capable of accepting
|
|
58
|
+
ACCEPTABLE_COMMANDS = [ 'branches', 'heads', 'lookup', 'capabilities', 'between', 'changegroup', 'changegroupsubset', 'unbundle' ]
|
|
59
|
+
READABLE_COMMANDS = [ 'branches', 'heads', 'lookup', 'capabilities', 'between', 'changegroup', 'changegroupsubset' ]
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# These methods are helpers that the server will run to implement the Mercurial
|
|
65
|
+
# HTTP(S) protocol. These should not be overridden if Mercurial compatibility is
|
|
66
|
+
# required. All methods - unless otherwise specified - return the exact data string
|
|
67
|
+
# that the server will serve as the HTTP data.
|
|
68
|
+
module AmpRepoMethods
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
# Checks if the given command performs a read operation
|
|
72
|
+
#
|
|
73
|
+
# @param [String] cmd the command to check
|
|
74
|
+
# @return [Boolean] does the command perform any reads on the repo?
|
|
75
|
+
def command_reads?(cmd); AmpExtension::READABLE_COMMANDS.include?(cmd); end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
# Checks if the given command performs a write operation
|
|
79
|
+
#
|
|
80
|
+
# @param [String] cmd the command to check
|
|
81
|
+
# @return [Boolean] does the command perform any writes on the repo?
|
|
82
|
+
def command_writes?(cmd); !command_reads?(cmd); end
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# Command: lookup
|
|
86
|
+
#
|
|
87
|
+
# Looks up a node-id for a key - the key could be an integer (for a revision index),
|
|
88
|
+
# a partial node_id (such as 12dead34beef), or even "tip" to get the current tip.
|
|
89
|
+
# Only concerns revisions in the changelog (the "global" revisions)
|
|
90
|
+
#
|
|
91
|
+
# HTTP parameter: "key" => the key being looked up in the changelogs
|
|
92
|
+
#
|
|
93
|
+
# @param [Repository] amp_repo the repository being inspected
|
|
94
|
+
# @return [String] a response to deliver to the client, in the format "#{success} #{node_id}",
|
|
95
|
+
# where success is 1 for a successful lookup and node_id is 0 for a failed lookup.
|
|
96
|
+
def amp_get_lookup(amp_repo)
|
|
97
|
+
begin
|
|
98
|
+
rev = amp_repo.lookup(params["key"]).hexlify
|
|
99
|
+
success = 1
|
|
100
|
+
rescue StandardError => e
|
|
101
|
+
rev = e.to_s
|
|
102
|
+
success = 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
"#{success} #{rev}\n"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# Command: heads
|
|
110
|
+
#
|
|
111
|
+
# Looks up the heads for the given repository. No parameters are taken - just the heads
|
|
112
|
+
# are returned.
|
|
113
|
+
#
|
|
114
|
+
# @param [Repository] amp_repo the repository whose heads are examined
|
|
115
|
+
# @return [String] a response to deliver to the client, with each head returned as a full
|
|
116
|
+
# node-id, in hex form (so 40 bytes total), each separated by a single space.
|
|
117
|
+
def amp_get_heads(amp_repo)
|
|
118
|
+
repo = amp_repo
|
|
119
|
+
repo.heads.map {|x| x.hexlify}.join(" ")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def amp_get_branches(amp_repo)
|
|
123
|
+
nodes = []
|
|
124
|
+
if params["nodes"]
|
|
125
|
+
nodes = params["nodes"].split(" ").map {|x| x.unhexlify}
|
|
126
|
+
end
|
|
127
|
+
amp_repo.branches(nodes).map do |branches|
|
|
128
|
+
branches.map {|branch| branch.hexlify}.join(" ")
|
|
129
|
+
end.join "\n"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# Command: capabilities
|
|
134
|
+
#
|
|
135
|
+
# Returns what special commands the server is capable of performing. This is where new
|
|
136
|
+
# additions to the protocol are added, so new clients can check to make sure new features
|
|
137
|
+
# are supported.
|
|
138
|
+
#
|
|
139
|
+
# @param [Repository] amp_repo the repository whose capabilities are returned
|
|
140
|
+
# @return [String] a response to deliver to the client, with each capability listed,
|
|
141
|
+
# separated by spaces. If the capability has multiple values (such as 'unbundle'),
|
|
142
|
+
# it is returned in the format "capability=value1,value2,value3" instead of just
|
|
143
|
+
# "capability". No spaces are allowed in the capability= fragment.
|
|
144
|
+
def amp_get_capabilities(amp_repo)
|
|
145
|
+
caps = ["lookup", "changegroupsubset"]
|
|
146
|
+
# uncompressed for streaming?
|
|
147
|
+
caps << "unbundle=#{Amp::RevlogSupport::ChangeGroup::FORMAT_PRIORITIES.join(',')}"
|
|
148
|
+
caps.join ' '
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
##
|
|
152
|
+
# Command: between
|
|
153
|
+
#
|
|
154
|
+
# Takes a list of node pairs. Each pair has a "start" and an "end" node ID, which specify
|
|
155
|
+
# a range of revisions. The +between+ command returns the nodes between the start and the
|
|
156
|
+
# end, exclusive, for each provided pair.
|
|
157
|
+
#
|
|
158
|
+
# HTTP param: pairs. Each pair is presented as 2 node IDs, as hex, separated by a a hyphen.
|
|
159
|
+
# then, each pair is delimited by other pairs with a space. Example:
|
|
160
|
+
# pair1startnodeid-pair1endnodeid pair2startnodeid-pair2endnodeid pair3startnodeid-pair3endnodeid
|
|
161
|
+
#
|
|
162
|
+
# @param [Repository] amp_repo the repository upon which to perform node lookups
|
|
163
|
+
# @return [String] a response to deliver to the client, with the nodes between each pair
|
|
164
|
+
# provided. Each pair provided by the client will result in a list of node IDs - this list
|
|
165
|
+
# is returned as each node ID in the list, with spaces between the nodes. Each pair has its
|
|
166
|
+
# results on a new line. Example output for 3 provided pairs:
|
|
167
|
+
# abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde 1234567890123456789012345678901234567890
|
|
168
|
+
# 1234567890123456789012345678901234567890
|
|
169
|
+
# abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
|
|
170
|
+
#
|
|
171
|
+
def amp_get_between(amp_repo)
|
|
172
|
+
pairs = []
|
|
173
|
+
|
|
174
|
+
if params["pairs"]
|
|
175
|
+
pairs = params["pairs"].split(" ").map {|p| p.split("-").map {|i| i.unhexlify } }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
amp_repo.between(pairs).map do |nodes|
|
|
179
|
+
nodes.map {|i| i.hexlify }.join " "
|
|
180
|
+
end.join "\n"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
##
|
|
184
|
+
# = DelayedGzipper
|
|
185
|
+
# Takes a block when initialized, but doesn't run the block. It actually
|
|
186
|
+
# saves the block, and then runs it later, streaming the results into a
|
|
187
|
+
# GZip stream. Very memory friendly.
|
|
188
|
+
#
|
|
189
|
+
# This class is designed to work with Rack. All it has to do is implement an
|
|
190
|
+
# #each method which takes a block, and which calls that method when
|
|
191
|
+
# gzip data is to be written out. This way data doesn't have to be generated,
|
|
192
|
+
# then processed, then processed, etc. It is actually streamed to the client.
|
|
193
|
+
class DelayedGzipper
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# Creates a new DelayedGzipper. All you must do is create a new DelayedGzipper,
|
|
197
|
+
# whose block results in an IO-like object, and return it as the result of
|
|
198
|
+
# a Sinatra/Rack endpoint. Rack will run the #each method to stream the data
|
|
199
|
+
# out.
|
|
200
|
+
#
|
|
201
|
+
# @yield The block provided will be stored and executed lazily later when
|
|
202
|
+
# the results of the block need to be generated and gzipped. Should return
|
|
203
|
+
# an IO-like object to maximize memory-friendliness.
|
|
204
|
+
def initialize(&block)
|
|
205
|
+
@result_generator = block
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# For Rack compliance. The block should be called whenever data is to be written
|
|
210
|
+
# to the client. We actually save the block, and use a GzipWriter to funnel the
|
|
211
|
+
# gzipped data into the block. Pretty nifty.
|
|
212
|
+
def each(&block)
|
|
213
|
+
|
|
214
|
+
# Save the writer for safe-keeping
|
|
215
|
+
@writer = block
|
|
216
|
+
|
|
217
|
+
# This creates a gzip-writer. By passing in +self+ as the parameter, when we
|
|
218
|
+
# write to the gzip-writer, it will then call #write on +self+ with the
|
|
219
|
+
# gzipped data. This allows us to handle the compressed data immediately
|
|
220
|
+
# instead of funneling it to a buffer or something useless like that.
|
|
221
|
+
gzip = ::Zlib::GzipWriter.new self
|
|
222
|
+
gzip.mtime = Time.now
|
|
223
|
+
|
|
224
|
+
# Gets the IO-like object that we need to gzip
|
|
225
|
+
f = @result_generator.call
|
|
226
|
+
|
|
227
|
+
begin
|
|
228
|
+
chunk = f.read 4.kb
|
|
229
|
+
gzip << chunk if chunk && chunk.any?
|
|
230
|
+
end while chunk && chunk.any?
|
|
231
|
+
|
|
232
|
+
# Finish it off
|
|
233
|
+
gzip.close
|
|
234
|
+
|
|
235
|
+
# We're done!
|
|
236
|
+
@writer = nil
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
##
|
|
240
|
+
# Called by GzipWriter so we can immediately handle our gzipped data.
|
|
241
|
+
# We write it to the client using the @writer given to us in #each.
|
|
242
|
+
#
|
|
243
|
+
# @param [String] data the data to write to the client. Gzipped.
|
|
244
|
+
def write(data)
|
|
245
|
+
@writer.call data
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
##
|
|
250
|
+
# Helper method for setting up the headers for lazily gzipped results in a sinatra
|
|
251
|
+
# app.
|
|
252
|
+
#
|
|
253
|
+
# @return [Rack::Utils::HeaderHash] the headers that tell a client to expect
|
|
254
|
+
# gzipped data, and that we don't know how big the data is going to be,
|
|
255
|
+
# because we're gzipping it on the fly!
|
|
256
|
+
def gzipped_response
|
|
257
|
+
headers = Rack::Utils::HeaderHash.new(response.headers)
|
|
258
|
+
vary = headers["Vary"].to_s.split(",").map { |v| v.strip }
|
|
259
|
+
|
|
260
|
+
unless vary.include?("*") || vary.include?("Accept-Encoding")
|
|
261
|
+
headers["Vary"] = vary.push("Accept-Encoding").join ","
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
headers.delete 'Content-Length'
|
|
265
|
+
headers["Content-Encoding"] = "gzip"
|
|
266
|
+
headers
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
##
|
|
270
|
+
# Command: changegroup
|
|
271
|
+
#
|
|
272
|
+
# Gets a given changegroup from the repository. Starts at the requested roots,
|
|
273
|
+
# and then goes to the heads of the repository from those roots.
|
|
274
|
+
#
|
|
275
|
+
# HTTP Param: roots. The roots of the trees we are requesting, in the form of
|
|
276
|
+
# a list of node IDs. the IDs are in hex, and separated by spaces.
|
|
277
|
+
#
|
|
278
|
+
# @param [Repository] amp_repo the repository from which we are requesting the
|
|
279
|
+
# changegroup.
|
|
280
|
+
# @return [String] the changegroup to be returned to the client. Well, more
|
|
281
|
+
# specifically, we halt processing, return an object that will gzip our
|
|
282
|
+
# data on the fly without using ridiculous amounts of memory, and with the
|
|
283
|
+
# correct headers. It ends up being the changegroup, or a large bundled up
|
|
284
|
+
# set of changesets, for the client to add to its repo (or just examine).
|
|
285
|
+
def amp_get_changegroup(amp_repo)
|
|
286
|
+
headers = gzipped_response
|
|
287
|
+
|
|
288
|
+
nodes = []
|
|
289
|
+
if params["roots"]
|
|
290
|
+
nodes = params["roots"].split(" ").map {|i| i.unhexlify }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
result = DelayedGzipper.new do
|
|
294
|
+
amp_repo.changegroup(nodes, :serve)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
throw :halt, [200, headers, result]
|
|
298
|
+
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
##
|
|
302
|
+
# Command: changegroupsubset
|
|
303
|
+
# Requires an explicit capability: changegroupsubset
|
|
304
|
+
#
|
|
305
|
+
# Gets a given changegroup subset from the repository. Starts at the requested roots,
|
|
306
|
+
# and then goes to the heads given as parameters. This is how one might "slice"
|
|
307
|
+
# a repository, just as one slices an array with arr[3..7]. The "root" is 3, the
|
|
308
|
+
# "head" is 7. However, one can provide a number of roots or heads, as a mercurial
|
|
309
|
+
# repository is a DAG, and not a simple list of numbers such as 3..7.
|
|
310
|
+
#
|
|
311
|
+
# HTTP Param: roots. The roots of the trees we are requesting, in the form of
|
|
312
|
+
# a list of node IDs. the IDs are in hex, and separated by spaces.
|
|
313
|
+
# HTTP Param: heads. The heads of the slice of the trees we are requesting.
|
|
314
|
+
# The changegroup will stop being processed at the heads. In the form of a list of
|
|
315
|
+
# node IDs, each in hex, and separated by spaces.
|
|
316
|
+
#
|
|
317
|
+
# @param [Repository] amp_repo the repository from which we are requesting the
|
|
318
|
+
# changegroup subset.
|
|
319
|
+
# @return [String] the changegroup subset to be returned to the client. Well, more
|
|
320
|
+
# specifically, we halt processing, return an object that will gzip our
|
|
321
|
+
# data on the fly without using ridiculous amounts of memory, and with the
|
|
322
|
+
# correct headers. It ends up being the changegroup subset, or a large bundled up
|
|
323
|
+
# set of changesets, for the client to add to its repo (or just examine).
|
|
324
|
+
def amp_get_changegroupsubset(amp_repo)
|
|
325
|
+
headers = gzipped_response
|
|
326
|
+
|
|
327
|
+
bases, heads = [], []
|
|
328
|
+
|
|
329
|
+
bases = params["bases"].split(" ").map {|i| i.unhexlify } if params["bases"]
|
|
330
|
+
heads = params["heads"].split(" ").map {|i| i.unhexlify } if params["heads"]
|
|
331
|
+
|
|
332
|
+
result = DelayedGzipper.new do
|
|
333
|
+
amp_repo.changegroup_subset bases, heads, :serve
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
throw :halt, [200, headers, result]
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def amp_get_fake_writing(amp_repo)
|
|
340
|
+
"You're logged in!"
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
##
|
|
344
|
+
# Command: unbundle
|
|
345
|
+
#
|
|
346
|
+
# This command is used when a client wishes to push over HTTP. A bundle is posted
|
|
347
|
+
# as the request's data body.
|
|
348
|
+
#
|
|
349
|
+
# HTTP Method: post
|
|
350
|
+
# HTTP parameters: heads. The client repo's heads. Could be "force".hexlify, if
|
|
351
|
+
# the client is going to push anyway.
|
|
352
|
+
# HTTP post body: the bundled up set of changegroups.
|
|
353
|
+
#
|
|
354
|
+
# @param [Repository] amp_repo the repository to be pushed to
|
|
355
|
+
# @return [String] the results of the push, which are streamed to the client.
|
|
356
|
+
#
|
|
357
|
+
# @todo locking
|
|
358
|
+
# @todo finish this method!
|
|
359
|
+
def amp_get_unbundle(amp_repo)
|
|
360
|
+
their_heads = params["heads"].split(" ")
|
|
361
|
+
|
|
362
|
+
check_heads = proc do
|
|
363
|
+
heads = amp_repo.heads.map {|i| i.hexlify}
|
|
364
|
+
return their_heads == ["force".hexlify] || their_heads == heads
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
unless check_heads.call
|
|
368
|
+
throw :halt, [200, "unsynced changes"]
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
Tempfile.open("amp-unbundle-") do |fp|
|
|
372
|
+
length = request.content_length
|
|
373
|
+
fp.write request.body
|
|
374
|
+
|
|
375
|
+
unless check_heads.call
|
|
376
|
+
# in case our heads have changed in the last few milliseconds
|
|
377
|
+
throw :halt, [200, "unsynced changes"]
|
|
378
|
+
end
|
|
379
|
+
fp.seek(0, IO::SEEK_SET)
|
|
380
|
+
header = fp.read(6)
|
|
381
|
+
if header.start_with?("HG") && !header.start_with?("HG10")
|
|
382
|
+
raise ArgumentError.new("unknown bundle version")
|
|
383
|
+
elsif !Amp::RevlogSupport::ChangeGroup::BUNDLE_HEADERS.include?(header)
|
|
384
|
+
raise ArgumentError.new("unknown bundle compression type")
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
stream = Amp::RevlogSupport::ChangeGroup.unbundle(header, fp)
|
|
388
|
+
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
helpers AmpRepoMethods
|
|
395
|
+
register AmpExtension
|
|
396
|
+
end
|