amp 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,94 @@
1
+ module Amp
2
+ module Repositories
3
+
4
+ def self.pick(config, path='', create=false)
5
+ # hot path so we don't load the HTTP repos!
6
+ unless path[0,4] == "http"
7
+ return LocalRepository.new(find_repo(path), create, config)
8
+ end
9
+ return HTTPSRepository.new(path, create, config) if path[0,5] == "https"
10
+ return HTTPRepository.new(path, create, config) if path[0,4] == "http"
11
+ end
12
+
13
+ def self.find_repo path
14
+ while !(File.directory?(File.join(path, ".hg")))
15
+ old_path, path = path, File.dirname(path)
16
+ if path == old_path
17
+ raise "No Repository Found"
18
+ end
19
+ end
20
+ path
21
+ end
22
+
23
+ class RepositoryCapabilityError < StandardError; end
24
+ class RepoError < StandardError; end
25
+
26
+ ##
27
+ # = Repository
28
+ # This is an abstract class that represents a repository.
29
+ # All repositories must inherit from this class.
30
+ class Repository
31
+
32
+ ##
33
+ # Is this repository capable of the given action/format? Or, if the capability
34
+ # has a value assigned to it (like "revlog" = "version2"), what is it?
35
+ #
36
+ # @param [String] capability the name of the action/format/what have you that we need to test
37
+ # @return [Boolean, String] whether or not we support the given capability; or, for
38
+ # capabilities that have a value, the string value.
39
+ def capable?(capability)
40
+ get_capabilities
41
+ @capabilities[capability]
42
+ end
43
+
44
+ ##
45
+ # No-op, to be implemented by remote repo classes.
46
+ def get_capabilities; end
47
+
48
+ ##
49
+ # Raises an exception if we don't have a given capability.
50
+ #
51
+ # @param [String] capability what capability we are requiring
52
+ # @param [String] purpose why we need it - enhances the output
53
+ # @raise [RepositoryCapabilityError] if we don't support it, this is raised
54
+ def require_capability(capability, purpose)
55
+ get_capabilities
56
+ raise RepositoryCapabilityError.new(<<-EOF
57
+ Can't #{purpose}; remote repository doesn't support the #{capability} capability.
58
+ EOF
59
+ ) unless @capabilities[capability]
60
+ end
61
+
62
+ ##
63
+ # is the repository a local repo?
64
+ #
65
+ # @return [Boolean] is the repository local?
66
+ def local?
67
+ false
68
+ end
69
+
70
+ ##
71
+ # can we copy files? Only for local repos.
72
+ #
73
+ # @return [Boolean] whether we are able to copy files
74
+ def can_copy?
75
+ local?
76
+ end
77
+
78
+ ##
79
+ # Joins the given path with our URL. Necessary due to the difference between local
80
+ # and remote repos.
81
+ #
82
+ # @param [String] path the path we are appending
83
+ # @return [String] our URL joined with the requested path
84
+ def add_path(path)
85
+ myurl = self.url
86
+ if myurl.end_with? '/'
87
+ myurl + path
88
+ else
89
+ myurl + '/' + path
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,485 @@
1
+ module Amp
2
+ module Repositories
3
+ module Stores
4
+ extend self
5
+ class StoreError < StandardError; end
6
+ # Picks which store to use, given a list of requirements.
7
+ def pick(requirements, path, opener, pathjoiner=nil)
8
+ pathjoiner ||= proc {|*args| File.join(args) }
9
+ if requirements.include? 'store'
10
+ if requirements.include? 'fncache'
11
+ return FilenameCacheStore.new(path, opener, pathjoiner)
12
+ else
13
+ return EncodedStore.new(path, EncodedOpener, pathjoiner)
14
+ end
15
+ else
16
+ return BasicStore.new(path, opener, pathjoiner)
17
+ end
18
+ end
19
+
20
+ ##
21
+ # = BasicStore
22
+ # This class is the one from which all other stores derive. It implements
23
+ # basic methods #walk, #join, #datafiles, and #copy_list which are the
24
+ # public methods for all stores. All others are basically internal.
25
+ class BasicStore
26
+ BASIC_DATA_FILES = %W(data 00manifest.d 00manifest.i 00changelog.d 00changelog.i)
27
+
28
+ attr_accessor :path_joiner
29
+ attr_reader :path
30
+ attr_reader :opener
31
+ attr_reader :create_mode
32
+
33
+ def initialize(path, openerklass, pathjoiner)
34
+ @path_joiner, @path = pathjoiner, path
35
+ @create_mode = calculate_mode path
36
+ @opener = openerklass.new(@path)
37
+ @opener.create_mode = @create_mode
38
+ #@opener.default = :open_hg
39
+ end
40
+
41
+ ##
42
+ # Joins the file _f_ to the store's base path using the path-joiner.
43
+ #
44
+ # @param [String] f the filename to join to the store's base path
45
+ # @return the combined base path and file path
46
+ def join(f)
47
+ @path_joiner.call(@path, f)
48
+ end
49
+
50
+ ##
51
+ # Iterates over every file tracked in the store and yield it.
52
+ #
53
+ # @yield [file] every file in the store
54
+ # @yieldparam [String] file the filepath to an entry in the store
55
+ def walk
56
+ datafiles do |x|
57
+ yield x
58
+ end
59
+
60
+ meta = do_walk '', false
61
+ meta.reverse.each do |x|
62
+ yield x
63
+ end
64
+ end
65
+
66
+ ##
67
+ # Returns all the data files in the store.
68
+ def datafiles
69
+ do_walk('data', true)
70
+ end
71
+
72
+ ##
73
+ # Basic walker that is not very smart at all. It can recursively search
74
+ # for data files, but it actually uses a queue to do its searching.
75
+ #
76
+ # @param [String] relpath the base path to search
77
+ # @param [Boolean] recurse (false) whether or not to recursively
78
+ # search each discovered directory.
79
+ # @return [(String, String, Fixnum)] Each entry is returned in the form
80
+ # [filepath, filepath, filesize]
81
+ def do_walk(relpath, recurse=false)
82
+ path = join relpath
83
+ stripped_len = path.size + File::SEPARATOR.size - 1
84
+ list = []
85
+ if File.directory?(path)
86
+ to_visit = [path]
87
+ while to_visit.any?
88
+ p = to_visit.shift
89
+ Dir.stat_list(p, true) do |file, kind, stat|
90
+ fp = join(file)
91
+ if kind =~ /file/ && ['.d','.i'].include?(file[-2..-1])
92
+ n = fp[stripped_len..-1]
93
+ list << [n, n, stat.size]
94
+ elsif kind =~ /directory/ && recurse
95
+ to_visit << fp
96
+ end
97
+ end
98
+ end
99
+ end
100
+ list.sort
101
+ end
102
+
103
+ ##
104
+ # Calculates the mode for the user on the file at the given path.
105
+ # I guess this saves some wasted chmods.
106
+ #
107
+ # @param [String] path the path to calculate the mode for
108
+ # @return [Fixnum] the mode to use for chmod. Octal, like 0777
109
+ def calculate_mode(path)
110
+ begin
111
+ mode = File.stat(path).mode
112
+ if (0777 & ~Amp::Support.UMASK) == (0777 & mode)
113
+ mode = nil
114
+ end
115
+ rescue
116
+ mode = nil
117
+ end
118
+ mode
119
+ end
120
+
121
+ ##
122
+ # Returns the list of basic files that are crucial for the store to
123
+ # function.
124
+ #
125
+ # @return [Array<String>] the list of basic files crucial to this class
126
+ def copy_list
127
+ ['requires'] + BASIC_DATA_FILES
128
+ end
129
+ end
130
+
131
+ ##
132
+ # = EncodedOpener
133
+ # This opener uses the Stores' encoding function to modify the filename
134
+ # before it is loaded.
135
+ class EncodedOpener < Amp::Opener
136
+
137
+ ##
138
+ # Overrides the normal opener method to use encoded filenames.
139
+ def open(f, mode="r", &block)
140
+ super(Stores.encode_filename(f), mode, &block)
141
+ end
142
+ end
143
+
144
+ ##
145
+ # = EncodedStore
146
+ # This version of the store uses encoded file paths to preserve
147
+ # consistency across platforms.
148
+ class EncodedStore < BasicStore
149
+
150
+ ##
151
+ # over-ride the datafiles block so that it decodes filenames before
152
+ # it returns them.
153
+ #
154
+ # @see BasicStore
155
+ def datafiles
156
+ do_walk('data', true) do |a, b, size|
157
+ a = decode_filename(a) || nil
158
+ yield [a, b, size] if block_given?
159
+ end
160
+ end
161
+
162
+ ##
163
+ # Encode the filename before joining
164
+ def join
165
+ @path_joiner.call @path, encode_filename(f)
166
+ end
167
+
168
+ ##
169
+ # We've got a new required file so let's include it
170
+ def copy_list
171
+ BASIC_DATA_FILES.inject ['requires', '00changelog.i'] do |a, f|
172
+ a + @path_joiner.call('store', f)
173
+ end
174
+ end
175
+ end
176
+
177
+ ##
178
+ # = FilenameCache
179
+ # This module handles dealing with Filename Caches - namely, parsing
180
+ # them.
181
+ module FilenameCache
182
+
183
+ ##
184
+ # Parses the filename cache, given an object capable of opening
185
+ # a file relative to the right directory.
186
+ #
187
+ # @param [Amp::Opener] opener An opener initialized to the repo's
188
+ # directory.
189
+ def self.parse(opener)
190
+ return unless File.exist? opener.join("fncache")
191
+ opener.open 'fncache', 'r' do |fp|
192
+ # error handling?
193
+ i = 0
194
+ fp.each_line do |line| #this is how we parse it
195
+ if line.size < 2 || line[-1,1] != "\n"
196
+ raise StoreError.new("invalid fncache entry, line #{i}")
197
+ end
198
+ yield line.chomp
199
+ end
200
+ end
201
+ end
202
+
203
+ ##
204
+ # = FilenameCacheOpener
205
+ # This opener handles a cache of filenames that we are currently
206
+ # tracking. This way we don't need to recursively walk though
207
+ # the folders every single time. To use this class, you pass in
208
+ # the real Opener object (that responds to #open and returns a file
209
+ # pointer). then just treat it like any other opener. It will handle
210
+ # the behind-the-scenes work itself.
211
+ class FilenameCacheOpener < Amp::Opener
212
+
213
+ ##
214
+ # Initializes a new FNCacheOpener. Requires a normal object capable
215
+ # of opening files.
216
+ #
217
+ # @param [Amp::Opener] opener an opener object initialized to the
218
+ # appropriate root directory.
219
+ def initialize(opener)
220
+ @opener = opener
221
+ @entries = nil
222
+ end
223
+
224
+ def path; @opener.path; end
225
+ alias_method :root, :path
226
+
227
+ ##
228
+ # Parses the filename cache and loads it into an ivar.
229
+ def load_filename_cache
230
+ @entries = {}
231
+ FilenameCache.parse @opener do |f|
232
+ @entries[f] = true
233
+ end
234
+ end
235
+
236
+ ##
237
+ # Opens a file while being sure to write the filename if we haven't
238
+ # seen it before. Just like the normal Opener's open() method.
239
+ #
240
+ # @param [String] path the path to the file
241
+ # @param [Fixnum] mode the read/write/append mode
242
+ # @param block the block to pass to it (optional)
243
+ def open(path, mode='r', &block)
244
+
245
+ if mode !~ /r/ && path =~ /data\//
246
+ load_filename_cache if @entries.nil?
247
+ if @entries[path].nil?
248
+ @opener.open('fncache','ab') {|f| f.puts path }
249
+ @entries[path] = true
250
+ end
251
+ end
252
+
253
+ begin
254
+ @opener.open(Stores.hybrid_encode(path), mode, &block)
255
+ rescue Errno::ENOENT
256
+ raise
257
+ rescue
258
+ raise unless mode == 'r'
259
+ end
260
+ rescue
261
+ raise
262
+ end
263
+
264
+ end
265
+ end
266
+
267
+ ##
268
+ # = FilenameCacheStore
269
+ # This version of the store uses a "Filename Cache", which is just a file
270
+ # that names all the tracked files in the store. It also uses an even more
271
+ # advanced "hybrid" encoding for filenames that again ensure consistency across
272
+ # platforms. However, this encoding is non-reversible - but since we're just
273
+ # doing file lookups anyway, that's just ducky.
274
+ class FilenameCacheStore < BasicStore
275
+
276
+ ##
277
+ # Initializes the store. Sets up the cache right away.
278
+ #
279
+ # @see BasicStore
280
+ def initialize(path, openerklass, pathjoiner)
281
+ @path_joiner = pathjoiner
282
+ @path = pathjoiner.call(path, 'store')
283
+ @create_mode = calculate_mode @path
284
+ @_op = openerklass.new(@path)
285
+ @_op.create_mode = @create_mode
286
+ @_op.default = :open_file
287
+
288
+ @opener = FilenameCache::FilenameCacheOpener.new(@_op)
289
+ end
290
+
291
+ ##
292
+ # Properly joins the path, but hybrid-encodes the file's path
293
+ # first.
294
+ def join(f)
295
+ @path_joiner.call(@path, Stores.hybrid_encode(f))
296
+ end
297
+
298
+ ##
299
+ # Here's how we walk through the files now. Oh, look, we don't need
300
+ # to do annoying directory traversal anymore! But we do have to
301
+ # maintain a consistent fnstore file. I think I can live with that.
302
+ def datafiles
303
+ rewrite = false
304
+ existing = []
305
+ pjoin = @path_joiner
306
+ spath = @path
307
+ result = []
308
+ FilenameCache.parse(@_op) do |f|
309
+
310
+ ef = Stores.hybrid_encode f
311
+ begin
312
+ st = File.stat(@path_joiner.call(spath, ef))
313
+ yield [f, ef, st.size] if block_given?
314
+ result << [f, ef, st.size] unless block_given?
315
+ existing << f
316
+ rescue Errno::ENOENT
317
+ rewrite = true
318
+ end
319
+ end
320
+ if rewrite
321
+ fp = @_op.open('fncache', 'wb')
322
+ existing.each do |p|
323
+ fp.write(p + "\n")
324
+ end
325
+ fp.close
326
+ end
327
+ result
328
+ end
329
+
330
+ ##
331
+ # A more advanced list of files we need, properly joined and whatnot.
332
+ def copy_list
333
+ d = BASIC_DATA_FILES + ['dh', 'fncache']
334
+ d.inject ['requires', '00changelog.i'] do |a, f|
335
+ a + @path_joiner.call('store', f)
336
+ end
337
+ result
338
+ end
339
+
340
+ end
341
+
342
+
343
+ #############################################
344
+ ############ Encoding formats ###############
345
+ #############################################
346
+
347
+ ##
348
+ # Gets the basic character map that maps disallowed letters to
349
+ # allowable substitutes.
350
+ #
351
+ # @param [Boolean] underscore Should underscores be inserted in front of
352
+ # capital letters before we downcase them? (e.g. if true, "A" => "_a")
353
+ def illegal_character_map(underscore=true)
354
+ e = '_'
355
+ win_reserved = "\\:*?\"<>|".split("").map {|x| x.ord}
356
+ cmap = {}; 0.upto(126) {|x| cmap[x.chr] = x.chr}
357
+ ((0..31).to_a + (126..255).to_a + win_reserved).each do |x|
358
+ cmap[x.chr] = "~%02x" % x
359
+ end
360
+ ((("A".ord)..("Z".ord)).to_a + [e.ord]).each do |x|
361
+ cmap[x.chr] = e + x.chr.downcase if underscore
362
+ cmap[x.chr] = x.chr.downcase unless underscore
363
+ end
364
+ cmap
365
+ end
366
+ memoize_method :illegal_character_map, true
367
+
368
+ ##
369
+ # Reversible encoding of the filename
370
+ #
371
+ # @param [String] s a file's path you wish to encode
372
+ # @param [Boolean] underscore should we insert underscores when
373
+ # downcasing letters? (e.g. if true, "A" => "_a")
374
+ # @return [String] an encoded file path
375
+ def encode_filename(s, underscore=true)
376
+ cmap = illegal_character_map underscore
377
+ s.split("").map {|c| cmap[c]}.join
378
+ end
379
+
380
+ ##
381
+ # Decodes an encoding performed by encode_filename
382
+ #
383
+ # @param [String] s an encoded file path
384
+ # @param [String] the decoded file path
385
+ def decode_filename(s)
386
+ cmap = illegal_character_map true
387
+ dmap = {}
388
+ cmap.each do |k, v|
389
+ dmap[v] = k
390
+ end
391
+
392
+ i = 0
393
+ result = []
394
+ while i < s.size
395
+ 1.upto(3) do |l|
396
+ if dmap[s[i..(i+l-1)]]
397
+ result << dmap[s[i..(i+l-1)]]
398
+ i += l
399
+ break
400
+ end
401
+ end
402
+ end
403
+ result.join
404
+ end
405
+
406
+ # can't name a file one of these on windows, apparently
407
+ WINDOWS_RESERVED_FILENAMES = %w(con prn aux nul com1
408
+ com2 com3 com4 com5 com6 com7 com8 com8 lpt1 lpt2
409
+ lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9)
410
+
411
+ ##
412
+ # Copypasta
413
+ def auxilliary_encode(path)
414
+ res = []
415
+ path.split('/').each do |n|
416
+ if n.any?
417
+ base = n.split('.')[0]
418
+ if !(base.nil?) && base.any? && WINDOWS_RESERVED_FILENAMES.include?(base)
419
+ ec = "~%02x" % n[2,1].ord
420
+ n = n[0..1] + ec + n[3..-1]
421
+ end
422
+ if ['.',' '].include? n[-1,1]
423
+ n = n[0..-2] + ("~%02x" % n[-1,1].ord)
424
+ end
425
+ end
426
+ res << n
427
+ end
428
+ res.join("/")
429
+ end
430
+
431
+ ##
432
+ # Normal encoding, but without extra underscores in the filenames.
433
+ def lower_encode(s)
434
+ encode_filename s, false
435
+ end
436
+
437
+ MAX_PATH_LEN_IN_HGSTORE = 120
438
+ DIR_PREFIX_LEN = 8
439
+ MAX_SHORTENED_DIRS_LEN = 8 * (DIR_PREFIX_LEN + 1) - 4
440
+
441
+ ##
442
+ # uber encoding that's straight up crazy.
443
+ # Max length of 120 means we have a non-reversible encoding,
444
+ # but since the FilenameCache only cares about name lookups, one-way
445
+ # is really all that matters!
446
+ #
447
+ # @param [String] path the path to encode
448
+ # @return [String] an encoded path, with a maximum length of 120.
449
+ def hybrid_encode(path)
450
+ return path unless path =~ /data\//
451
+ ndpath = path["data/".size..-1]
452
+ res = "data/" + auxilliary_encode(encode_filename(ndpath))
453
+ if res.size > MAX_PATH_LEN_IN_HGSTORE
454
+ digest = path.sha1.hexdigest
455
+ aep = auxilliary_encode(lower_encode(ndpath))
456
+ root, ext = File.amp_split_extension aep
457
+ parts = aep.split('/')
458
+ basename = File.basename aep
459
+ sdirs = []
460
+ parts[0..-2].each do |p|
461
+ d = p[0..(DIR_PREFIX_LEN-1)]
462
+
463
+ d = d[0..-2] + "_" if " .".include?(d[-1,1])
464
+
465
+ t = sdirs.join("/") + "/" + d
466
+ break if t.size > MAX_SHORTENED_DIRS_LEN
467
+
468
+ sdirs << d
469
+ end
470
+ dirs = sdirs.join("/")
471
+ dirs += "/" if dirs.size > 0
472
+
473
+ res = "dh/" + dirs + digest + ext
474
+ space_left = MAX_PATH_LEN_IN_HGSTORE - res.size
475
+ if space_left > 0
476
+ filler = basename[0..(space_left-1)]
477
+ res = "dh/" + dirs + filler + digest + ext
478
+ end
479
+ end
480
+ return res
481
+
482
+ end
483
+ end
484
+ end
485
+ end