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,165 @@
1
+ module Amp
2
+ ##
3
+ # = FileLog
4
+ # A FileLog is the revision log that stores revision history for
5
+ # each individual file tracked by the system. It stores special meta-data
6
+ # for handling files that have been copied over their history.
7
+ #
8
+ class FileLog < Revlog
9
+ ##
10
+ # Initializes the revision log, being sure to encode directories
11
+ # to avoid naming conflicts
12
+ # @param [Opener] opener the opener to use for opening the file
13
+ # @param [String] path the path to the file, excluding "data".
14
+ #
15
+ def initialize(opener, path)
16
+ super(opener, ["data", encode_dir(path + ".i")].join("/"))
17
+ end
18
+
19
+ ##
20
+ # Encodes the directory to avoid naming conflicts
21
+ # @param [String] path the path to encode for naming conflict issues
22
+ # @return [String] the encoded directory path
23
+ #
24
+ def encode_dir(path)
25
+ path.gsub(".hg/",".hg.hg/").gsub(".i/",".i.hg/").gsub(".d/",".d.hg/")
26
+ end
27
+
28
+ ##
29
+ # Decodes the directory to avoid naming conflicts
30
+ # @param [String] path the path to decode for naming conflict issues
31
+ # @return [String] the decoded directory path
32
+ #
33
+ def decode_dir(path)
34
+ path.gsub(".d.hg/",".d/").gsub(".i.hg/",".i/").gsub(".hg.hg/",".hg/")
35
+ end
36
+
37
+ ##
38
+ # Reads the data of the revision, ignoring the meta data for copied files
39
+ # @param [String] node the node_id to read
40
+ # @return [String] the data of the revision
41
+ #
42
+ def read(node)
43
+ t = decompress_revision(node)
44
+ return t unless t.start_with?("\1\n")
45
+
46
+ start = t.index("\1\n", 2)
47
+ t[(start+2)..-1]
48
+ end
49
+
50
+ ##
51
+ # Reads the meta data in the node
52
+ # @param [String] node the node_id to read the meta of
53
+ # @return [Hash] the meta data in this revision. Could be empty hash.
54
+ #
55
+ def read_meta(node)
56
+ t = decompress_revision(node)
57
+ return {} unless t.start_with?("\1\n")
58
+
59
+ start = t.index("\1\n", 2)
60
+ mt = t[2..(start-1)]
61
+ m = {}
62
+ mt.split("\n").each do |l|
63
+ k, v = l.split(": ", 2)
64
+ m[k] = v
65
+ end
66
+ m
67
+ end
68
+
69
+ ##
70
+ # Adds a revision to the file's history. Overridden for special metadata
71
+ #
72
+ # @param [String] text the new text of the file
73
+ # @param [Hash] meta the meta data to use (if we copied)
74
+ # @param [Journal] journal for aborting transaction
75
+ # @param [Integer] link the revision number this is linked to
76
+ # @param [Integer] p1 (nil) the first parent of this new revision
77
+ # @param [Integer] p2 (nil) the second parent of this new revision
78
+ # @param [String] digest referring to the node this makes
79
+ def add(text, meta, journal, link, p1=nil, p2=nil)
80
+ if (meta && meta.any?) || text.start_with?("\1\n")
81
+ mt = ""
82
+ mt = meta.map {|k, v| "#{k}: #{v}\n"} if meta
83
+ text = "\1\n" + mt.join + "\1\n" + text
84
+ end
85
+ add_revision(text, journal, link, p1, p2)
86
+ end
87
+
88
+ ##
89
+ # Returns whether or not the file at _node_ has been renamed or
90
+ # copied.
91
+ #
92
+ # @param [String] node the node_id of the revision
93
+ # @return [Boolean] has the file been renamed or copied at this
94
+ # revision?
95
+ def renamed(node)
96
+ return false if parents_for_node(node).first != NULL_ID
97
+
98
+ m = read_meta node
99
+
100
+ if m.any? && m["copy"]
101
+ return [m["copy"], m["copyrev"].unhexlify]
102
+ end
103
+
104
+ false
105
+ end
106
+ alias_method :renamed?, :renamed
107
+
108
+ ##
109
+ # Yields a block for every revision, while being sure to follow copies.
110
+ def each(&block)
111
+ if @index[0].parent_one_rev == NULL_REV
112
+ meta_info = renamed(@index[0].node_id)
113
+ if meta_info
114
+ copied_log = FileLog.new(@opener, meta_info.first)
115
+ copied_log.each(&block)
116
+ end
117
+ end
118
+ super(&block)
119
+ end
120
+
121
+ ##
122
+ # Unified diffs 2 revisions, based on their indices. They are returned in a sexified
123
+ # unified diff format.
124
+ def unified_revision_diff(rev1, old_date, rev2, new_date, path1, path2, opts={})
125
+ opts = Diffs::MercurialDiff::DEFAULT_OPTIONS.merge(opts)
126
+ version_1 = rev1 ? read(self.node_id_for_index(rev1)) : nil
127
+ version_2 = rev2 ? read(self.node_id_for_index(rev2)) : nil
128
+
129
+ Diffs::MercurialDiff.unified_diff( version_1, old_date, version_2, new_date,
130
+ path1, path2, false, opts)
131
+ end
132
+
133
+ ##
134
+ # Gets the size of the file. Overridden because of the metadata for
135
+ # copied files.
136
+ #
137
+ # @param [Integer] rev the number of the revision to lookup
138
+ # @return [String] the file's data
139
+ def size(rev)
140
+ node = self.node rev
141
+ if renamed? node
142
+ read(node).size
143
+ else
144
+ self[rev].compressed_len
145
+ end
146
+ end
147
+
148
+ ##
149
+ # Converts a given node in this revision with the text provided.
150
+ # overridden because it handles renamed files.
151
+ #
152
+ # @param [String] thenode the node ID to use
153
+ # @param [String] text the text to compare against
154
+ # @return [Boolean] true if they're different, false if not. silly, isn't
155
+ # it?
156
+ def cmp(thenode, text)
157
+ if renamed? thenode
158
+ t2 = read thenode
159
+ return t2 != text
160
+ end
161
+ super(thenode, text)
162
+ end
163
+ end # class FileLog
164
+
165
+ end # module Amp
@@ -0,0 +1,493 @@
1
+ module Amp
2
+ module RevlogSupport
3
+ include Node
4
+
5
+ ##
6
+ # This class represents one revision entry in the index.
7
+ #
8
+ # Format on disk (in BitStruct notation):
9
+ #
10
+ # default_options :endian => :network
11
+ #
12
+ # signed :offset_flags, 64
13
+ # signed :compressed_len, 32
14
+ # signed :uncompressed_len, 32
15
+ # signed :base_rev, 32
16
+ # signed :link_rev, 32
17
+ # signed :parent_one_rev, 32
18
+ # signed :parent_two_rev, 32
19
+ # char :node_id, 160
20
+ # pad :padding, 96
21
+ #
22
+ # [offset_flags] - this is a double-word (8 bytes) - that combines the offset into the data file
23
+ # and any flags about the entry
24
+ # [compressed_len] - this is the length of the data when compressed
25
+ # [uncompresed_len] - length of the data uncompresed
26
+ # [base_rev] - the revision of the filelog
27
+ # [link_rev] - the revision of the whole repo where this was attached
28
+ # [parent_one_rev] - the parent revision the revision. Even if it's not a merge,
29
+ # it will have at least this parent entry
30
+ # [parent_two_rev] - if the revision is a merge, then it will have a second parent.
31
+ class IndexEntry < Struct.new(:offset_flags, :compressed_len, :uncompressed_len, :base_rev,
32
+ :link_rev, :parent_one_rev, :parent_two_rev, :node_id)
33
+ include Comparable
34
+ INDEX_FORMAT_NG = "Q NNNNNN a20 x12"
35
+ BLOCK_SIZE = 64
36
+ def initialize(*args)
37
+ if args.size == 1 && args[0].respond_to?(:read)
38
+ super(*(args[0].read(BLOCK_SIZE).unpack(INDEX_FORMAT_NG)))
39
+ else
40
+ super(*args)
41
+ end
42
+ end
43
+
44
+ def to_s
45
+ fix_signs
46
+ ret = self.to_a.pack(INDEX_FORMAT_NG)
47
+ fix_signs
48
+ ret
49
+ end
50
+
51
+ # Fixes the values to force them to be signed (possible to be negative)
52
+ def fix_signs
53
+ require 'amp/dependencies/amp_support/ruby_amp_support'
54
+
55
+ self.offset_flags = self.offset_flags.byte_swap_64
56
+ self.compressed_len = self.compressed_len.to_signed_32
57
+ self.uncompressed_len = self.uncompressed_len.to_signed_32
58
+ self.base_rev = self.base_rev.to_signed_32
59
+ self.link_rev = self.link_rev.to_signed_32
60
+ self.parent_one_rev = self.parent_one_rev.to_signed_32
61
+ self.parent_two_rev = self.parent_two_rev.to_signed_32
62
+
63
+ self
64
+ end
65
+
66
+ # Compares this entry to another
67
+ def <=> other_entry
68
+ this.base_rev <=> other_entry.base_rev
69
+ end
70
+
71
+ # Gives a hash value so we can stick these entries into a hash
72
+ def hash
73
+ node_id.hash
74
+ end
75
+ end
76
+
77
+ ##
78
+ # = Index
79
+ # The Index is a file that keeps track of the revision data. All revisions
80
+ # go through an index. This class, {Index}, is the most basic class. It
81
+ # provides an Index.parse method so that you can read a file in, and
82
+ # the class will figure out which version it is, and all that jazz.
83
+ #
84
+ class Index
85
+ include Amp::RevlogSupport::Support
86
+ include Enumerable
87
+
88
+ # This is the packed format of the version number of the index.
89
+ VERSION_FORMAT = "N"
90
+ # The version (either {REVLOG_VERSION_0} or {REVLOG_VERSION_NG})
91
+ attr_reader :version
92
+ # The actual lookup array. Each revision has an index in this list, and
93
+ # that list also happens to be relatively chronological.
94
+ attr_reader :index
95
+ # This node_map lets you look up node_id's (NOT INDEX-NUMBERS),
96
+ # which are strings, and get the index into @index of the
97
+ # node you're looking up.
98
+ attr_reader :node_map
99
+ # This allows a couple neat caching tricks to speed up acces and cut
100
+ # down on IO.
101
+ attr_reader :chunk_cache
102
+ # This is the path to the index file. Can be a URL. I don't care and
103
+ # neither should you.
104
+ attr_reader :indexfile
105
+ # This is the raw cache data. Another helpful bit.
106
+ attr_accessor :cache
107
+
108
+ ##
109
+ # This method will parse the file at the provided path and return
110
+ # an appropriate Index object. The object will be of the class that
111
+ # fits the file provided, based on version and whether
112
+ # it is inline.
113
+ #
114
+ # @param [String] inputfile the filepath to load and parse
115
+ # @return [Index] Some subclassed version of Index that's parsed the file
116
+ def self.parse(opener, inputfile)
117
+ versioninfo = REVLOG_DEFAULT_VERSION
118
+ i = ""
119
+ begin
120
+ opener.open(inputfile) do |f|
121
+ i = f.read(4)
122
+ end
123
+ versioninfo = i.unpack(VERSION_FORMAT).first if i.size > 0
124
+ # Check if the data is with the index info.
125
+ inline = (versioninfo & REVLOG_NG_INLINE_DATA > 0)
126
+ # Get the version number of the index file.
127
+ version = Support.get_version versioninfo
128
+ rescue
129
+ inline = true
130
+ version = REVLOG_VERSION_NG
131
+ end
132
+
133
+ # Pick a subclass for the version and in-line-icity we found.
134
+ case [version, inline]
135
+ when [REVLOG_VERSION_0, false]
136
+ IndexVersion0.new opener, inputfile
137
+ when [REVLOG_VERSION_NG, false]
138
+ IndexVersionNG.new opener, inputfile
139
+ when [REVLOG_VERSION_NG, true]
140
+ IndexInlineNG.new opener, inputfile
141
+ else
142
+ raise RevlogError.new("Invalid format: #{version} flags: #{get_flags(versioninfo)}")
143
+ end
144
+ end
145
+
146
+ ##
147
+ # Returns whether a given node ID exists, without throwing a lookup error.
148
+ #
149
+ # @param [String] node the node_id to lookup. 20 bytes, binary.
150
+ # @return [Boolean] is the node in the index?
151
+ def has_node?(node)
152
+ @node_map[node]
153
+ end
154
+
155
+ ##
156
+ # This provides quick lookup into the index, based on revision
157
+ # number. NOT ID's, index numbers.
158
+ #
159
+ # @param [Fixnum] index the index to look up
160
+ # @return [IndexEntry] the revision requested
161
+ def [](index)
162
+ @index[index]
163
+ end
164
+ ##
165
+ # This method writes the index to file. Pretty 1337h4><.
166
+ #
167
+ # @param [String] index_file the path to the index file.
168
+ def write_entry(index_file, journal)
169
+ raise abort("Use a concrete class. Yeah, I called it a concrete class. I hate" +
170
+ " Java too, but you tried to use an abstract class.")
171
+ end
172
+
173
+ ##
174
+ # Adds an item to the index safely. DO NOT USE some_index.index <<. It's
175
+ # some_index << entry.
176
+ #
177
+ # @param [[Integer, Integer, Integer, Integer, Integer, Integer,
178
+ # Integer, Integer]] item the data to enter as an entry. See the spec fo
179
+ # {IndexEntry}.
180
+ def <<(item)
181
+ @index.insert(-2, IndexEntry.new(*item)) if item.is_a? Array
182
+ @index.insert(-2, item) if item.is_a? IndexEntry
183
+ # leave the terminating entry intact
184
+ end
185
+
186
+ # Returns the number of entries in the index, including the null revision
187
+ def size
188
+ @index.size
189
+ end
190
+
191
+ # Iterates over each entry in the index, including the null revision
192
+ def each(&b)
193
+ @index.each(&b)
194
+ end
195
+ end
196
+
197
+ ##
198
+ # = IndexVersion0
199
+ # This handles old versions of the index file format.
200
+ # These are apparently so old they were version 0.
201
+ class IndexVersion0 < Index
202
+ # Binary data format for each revision entry
203
+ INDEX_FORMAT_V0 = "N4 a20 a20 a20"
204
+ # The size of each revision entry
205
+ BLOCK_SIZE = (4 * 4) + (3 * 20)
206
+ # The offset into the entry where the SHA1 is stored for validation
207
+ SHA1_OFFSET = 56
208
+
209
+ # Return the size of 1 entry
210
+ def entry_size; BLOCK_SIZE; end
211
+ # Return what version this index is
212
+ def version; REVLOG_VERSION_0; end
213
+ # Does the index store the data with the revision index entries?
214
+ def inline?; false; end
215
+
216
+ # Initializes the index by reading from the provided filename. Users probably
217
+ # don't need this because {Index}#{parse} will do this for you.
218
+ #
219
+ # @param [String] inputfile the path to the index file
220
+ def initialize(opener, inputfile)
221
+ @opener = opener
222
+ @indexfile = inputfile
223
+ @node_map = {Node::NULL_ID => Node::NULL_REV}
224
+ @index = []
225
+ n = offset = 0
226
+ if File.exists?(opener.join(inputfile))
227
+ opener.open(inputfile) do |f|
228
+
229
+ while !f.eof?
230
+ current = f.read(BLOCK_SIZE)
231
+ entry = current.unpack(INDEX_FORMAT_V0)
232
+ new_entry = IndexEntry.new(offset_version(entry[0],0), entry[1], -1, entry[2], entry[3],
233
+ (@node_map[entry[4]] || nullrev), (@node_map[entry[5]] || nullrev),
234
+ entry[6])
235
+ @index << new_entry.fix_signs
236
+ @node_map[entry[6]] = n
237
+ n += 1
238
+ end
239
+ end
240
+ end
241
+ @cache = nil
242
+ self
243
+ end
244
+
245
+ ##
246
+ # This method writes the index to file. Pretty 1337h4><.
247
+ #
248
+ # @param [String] index_file the path to the index file.
249
+ def write_entry(index_file, entry, journal, data)
250
+ curr = self.size - 1
251
+
252
+ node_map[entry.last] = curr
253
+
254
+ link = entry[4]
255
+ data_file = index_file[0..-3] + ".d"
256
+
257
+ entry = pack_entry entry, link
258
+
259
+ data_file_handle = open(data_file, "a")
260
+ index_file_handle = open(index_file, "a+")
261
+
262
+ journal << [data_file, offset]
263
+ journal << [index_file, curr * entry.size]
264
+
265
+ data_file_handle.write data[:compression] if data[:compression].any?
266
+ data_file_handle.write data[:text]
267
+
268
+ data_file_handle.flush
269
+ index_file_handle.write entry
270
+ end
271
+ ##
272
+ # This takes an entry and packs it into binary data for writing to
273
+ # the file.
274
+ #
275
+ # @param [IndexEntry] entry the revision entry to pack up for writing
276
+ # @param rev unused by version 0. Kept to make the interface uniform
277
+ # @return [String] the Binary data packed up for writing.
278
+ def pack_entry(entry, rev)
279
+ entry = IndexEntry.new(*entry) if entry.kind_of? Array
280
+ entry.fix_signs
281
+ e2 = [RevlogSupport::Support.offset_type(entry.offset_flags),
282
+ entry.compressed_len, entry.base_rev, entry.link_rev,
283
+ @index[entry.parent_one_rev].node_id,
284
+ @index[entry.parent_two_rev].node_id, entry.node_id]
285
+ e2.pack(INDEX_FORMAT_V0)
286
+ end
287
+ end
288
+
289
+ ##
290
+ # = IndexVersionNG
291
+ # This is the current version of the index. I'm not sure why they call
292
+ # it Version 'NG' but they do. An index of this type is *not* inline.
293
+ class IndexVersionNG < Index
294
+ VERSION_FORMAT = "N"
295
+ # The binary format used for pack/unpack
296
+ INDEX_FORMAT_NG = "Q NNNNNN a20 x12"
297
+ # The distance into the entry to go to find the SHA1 hash
298
+ SHA1_OFFSET = 32
299
+ # The size of a single block in the index
300
+ BLOCK_SIZE = 8 + (6 * 4) + 20 + 12
301
+
302
+ ##
303
+ # Initializes the index by parsing the given file.
304
+ #
305
+ # @param [String] inputfile the path to the index file.
306
+ def initialize(opener, inputfile)
307
+ @opener = opener
308
+ @indexfile = inputfile
309
+ @cache = nil
310
+ @index = []
311
+ @node_map = {Node::NULL_ID => Node::NULL_REV}
312
+
313
+ opened = parse_file
314
+
315
+ if opened
316
+ first_entry = @index[0]
317
+ type = get_version(first_entry.offset_flags)
318
+ first_entry.offset_flags = offset_version(0, type) #turn off inline
319
+ @index[0] = first_entry
320
+ end
321
+
322
+ @index << IndexEntry.new(0,0,0,-1,-1,-1,-1,Node::NULL_ID)
323
+ end
324
+
325
+ ##
326
+ # returns the size of 1 block in this type of index
327
+ def entry_size; BLOCK_SIZE; end
328
+
329
+ # returns the version number of the index
330
+ def version; REVLOG_VERSION_NG; end
331
+ # returns whether or not the index stores data with revision info
332
+ def inline?; false; end
333
+
334
+ ##
335
+ # Parses each index entry. Internal use only.
336
+ #
337
+ # @return [Boolean] whether the file was opened
338
+ def parse_file
339
+ n = 0
340
+ begin
341
+ @opener.open(@indexfile,"r") do |f|
342
+ until f.eof?
343
+ # read the entry
344
+ entry = IndexEntry.new(f).fix_signs
345
+ # store it in the map
346
+ @node_map[entry.node_id] = n
347
+ # add it to the index
348
+ @index << entry
349
+ n += 1
350
+ end
351
+ end
352
+ return true
353
+ rescue Errno::ENOENT
354
+ return false
355
+ end
356
+ end
357
+
358
+ ##
359
+ # Packs up the revision entry for writing to the binary file.
360
+ #
361
+ # @param [IndexEntry] entry this is the entry that has to be formatted
362
+ # into binary.
363
+ # @param [Fixnum] rev this is the index number of the entry - if it's
364
+ # the first revision (rev == 0) then we treat it slightly differently.
365
+ # @return [String] the entry converted into binary suitable for writing.
366
+ def pack_entry(entry, rev)
367
+ entry = IndexEntry.new(*entry) if entry.kind_of? Array
368
+ p = entry.to_s
369
+ if rev == 0 || rev == 1
370
+ p = [version].pack(VERSION_FORMAT) + p[4..-1] # initial entry
371
+ end
372
+ p
373
+ end
374
+
375
+ ##
376
+ # This method writes the index to file. Pretty 1337h4><.
377
+ #
378
+ # @param [String] index_file the path to the index file.
379
+ def write_entry(index_file, entry, journal, data, index_file_handle = nil)
380
+ curr = self.size - 1
381
+
382
+ link = (entry.is_a? Array) ? entry[4] : entry.link_rev
383
+ data_file = index_file[0..-3] + ".d"
384
+
385
+ entry = pack_entry entry, link
386
+
387
+ @opener.open(data_file, "a+") do |data_file_handle|
388
+ data_file_handle.write data[:compression] if data[:compression].any?
389
+ data_file_handle.write data[:text]
390
+
391
+ data_file_handle.flush
392
+ end
393
+
394
+ index_file_handle ||= (opened = true && @opener.open(index_file, "a+"))
395
+
396
+ index_file_handle.write entry
397
+ index_file_handle.close if opened
398
+
399
+ #journal << [data_file, offset]
400
+ #journal << [index_file, curr * entry.size]
401
+ end
402
+
403
+ end
404
+
405
+ ##
406
+ # = LazyIndex
407
+ # When this gets filled in, this class will let us access an index without loading
408
+ # every entry first. This is handy because index files can get pretty fuckin big.
409
+ class LazyIndex < Index
410
+
411
+ end
412
+
413
+ ##
414
+ # = IndexInlineNG
415
+ # This is a variant of the current version of the index format, in which the data
416
+ # is stored in the actual index file itself, right after the little revision
417
+ # entry block (see {IndexEntry}). This means less IO, which is good.
418
+ #
419
+ class IndexInlineNG < IndexVersionNG
420
+ VERSION_FORMAT = "N"
421
+ # We're inline!
422
+ INDEX_FORMAT_NG = "Q NNNNNN a20 x12"
423
+ # The distance into the entry to go to find the SHA1 hash
424
+ SHA1_OFFSET = 32
425
+ # The size of a single block in the index
426
+ BLOCK_SIZE = 8 + (6 * 4) + 20 + 12
427
+
428
+ def inline?; true; end
429
+ def version; REVLOG_VERSION_NG | REVLOG_NG_INLINE_DATA; end
430
+ def pack_entry(entry, rev)
431
+ entry = IndexEntry.new(*entry) if entry.kind_of? Array
432
+ p = entry.to_s
433
+ if rev == 0 || rev == 1
434
+ p = [version].pack(VERSION_FORMAT) + p[4..-1] # initial entry
435
+ end
436
+ p
437
+ end
438
+
439
+
440
+ ##
441
+ # @todo "not sure what the 0 is for yet or i'd make this a hash" (see code)
442
+ # This method overrides the parent class' method that reads entries sequentially
443
+ # from the index file. Each entry is followed by the data for that revision
444
+ # so we have to skip over that data for our purposes.
445
+ def parse_file
446
+ n = offset = 0
447
+ begin
448
+ @opener.open(@indexfile,"r") do |f|
449
+ return false if f.eof?
450
+ while !f.eof?
451
+ # read 1 entry
452
+ entry = IndexEntry.new(f).fix_signs
453
+ # store it in the map
454
+ @node_map[entry.node_id] = n
455
+ # add it to the index
456
+ @index << entry
457
+ n += 1
458
+ break if entry.compressed_len < 0
459
+
460
+ # skip past the data, too!
461
+ f.seek(entry.compressed_len, IO::SEEK_CUR)
462
+ end
463
+ end
464
+ return true
465
+ rescue Errno::ENOENT
466
+ return false
467
+ end
468
+ end
469
+
470
+ ##
471
+ # This method writes the index to file. Pretty 1337h4><.
472
+ #
473
+ # @param [String] index_file the path to the index file.
474
+ def write_entry(index_file, entry, journal, data, index_file_handle = nil)
475
+ curr = self.size - 1
476
+ link = (entry.is_a? Array) ? entry[4] : entry.link_rev
477
+
478
+ entry = pack_entry entry, curr
479
+
480
+ index_file_handle ||= (opened = true && @opener.open(index_file, "a+"))
481
+
482
+ index_file_handle.write entry
483
+ index_file_handle.write data[:compression] if data[:compression].any?
484
+ index_file_handle.write data[:text]
485
+
486
+ index_file_handle.close if opened
487
+
488
+ end
489
+
490
+
491
+ end
492
+ end
493
+ end