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.
Files changed (295) hide show
  1. data/.gitignore +1 -0
  2. data/.hgignore +26 -0
  3. data/AUTHORS +2 -0
  4. data/History.txt +6 -0
  5. data/LICENSE +37 -0
  6. data/MANIFESTO +7 -0
  7. data/Manifest.txt +294 -0
  8. data/README.md +129 -0
  9. data/Rakefile +102 -0
  10. data/SCHEDULE.markdown +12 -0
  11. data/STYLE +27 -0
  12. data/TODO.markdown +149 -0
  13. data/ampfile.rb +47 -0
  14. data/bin/amp +30 -0
  15. data/bin/amp1.9 +30 -0
  16. data/ext/amp/bz2/README.txt +39 -0
  17. data/ext/amp/bz2/bz2.c +1582 -0
  18. data/ext/amp/bz2/extconf.rb +77 -0
  19. data/ext/amp/bz2/mkmf.log +29 -0
  20. data/ext/amp/mercurial_patch/extconf.rb +5 -0
  21. data/ext/amp/mercurial_patch/mpatch.c +405 -0
  22. data/ext/amp/priority_queue/extconf.rb +5 -0
  23. data/ext/amp/priority_queue/priority_queue.c +947 -0
  24. data/ext/amp/support/extconf.rb +5 -0
  25. data/ext/amp/support/support.c +250 -0
  26. data/lib/amp.rb +200 -0
  27. data/lib/amp/commands/command.rb +507 -0
  28. data/lib/amp/commands/command_support.rb +137 -0
  29. data/lib/amp/commands/commands/config.rb +143 -0
  30. data/lib/amp/commands/commands/help.rb +29 -0
  31. data/lib/amp/commands/commands/init.rb +10 -0
  32. data/lib/amp/commands/commands/templates.rb +137 -0
  33. data/lib/amp/commands/commands/version.rb +7 -0
  34. data/lib/amp/commands/commands/workflow.rb +28 -0
  35. data/lib/amp/commands/commands/workflows/git/add.rb +65 -0
  36. data/lib/amp/commands/commands/workflows/git/copy.rb +27 -0
  37. data/lib/amp/commands/commands/workflows/git/mv.rb +23 -0
  38. data/lib/amp/commands/commands/workflows/git/rm.rb +60 -0
  39. data/lib/amp/commands/commands/workflows/hg/add.rb +53 -0
  40. data/lib/amp/commands/commands/workflows/hg/addremove.rb +86 -0
  41. data/lib/amp/commands/commands/workflows/hg/annotate.rb +46 -0
  42. data/lib/amp/commands/commands/workflows/hg/archive.rb +126 -0
  43. data/lib/amp/commands/commands/workflows/hg/branch.rb +28 -0
  44. data/lib/amp/commands/commands/workflows/hg/branches.rb +30 -0
  45. data/lib/amp/commands/commands/workflows/hg/bundle.rb +115 -0
  46. data/lib/amp/commands/commands/workflows/hg/clone.rb +95 -0
  47. data/lib/amp/commands/commands/workflows/hg/commit.rb +42 -0
  48. data/lib/amp/commands/commands/workflows/hg/copy.rb +31 -0
  49. data/lib/amp/commands/commands/workflows/hg/debug/dirstate.rb +32 -0
  50. data/lib/amp/commands/commands/workflows/hg/debug/index.rb +36 -0
  51. data/lib/amp/commands/commands/workflows/hg/default.rb +9 -0
  52. data/lib/amp/commands/commands/workflows/hg/diff.rb +30 -0
  53. data/lib/amp/commands/commands/workflows/hg/forget.rb +11 -0
  54. data/lib/amp/commands/commands/workflows/hg/heads.rb +25 -0
  55. data/lib/amp/commands/commands/workflows/hg/identify.rb +23 -0
  56. data/lib/amp/commands/commands/workflows/hg/import.rb +135 -0
  57. data/lib/amp/commands/commands/workflows/hg/incoming.rb +85 -0
  58. data/lib/amp/commands/commands/workflows/hg/info.rb +18 -0
  59. data/lib/amp/commands/commands/workflows/hg/log.rb +21 -0
  60. data/lib/amp/commands/commands/workflows/hg/manifest.rb +13 -0
  61. data/lib/amp/commands/commands/workflows/hg/merge.rb +53 -0
  62. data/lib/amp/commands/commands/workflows/hg/move.rb +28 -0
  63. data/lib/amp/commands/commands/workflows/hg/outgoing.rb +61 -0
  64. data/lib/amp/commands/commands/workflows/hg/pull.rb +74 -0
  65. data/lib/amp/commands/commands/workflows/hg/push.rb +20 -0
  66. data/lib/amp/commands/commands/workflows/hg/remove.rb +45 -0
  67. data/lib/amp/commands/commands/workflows/hg/resolve.rb +83 -0
  68. data/lib/amp/commands/commands/workflows/hg/revert.rb +53 -0
  69. data/lib/amp/commands/commands/workflows/hg/root.rb +13 -0
  70. data/lib/amp/commands/commands/workflows/hg/serve.rb +38 -0
  71. data/lib/amp/commands/commands/workflows/hg/status.rb +116 -0
  72. data/lib/amp/commands/commands/workflows/hg/tag.rb +69 -0
  73. data/lib/amp/commands/commands/workflows/hg/tags.rb +27 -0
  74. data/lib/amp/commands/commands/workflows/hg/tip.rb +13 -0
  75. data/lib/amp/commands/commands/workflows/hg/update.rb +27 -0
  76. data/lib/amp/commands/commands/workflows/hg/verify.rb +9 -0
  77. data/lib/amp/commands/commands/workflows/hg/view.rb +36 -0
  78. data/lib/amp/commands/dispatch.rb +181 -0
  79. data/lib/amp/commands/hooks.rb +81 -0
  80. data/lib/amp/dependencies/amp_support.rb +1 -0
  81. data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +103 -0
  82. data/lib/amp/dependencies/minitar.rb +979 -0
  83. data/lib/amp/dependencies/priority_queue.rb +18 -0
  84. data/lib/amp/dependencies/priority_queue/c_priority_queue.rb +1 -0
  85. data/lib/amp/dependencies/priority_queue/poor_priority_queue.rb +46 -0
  86. data/lib/amp/dependencies/priority_queue/ruby_priority_queue.rb +525 -0
  87. data/lib/amp/dependencies/python_config.rb +211 -0
  88. data/lib/amp/dependencies/trollop.rb +713 -0
  89. data/lib/amp/dependencies/zip/ioextras.rb +155 -0
  90. data/lib/amp/dependencies/zip/stdrubyext.rb +111 -0
  91. data/lib/amp/dependencies/zip/tempfile_bugfixed.rb +186 -0
  92. data/lib/amp/dependencies/zip/zip.rb +1850 -0
  93. data/lib/amp/dependencies/zip/zipfilesystem.rb +609 -0
  94. data/lib/amp/dependencies/zip/ziprequire.rb +90 -0
  95. data/lib/amp/encoding/base85.rb +97 -0
  96. data/lib/amp/encoding/binary_diff.rb +82 -0
  97. data/lib/amp/encoding/difflib.rb +166 -0
  98. data/lib/amp/encoding/mercurial_diff.rb +378 -0
  99. data/lib/amp/encoding/mercurial_patch.rb +1 -0
  100. data/lib/amp/encoding/patch.rb +292 -0
  101. data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +123 -0
  102. data/lib/amp/extensions/ditz.rb +41 -0
  103. data/lib/amp/extensions/lighthouse.rb +167 -0
  104. data/lib/amp/graphs/ancestor.rb +147 -0
  105. data/lib/amp/graphs/copies.rb +261 -0
  106. data/lib/amp/merges/merge_state.rb +164 -0
  107. data/lib/amp/merges/merge_ui.rb +322 -0
  108. data/lib/amp/merges/simple_merge.rb +450 -0
  109. data/lib/amp/profiling_hacks.rb +36 -0
  110. data/lib/amp/repository/branch_manager.rb +234 -0
  111. data/lib/amp/repository/dir_state.rb +950 -0
  112. data/lib/amp/repository/journal.rb +203 -0
  113. data/lib/amp/repository/lock.rb +207 -0
  114. data/lib/amp/repository/repositories/bundle_repository.rb +214 -0
  115. data/lib/amp/repository/repositories/http_repository.rb +377 -0
  116. data/lib/amp/repository/repositories/local_repository.rb +2661 -0
  117. data/lib/amp/repository/repository.rb +94 -0
  118. data/lib/amp/repository/store.rb +485 -0
  119. data/lib/amp/repository/tag_manager.rb +319 -0
  120. data/lib/amp/repository/updatable.rb +532 -0
  121. data/lib/amp/repository/verification.rb +431 -0
  122. data/lib/amp/repository/versioned_file.rb +475 -0
  123. data/lib/amp/revlogs/bundle_revlogs.rb +246 -0
  124. data/lib/amp/revlogs/changegroup.rb +217 -0
  125. data/lib/amp/revlogs/changelog.rb +338 -0
  126. data/lib/amp/revlogs/changeset.rb +521 -0
  127. data/lib/amp/revlogs/file_log.rb +165 -0
  128. data/lib/amp/revlogs/index.rb +493 -0
  129. data/lib/amp/revlogs/manifest.rb +195 -0
  130. data/lib/amp/revlogs/node.rb +18 -0
  131. data/lib/amp/revlogs/revlog.rb +1032 -0
  132. data/lib/amp/revlogs/revlog_support.rb +126 -0
  133. data/lib/amp/server/amp_user.rb +44 -0
  134. data/lib/amp/server/extension/amp_extension.rb +396 -0
  135. data/lib/amp/server/extension/authorization.rb +201 -0
  136. data/lib/amp/server/fancy_http_server.rb +252 -0
  137. data/lib/amp/server/fancy_views/_browser.haml +28 -0
  138. data/lib/amp/server/fancy_views/_diff_file.haml +13 -0
  139. data/lib/amp/server/fancy_views/_navbar.haml +17 -0
  140. data/lib/amp/server/fancy_views/changeset.haml +31 -0
  141. data/lib/amp/server/fancy_views/commits.haml +32 -0
  142. data/lib/amp/server/fancy_views/file.haml +35 -0
  143. data/lib/amp/server/fancy_views/file_diff.haml +23 -0
  144. data/lib/amp/server/fancy_views/harshcss/all_hallows_eve.css +72 -0
  145. data/lib/amp/server/fancy_views/harshcss/amy.css +147 -0
  146. data/lib/amp/server/fancy_views/harshcss/twilight.css +138 -0
  147. data/lib/amp/server/fancy_views/stylesheet.sass +175 -0
  148. data/lib/amp/server/http_server.rb +140 -0
  149. data/lib/amp/server/repo_user_management.rb +287 -0
  150. data/lib/amp/support/amp_config.rb +164 -0
  151. data/lib/amp/support/amp_ui.rb +287 -0
  152. data/lib/amp/support/docs.rb +54 -0
  153. data/lib/amp/support/generator.rb +78 -0
  154. data/lib/amp/support/ignore.rb +144 -0
  155. data/lib/amp/support/loaders.rb +93 -0
  156. data/lib/amp/support/logger.rb +103 -0
  157. data/lib/amp/support/match.rb +151 -0
  158. data/lib/amp/support/multi_io.rb +87 -0
  159. data/lib/amp/support/openers.rb +121 -0
  160. data/lib/amp/support/ruby_19_compatibility.rb +66 -0
  161. data/lib/amp/support/support.rb +1095 -0
  162. data/lib/amp/templates/blank.commit.erb +23 -0
  163. data/lib/amp/templates/blank.log.erb +18 -0
  164. data/lib/amp/templates/default.commit.erb +23 -0
  165. data/lib/amp/templates/default.log.erb +26 -0
  166. data/lib/amp/templates/template.rb +165 -0
  167. data/site/Rakefile +24 -0
  168. data/site/src/about/ampfile.haml +57 -0
  169. data/site/src/about/commands.haml +106 -0
  170. data/site/src/about/index.haml +33 -0
  171. data/site/src/about/performance.haml +31 -0
  172. data/site/src/about/workflows.haml +34 -0
  173. data/site/src/contribute/index.haml +65 -0
  174. data/site/src/contribute/style.haml +297 -0
  175. data/site/src/css/active4d.css +114 -0
  176. data/site/src/css/all_hallows_eve.css +72 -0
  177. data/site/src/css/all_themes.css +3299 -0
  178. data/site/src/css/amp.css +260 -0
  179. data/site/src/css/amy.css +147 -0
  180. data/site/src/css/blackboard.css +88 -0
  181. data/site/src/css/brilliance_black.css +605 -0
  182. data/site/src/css/brilliance_dull.css +599 -0
  183. data/site/src/css/cobalt.css +149 -0
  184. data/site/src/css/cur_amp.css +185 -0
  185. data/site/src/css/dawn.css +121 -0
  186. data/site/src/css/eiffel.css +121 -0
  187. data/site/src/css/espresso_libre.css +109 -0
  188. data/site/src/css/idle.css +62 -0
  189. data/site/src/css/iplastic.css +80 -0
  190. data/site/src/css/lazy.css +73 -0
  191. data/site/src/css/mac_classic.css +123 -0
  192. data/site/src/css/magicwb_amiga.css +104 -0
  193. data/site/src/css/pastels_on_dark.css +188 -0
  194. data/site/src/css/reset.css +55 -0
  195. data/site/src/css/slush_poppies.css +85 -0
  196. data/site/src/css/spacecadet.css +51 -0
  197. data/site/src/css/sunburst.css +180 -0
  198. data/site/src/css/twilight.css +137 -0
  199. data/site/src/css/zenburnesque.css +91 -0
  200. data/site/src/get/index.haml +32 -0
  201. data/site/src/helpers.rb +121 -0
  202. data/site/src/images/amp_logo.png +0 -0
  203. data/site/src/images/carbonica.png +0 -0
  204. data/site/src/images/revolution.png +0 -0
  205. data/site/src/images/tab-bg.png +0 -0
  206. data/site/src/images/tab-sliding-left.png +0 -0
  207. data/site/src/images/tab-sliding-right.png +0 -0
  208. data/site/src/include/_footer.haml +22 -0
  209. data/site/src/include/_header.haml +17 -0
  210. data/site/src/index.haml +104 -0
  211. data/site/src/learn/index.haml +46 -0
  212. data/site/src/scripts/jquery-1.3.2.min.js +19 -0
  213. data/site/src/scripts/jquery.cookie.js +96 -0
  214. data/tasks/stats.rake +155 -0
  215. data/tasks/yard.rake +171 -0
  216. data/test/dirstate_tests/dirstate +0 -0
  217. data/test/dirstate_tests/hgrc +5 -0
  218. data/test/dirstate_tests/test_dir_state.rb +192 -0
  219. data/test/functional_tests/resources/.hgignore +2 -0
  220. data/test/functional_tests/resources/STYLE.txt +25 -0
  221. data/test/functional_tests/resources/command.rb +372 -0
  222. data/test/functional_tests/resources/commands/annotate.rb +57 -0
  223. data/test/functional_tests/resources/commands/experimental/lolcats.rb +17 -0
  224. data/test/functional_tests/resources/commands/heads.rb +22 -0
  225. data/test/functional_tests/resources/commands/manifest.rb +12 -0
  226. data/test/functional_tests/resources/commands/status.rb +90 -0
  227. data/test/functional_tests/resources/version2/.hgignore +5 -0
  228. data/test/functional_tests/resources/version2/STYLE.txt +25 -0
  229. data/test/functional_tests/resources/version2/command.rb +372 -0
  230. data/test/functional_tests/resources/version2/commands/annotate.rb +45 -0
  231. data/test/functional_tests/resources/version2/commands/experimental/lolcats.rb +17 -0
  232. data/test/functional_tests/resources/version2/commands/heads.rb +22 -0
  233. data/test/functional_tests/resources/version2/commands/manifest.rb +12 -0
  234. data/test/functional_tests/resources/version2/commands/status.rb +90 -0
  235. data/test/functional_tests/resources/version3/.hgignore +5 -0
  236. data/test/functional_tests/resources/version3/STYLE.txt +31 -0
  237. data/test/functional_tests/resources/version3/command.rb +376 -0
  238. data/test/functional_tests/resources/version3/commands/annotate.rb +45 -0
  239. data/test/functional_tests/resources/version3/commands/experimental/lolcats.rb +17 -0
  240. data/test/functional_tests/resources/version3/commands/heads.rb +22 -0
  241. data/test/functional_tests/resources/version3/commands/manifest.rb +12 -0
  242. data/test/functional_tests/resources/version3/commands/status.rb +90 -0
  243. data/test/functional_tests/resources/version4/.hgignore +5 -0
  244. data/test/functional_tests/resources/version4/STYLE.txt +31 -0
  245. data/test/functional_tests/resources/version4/command.rb +376 -0
  246. data/test/functional_tests/resources/version4/commands/experimental/lolcats.rb +17 -0
  247. data/test/functional_tests/resources/version4/commands/heads.rb +22 -0
  248. data/test/functional_tests/resources/version4/commands/manifest.rb +12 -0
  249. data/test/functional_tests/resources/version4/commands/stats.rb +25 -0
  250. data/test/functional_tests/resources/version4/commands/status.rb +90 -0
  251. data/test/functional_tests/resources/version5_1/.hgignore +5 -0
  252. data/test/functional_tests/resources/version5_1/STYLE.txt +2 -0
  253. data/test/functional_tests/resources/version5_1/command.rb +374 -0
  254. data/test/functional_tests/resources/version5_1/commands/experimental/lolcats.rb +17 -0
  255. data/test/functional_tests/resources/version5_1/commands/heads.rb +22 -0
  256. data/test/functional_tests/resources/version5_1/commands/manifest.rb +12 -0
  257. data/test/functional_tests/resources/version5_1/commands/stats.rb +25 -0
  258. data/test/functional_tests/resources/version5_1/commands/status.rb +90 -0
  259. data/test/functional_tests/resources/version5_2/.hgignore +5 -0
  260. data/test/functional_tests/resources/version5_2/STYLE.txt +14 -0
  261. data/test/functional_tests/resources/version5_2/command.rb +376 -0
  262. data/test/functional_tests/resources/version5_2/commands/experimental/lolcats.rb +17 -0
  263. data/test/functional_tests/resources/version5_2/commands/manifest.rb +12 -0
  264. data/test/functional_tests/resources/version5_2/commands/newz.rb +12 -0
  265. data/test/functional_tests/resources/version5_2/commands/stats.rb +25 -0
  266. data/test/functional_tests/resources/version5_2/commands/status.rb +90 -0
  267. data/test/functional_tests/test_functional.rb +604 -0
  268. data/test/localrepo_tests/test_local_repo.rb +121 -0
  269. data/test/localrepo_tests/testrepo.tar.gz +0 -0
  270. data/test/manifest_tests/00manifest.i +0 -0
  271. data/test/manifest_tests/test_manifest.rb +72 -0
  272. data/test/merge_tests/base.txt +10 -0
  273. data/test/merge_tests/expected.local.txt +16 -0
  274. data/test/merge_tests/local.txt +11 -0
  275. data/test/merge_tests/remote.txt +11 -0
  276. data/test/merge_tests/test_merge.rb +26 -0
  277. data/test/revlog_tests/00changelog.i +0 -0
  278. data/test/revlog_tests/revision_added_changelog.i +0 -0
  279. data/test/revlog_tests/test_adding_index.i +0 -0
  280. data/test/revlog_tests/test_revlog.rb +333 -0
  281. data/test/revlog_tests/testindex.i +0 -0
  282. data/test/store_tests/store.tar.gz +0 -0
  283. data/test/store_tests/test_fncache_store.rb +122 -0
  284. data/test/test_amp.rb +9 -0
  285. data/test/test_base85.rb +14 -0
  286. data/test/test_bdiff.rb +42 -0
  287. data/test/test_commands.rb +122 -0
  288. data/test/test_difflib.rb +50 -0
  289. data/test/test_helper.rb +15 -0
  290. data/test/test_journal.rb +29 -0
  291. data/test/test_match.rb +134 -0
  292. data/test/test_mdiff.rb +74 -0
  293. data/test/test_mpatch.rb +14 -0
  294. data/test/test_support.rb +24 -0
  295. metadata +385 -0
@@ -0,0 +1,246 @@
1
+ module Amp
2
+ module Bundles
3
+ ##
4
+ # This module handles revlogs passed to our client (or server)
5
+ # through the bundle file format. Thing is, this revision log
6
+ # spans both a physical filelog, and a bundle (the new revisions),
7
+ # and we might need to get stuff from both. It's kind of like the
8
+ # DelayedOpener/FakeAppender for changelogs.
9
+ module BundleRevlog
10
+ include RevlogSupport::Node
11
+ BUNDLED_INDEX_ENTRY_SIZE = 80
12
+
13
+ ##
14
+ # Initializes a bundle revlog. Takes, in addition to the normal
15
+ # revlog arguments, a bundle_file. This is any IO we can read
16
+ # from that will give us additional revisions, aside from the
17
+ # revisions stored in the real Revlog. It also takes a link_mapper
18
+ # that will connect things to the changelog revisions (including
19
+ # changelog revisions in the bundle).
20
+ #
21
+ # @param [Opener] opener the opener to use for openinf up the index_file
22
+ # @param [String] index_file the name of the file containing the revlog's
23
+ # index
24
+ # @param [IO] bundle_file an IO that we can #read from
25
+ # @param [Proc, #call] link_mapper a function that will give us the link-index
26
+ # to connect revisions to changelog revisions based on node_ids
27
+ def bundle_initialize(opener, index_file, bundle_file, link_mapper = nil)
28
+ @bundle_file = bundle_file
29
+ @base_map = {}
30
+
31
+ num_revs = self.index_size
32
+ previous = nil
33
+ all_chunk_positions do |chunk, start|
34
+ chunk_size = chunk.size
35
+
36
+ # each chunk starts with 4 node IDs: the new node's ID, its 2 parent node IDs,
37
+ # and the node ID of the corresponding revision in the changelog. In that order.
38
+ # If we have less than 80 bytes (BUNDLED_INDEX_ENTRY_SIZE), then we're fucked.
39
+ if chunk_size < BUNDLED_INDEX_ENTRY_SIZE
40
+ raise abort("invalid changegroup")
41
+ end
42
+
43
+ start += BUNDLED_INDEX_ENTRY_SIZE
44
+ chunk_size -= BUNDLED_INDEX_ENTRY_SIZE
45
+
46
+ # Get the aforementioned node IDs
47
+ node, parent_1, parent_2, changeset = chunk[0..79].unpack("a20a20a20a20")
48
+
49
+ # Do we already have this node? Skip it.
50
+ if @index.has_node? node
51
+ previous = node
52
+ next
53
+ end
54
+
55
+ # make sure we have the new node's parents, or all of our operations will fail!
56
+ # at least, the interesting ones.
57
+ [parent_1, parent_2].each do |parent|
58
+ unless @index.has_node? parent
59
+ raise abort("Unknown parent: #{parent}@#{index_file}")
60
+ end
61
+ end
62
+
63
+ link_rev = (link_mapper && link_mapper[changeset]) || num_revs
64
+ previous ||= parent_1
65
+
66
+ @index << [RevlogSupport::Support.offset_version(start, 0), chunk_size, -1, -1, link_rev,
67
+ revision_index_for_node(parent_1), revision_index_for_node(parent_2),
68
+ node]
69
+
70
+ @index.node_map[node] = num_revs
71
+ @base_map[num_revs] = previous
72
+
73
+ previous = node
74
+ num_revs += 1
75
+ end
76
+ end
77
+ alias_method :bundle_revlog_initialize, :initialize
78
+ ##
79
+ # Returns whether the revision index is in the bundle part of this revlog,
80
+ # or if it's in the actual, stored revlog file.
81
+ #
82
+ # @param [Fixnum] revision the revision index to lookup
83
+ # @return [Boolean] is the revision in the bundle section?
84
+ def bundled_revision?(revision)
85
+ return false if revision < 0
86
+ !!@base_map[revision]
87
+ end
88
+
89
+ ##
90
+ # Returns the base revision for the revision at the given index, while being
91
+ # cognizant of the bundle-ness of this revlog.
92
+ #
93
+ # @param [Fixnum] revision the revision index to lookup the base-revision of
94
+ # @return [String] the revision node ID of the base for the requested revision
95
+ def bundled_base_revision_for_index(revision)
96
+ @base_map[revision] || base_revision_for_index(revision)
97
+ end
98
+ alias_method :bundled_base, :bundled_base_revision_for_index
99
+
100
+ ##
101
+ # Gets a chunk of data from the datafile (or, if inline, from the index
102
+ # file). Just give it a revision index and which data file to use. Only difference
103
+ # is that this will check the bundlefile if necessary.
104
+ #
105
+ # @param [Fixnum] rev the revision index to extract
106
+ # @param [IO] data_file The IO file descriptor for loading data
107
+ # @return [String] the raw data from the index (posssibly compressed)
108
+ def get_chunk(revision, datafile=nil, cache_len = 4096)
109
+ # Warning: in case of bundle, the diff is against bundlebase, not against
110
+ # rev - 1
111
+ # TODO: could use some caching
112
+ unless bundled_revision?(revision)
113
+ return super(revision, datafile)
114
+ end
115
+ @bundle_file.seek data_start_for_index(revision)
116
+ @bundle_file.read self[revision].compressed_len
117
+ end
118
+
119
+ ##
120
+ # Diffs 2 revisions, based on their indices. They are returned in
121
+ # BinaryDiff format.
122
+ #
123
+ # @param [Fixnum] rev1 the index of the source revision
124
+ # @param [Fixnum] rev2 the index of the destination revision
125
+ # @return [String] The diff of the 2 revisions.
126
+ def revision_diff(rev1, rev2)
127
+ both_bundled = bundled_revision?(rev1) && bundled_revision?(rev2)
128
+ if both_bundled
129
+ # super-quick path if both are bundled and rev2 == rev1 + 1 diff
130
+ revision_base = self.revision_index_for_node bundled_base_revision_for_index(rev2)
131
+ if revision_base == rev1
132
+ # if rev2 = rev1 + a diff, just get the diff!
133
+ return get_chunk(revision2)
134
+ end
135
+ end
136
+ # normal style
137
+ return super(rev1, rev2)
138
+ end
139
+
140
+ ##
141
+ # Given a node ID, extracts that revision and decompresses it. What you get
142
+ # back will the pristine revision data! Checks for bundle-ness when we access
143
+ # a node.
144
+ #
145
+ # @param [String] node the Node ID of the revision to extract.
146
+ # @return [String] the pristine revision data.
147
+ def decompress_revision(node)
148
+ return "" if node == NULL_ID
149
+
150
+ text = nil
151
+ chain = []
152
+ iter_node = node
153
+ rev = revision_index_for_node(node)
154
+ # walk backwards down the chain. Every single node is going
155
+ # to be a diff, because it's from a bundle.
156
+ while bundled_revision? rev
157
+ if @index.cache && @index.cache[0] == iter_node
158
+ text = @index.cache[2]
159
+ break
160
+ end
161
+ chain << rev
162
+ iter_node = bundled_base_revision_for_index rev
163
+ rev = revision_index_for_node iter_node
164
+ end
165
+ # done walking back, see if we have a stored cache!
166
+ text = super(iter_node) if text.nil? || text.empty?
167
+
168
+ while chain.any?
169
+ delta = get_chunk(chain.pop)
170
+ text = Diffs::MercurialPatch.apply_patches(text, [delta])
171
+ end
172
+ p1, p2 = parents_for_node node
173
+
174
+ if node != RevlogSupport::Support.history_hash(text, p1, p2)
175
+ raise RevlogSupport::RevlogError.new("integrity check failed on %s:%d, data:%s" %
176
+ [(@index.inline? ? @index_file : @data_file), rev(node), text.inspect])
177
+ end
178
+
179
+ @index.cache = [node, revision_index_for_node(node), text]
180
+ text
181
+ end
182
+
183
+ ##
184
+ # Give an error to enforce read-only behavior
185
+ def add_revision(text, transaction, link, p1, p2, d=nil, index_file_handle = nil)
186
+ raise NotImplementedError.new
187
+ end
188
+
189
+ ##
190
+ # Give an error to enforce read-only behavior
191
+ def add_group(revisions, link_mapper, journal)
192
+ raise NotImplementedError.new
193
+ end
194
+
195
+
196
+ private
197
+
198
+ def all_chunk_positions
199
+ results = []
200
+ RevlogSupport::ChangeGroup.each_chunk(@bundle_file) do |chunk|
201
+ if block_given?
202
+ yield(chunk, @bundle_file.tell - chunk.size)
203
+ else
204
+ results << [chunk, @bundle_file.tell - chunk.size]
205
+ end
206
+ end
207
+ results unless block_given?
208
+ end
209
+ end
210
+
211
+ class BundleChangeLog < ChangeLog
212
+ include BundleRevlog
213
+ def initialize(opener, bundle_file)
214
+ # This is the changelog initializer
215
+ super(opener)
216
+ # This is the bundle initializer
217
+ bundle_initialize(opener, @index_file, bundle_file)
218
+ end
219
+ end
220
+
221
+ class BundleManifest < Manifest
222
+ include BundleRevlog
223
+
224
+ def initialize(opener, bundle_file, link_mapper)
225
+ # This is the manifest initializer
226
+ super(opener)
227
+ # This is the bundle initializer
228
+ bundle_initialize(opener, @index_file, bundle_file, link_mapper)
229
+ end
230
+
231
+ end
232
+
233
+ class BundleFileLog < FileLog
234
+ include BundleRevlog
235
+
236
+ def initialize(opener, path, bundle_file, link_mapper)
237
+ # This is the manifest initializer
238
+ super(opener, path)
239
+ # This is the bundle initializer
240
+ bundle_initialize(opener, @index_file, bundle_file, link_mapper)
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+ end
@@ -0,0 +1,217 @@
1
+ require 'tempfile'
2
+ module Amp
3
+ module RevlogSupport
4
+
5
+ ##
6
+ # This class handles changegroups - most specifically, packaging up a bunch
7
+ # of revisions into a single bundled file.
8
+ #
9
+ module ChangeGroup
10
+ BUNDLE_HEADERS = {
11
+ "" => "",
12
+ "HG10UN" => "HG10UN",
13
+ "HG10BZ" => "HG10",
14
+ "HG10GZ" => "HG10GZ"
15
+ }
16
+ FORMAT_PRIORITIES = BUNDLE_TYPES = ["HG10GZ", "HG10BZ", "HG10UN"]
17
+ class ChangeGroupError < StandardError; end
18
+
19
+ ##
20
+ # Loads a single chunk of data from a changegroup. Each chunk is stored in the
21
+ # changegroup as:
22
+ # (uint32_t) length <-- less than 4 if we should terminate
23
+ # (length * char) body
24
+ # Example:
25
+ # 00 00 00 05 h e l l o
26
+ #
27
+ # @param [IO, #read] source the source stream with all the data that we'll be
28
+ # working on.
29
+ # @return [String] the data read in. Will be either nil or the empty string if
30
+ # no data was read.
31
+ def self.get_chunk(source)
32
+ data = source.read 4
33
+ return "" if data.nil? || data.empty?
34
+ l = data.unpack("N")[0]
35
+ return "" if l <= 4
36
+ data = source.read(l - 4)
37
+ if data.size < l - 4
38
+ raise ChangeGroupError.new("premature EOF when reading changegroup:" +
39
+ "(got #{data.size} bytes, expected #{l-4})")
40
+ end
41
+ return data
42
+ end
43
+
44
+ ##
45
+ # Loads each chunk, in a row, until we run out of chunks in the changegroup.
46
+ # Yields the data to the caller. This will run until we hit a terminating chunk.
47
+ #
48
+ # @param [IO, #read] source The stream from which we will read the data in sequence
49
+ # @yield Each chunk of data will be yielded to be processed
50
+ # @yieldparam [String] chunk the chunk that is yielded
51
+ def self.each_chunk(source)
52
+ begin
53
+ c = self.get_chunk(source) # get a chunk
54
+ yield c unless c.empty? # yield if not empty
55
+ end until c.empty? # keep going if we have more!
56
+ end
57
+
58
+ ##
59
+ # If we have data of size +size+, then return the encoded header for the chunk
60
+ #
61
+ # @param [Integer] size the size of the chunk we wish to encode
62
+ # @return [String] the encoded header for the chunk
63
+ def self.chunk_header(size)
64
+ [size + 4].pack("N")
65
+ end
66
+
67
+ ##
68
+ # The terminating chunk that indicates the end of a chunk sequence
69
+ #
70
+ # @return [String] the encoded terminating chunk
71
+ def self.closing_chunk
72
+ "\000\000\000\000" # [0].pack("N")
73
+ end
74
+
75
+ ##
76
+ # Returns a compressing stream based on the header for a changegroup bundle.
77
+ # The bundle header will specify that the contents should be either uncompressed,
78
+ # BZip compressed, or GZip compressed. This will return a stream that responds
79
+ # to #<< and #flush, where #flush will return the unread, decompressed data,
80
+ # and #<< will input uncompressed data.
81
+ #
82
+ # @param [String] header the header for the changegroup bundle. Can be either
83
+ # HG10UN, HG10GZ, or HG10BZ, or HG10.
84
+ # @return [IO, #<<, #flush] an IO stream that accepts uncompressed data via #<< or
85
+ # #write, and returns compressed data by #flush.
86
+ def self.compressor_by_type(header)
87
+ case header
88
+ when "HG10UN", ""
89
+ # new StringIO
90
+ result = StringIO.new "",(ruby_19? ? "w+:ASCII-8BIT" : "w+")
91
+ # have to fix the fact that StringIO doesn't conform to the other streams,
92
+ # and give it a #flush method. Kind of hackish.
93
+ class << result
94
+ def flush
95
+ ret = self.string.dup # get the current read-in string
96
+ self.string.replace "" # erase our contents
97
+ self.rewind # rewind the IO
98
+ self.tell
99
+ ret # return the string
100
+ end
101
+ end
102
+ #return the altered StringIO
103
+ result
104
+ when "HG10GZ"
105
+ # lazy-load Zlib
106
+ require 'zlib'
107
+ # Return a deflating stream (compressor)
108
+ Zlib::Deflate.new
109
+ when "HG10BZ", "HG10"
110
+ # lazy load BZip
111
+ need { '../../../ext/amp/bz2/bz2' }
112
+ # Return a compressing BZip stream
113
+ BZ2::Writer.new
114
+ end
115
+ end
116
+
117
+ ##
118
+ # Returns a stream that will decompress the IO pointed to by file_handle,
119
+ # when #read is called upon it. Note: file_handle doesn't have to be a file!
120
+ #
121
+ # @param [String] header the header of the stream. Specifies which compression
122
+ # to handle
123
+ # @param [IO, #read] file_handle the input stream that will provide the compressed
124
+ # data.
125
+ # @return [IO, #read] an IO object that we can #read to get uncompressed data
126
+ def self.unbundle(header, file_handle)
127
+ # uncompressed? just return the input IO!
128
+ return file_handle if header == "HG10UN"
129
+ # if we have no header, we're uncompressed
130
+ if !header.start_with?("HG")
131
+ # append the header to it. meh
132
+ headerio = StringIO.new(header, (ruby_19? ? "w+:ASCII-8BIT" : "w+"))
133
+ Amp::Support::MultiIO.new(headerio, file_handle)
134
+ # WOW we have legacy support already
135
+ elsif header == "HG10GZ"
136
+ # Get a gzip reader
137
+ Zlib::GzipReader.new(file_handle)
138
+ elsif header == "HG10BZ"
139
+ # get a BZip reader, but it has to decompress "BZ" first. Meh.
140
+ headerio = StringIO.new("BZ", (ruby_19? ? "w+:ASCII-8BIT" : "w+"))
141
+ input = Amp::Support::MultiIO.new(headerio, file_handle)
142
+ BZ2::Reader.new(input)
143
+ end
144
+ end
145
+
146
+ ##
147
+ # Writes a set of changegroups to a bundle. If no IO is specified, a new StringIO
148
+ # is created, and the bundle is written to that (i.e., memory). the IO used is returned.
149
+ #
150
+ # @param [IO, #read, #seek] changegroup A stream that will feed in an uncompressed
151
+ # changegroup
152
+ # @param [String] bundletype A specified compression type - either "HG10UN", "HG10GZ",
153
+ # or "HG10BZ". The empty string defaults to "HG10UN".
154
+ # @param [IO, #write] fh (StringIO.new) An output stream to write to, such as a File
155
+ # or a socket. If not specified, a StringIO is created and returned.
156
+ # @return [IO, #write] the output IO stream is returned, even if a new one is not
157
+ # created on the fly.
158
+ def self.write_bundle(changegroup, bundletype, fh = StringIO.new("", (ruby_19? ? "w+:ASCII-8BIT" : "w+")))
159
+ # rewind the changegroup to start at the beginning
160
+ changegroup.rewind
161
+ # pick out our header
162
+ header = BUNDLE_HEADERS[bundletype]
163
+ # get a compressing stream
164
+ compressor = compressor_by_type header
165
+ # output the header (uncompressed)
166
+ fh.write header
167
+
168
+ # These 2 variables are for checking to see if #changegroup has been fully
169
+ # read in or not.
170
+ empty = false
171
+ count = 0
172
+
173
+ # Do at least 2 changegroups (changelog + manifest), then go until we're empty
174
+ while !empty || count <= 2
175
+ # Set empty to true for this particular file (each iteration of this loop
176
+ # represents compressing 1 file's changesets into changegroups in the bundle)
177
+ empty = true
178
+ # Add 1 to the number of files we've comrpessed
179
+ count += 1
180
+ # For each chunk in the changegroup (i.e. each changeset)
181
+ inner_count = 0
182
+ self.each_chunk(changegroup) do |chunk|
183
+
184
+ #puts "\t\twrite_bundle inner loop count #{inner_count}"
185
+ inner_count += 1
186
+ empty = false
187
+ # Compress the chunk header
188
+ compressor << chunk_header(chunk.size)
189
+ # Write the chunk header
190
+ fh.write(compressor.flush)
191
+
192
+ # compress the actual chunk 1 megabyte at a time
193
+ step_amt = 1048576
194
+ (0..chunk.size).step(step_amt) do |pos|
195
+ compressor << chunk[pos..(pos+step_amt-1)]
196
+ fh.write(compressor.flush)
197
+ fh.flush
198
+ end
199
+ end
200
+ # Compress the terminating chunk - this indicates that there are no more changesets
201
+ # for the current file
202
+ compressor << closing_chunk
203
+ # Write the terminating chunk out!
204
+ fh.write compressor.flush
205
+ fh.flush
206
+ end
207
+
208
+ # Write anything left over in that there compressor
209
+ fh.write compressor.flush
210
+ # Kill the compressor
211
+ compressor.close
212
+ # Return the IO we wrote to (in case we instantiated it)
213
+ return fh
214
+ end
215
+ end
216
+ end
217
+ end