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,167 @@
1
+ require 'delegate'
2
+ require 'rubygems'
3
+ require 'lighthouse'
4
+
5
+ module Amp
6
+
7
+ ##
8
+ # = LighthouseHook
9
+ # Makes creating lighthouse-committing hooks extremely easy. It is a
10
+ # delegate to Lighthouse (it requires rubygems)
11
+ #
12
+ # @example Amp::LighthouseHook.add_hooks(:commit) do |hook|
13
+ # hook.token = 'abcdefghiljklmnopqrstuvxyzabcdefghiljklmnopqrstuvxyz'
14
+ # hook.account = 'youraccount'
15
+ # hook.project = 'yourprojectname'
16
+ # end
17
+ #
18
+ # @example hook :commit do |opts|
19
+ # h = Amp::LighthouseHook.make do |hook|
20
+ # hook.token = 'abcdefghiljklmnopqrstuvxyzabcdefghiljklmnopqrstuvxyz'
21
+ # hook.account = 'youraccount'
22
+ # hook.project = 'yourprojectname'
23
+ # end
24
+ # h.call opts
25
+ # end
26
+ class LighthouseHook < DelegateClass(Lighthouse.class)
27
+
28
+ ##
29
+ # Takes a block to configure a hook that can submit changesets to a
30
+ # Lighthouse project. The result is a proc, which when run with an
31
+ # Amp::Hook's options, will submit the changeset.
32
+ #
33
+ # You must provide either a username/password, or an API token to
34
+ # the hook when configuring it. Otherwise, submitting changesets will fail.
35
+ #
36
+ # @example Creating a commit hook
37
+ # hook :commit do |opts|
38
+ # h = Amp::LighthouseHook.make do |hook|
39
+ # hook.token = 'abcdefghiljklmnopqrstuvxyzabcdefghiljklmnopqrstuvxyz'
40
+ # hook.account = 'youraccount'
41
+ # hook.project = 'yourprojectname'
42
+ # end
43
+ # h.call(opts)
44
+ # end
45
+ # @yield the hook, configurable exactly as the Lighthouse gem is configured.
46
+ # Also, you must specify #project= to set the project to send changesets to.
47
+ # @yieldparam hook the new hook object you must configure
48
+ # @return [Proc] a proc that takes a hook's options, and when called, will
49
+ # submit the new changeset(s)
50
+ def self.make(&block)
51
+ new(&block).block
52
+ end
53
+
54
+ ##
55
+ # Takes a block to configure a hook that can submit changesets to a
56
+ # Lighthouse project. The arguments are a list of symbols, which are
57
+ # the events that are automatically hooked into. This provides
58
+ # a terser, though slightly less explicit and less flexible syntax than
59
+ # that used with LighthouseHook.make().
60
+ #
61
+ # You must provide either a username/password, or an API token to
62
+ # the hook when configuring it. Otherwise, submitting changesets will fail.
63
+ #
64
+ # @example Creating a commit hook
65
+ # Amp::LighthouseHook.add_hooks(:commit) do |hook|
66
+ # hook.token = 'abcdefghiljklmnopqrstuvxyzabcdefghiljklmnopqrstuvxyz'
67
+ # hook.account = 'youraccount'
68
+ # hook.project = 'yourprojectname'
69
+ # end
70
+ # @param [Array<Symbol>] events each argument is an event that is hooked into,
71
+ # such as :commit, :incoming, etc. Currently, only :commit is supported.
72
+ # @yield the hook, configurable exactly as the Lighthouse gem is configured.
73
+ # Also, you must specify #project= to set the project to send changesets to.
74
+ # @yieldparam hook the new hook object you must configure
75
+ def self.add_hook(*events, &block)
76
+ h = self.make(&block)
77
+
78
+ events.each do |evt|
79
+ Amp::Hook.new(evt) do |opts|
80
+ h[opts]
81
+ end
82
+ end
83
+ end
84
+
85
+ ##
86
+ # @see {add_hook}
87
+ def self.add_hooks(*args, &block)
88
+ add_hook(*args, &block)
89
+ end
90
+
91
+ attr_reader :project
92
+
93
+ ##
94
+ # Initializes a new LighthouseHook. Delegates all unknown methods to the Lighthouse
95
+ # singleton class, yields itself to the (required!) block, and loads the requested
96
+ # project.
97
+ def initialize
98
+ super(Lighthouse)
99
+ yield self
100
+ load_project
101
+ end
102
+
103
+ ##
104
+ # Specifies the name of the project to which we will send changesets.
105
+ #
106
+ # @param [String, #to_s] val the name of the project to commit to
107
+ def project=(val)
108
+ @project_name = val.to_s
109
+ end
110
+
111
+ ##
112
+ # Creates a proc that - when executed, with a hook's options - will send
113
+ # a changeset to Lighthouse.
114
+ #
115
+ # @return [Proc] a proc that will send a changeset to lighthouse
116
+ def block
117
+ proc do |opts|
118
+ cs = Lighthouse::Changeset.new(:project_id => @project.id)
119
+
120
+ ##
121
+ # Each file must be sent as an array: ["A", file] for an added file,
122
+ # ["M", file] for a modified file, ["D", file] for a removed file.
123
+ # Thus, all changes are an array of these arrays, pairing each changed
124
+ # file with a letter.
125
+ temp_arr = []
126
+ opts[:added].each {|file| temp_arr << ["A", file]} if opts[:added].any?
127
+ opts[:modified].each {|file| temp_arr << ["M", file]} if opts[:modified].any?
128
+ opts[:removed].each {|file| temp_arr << ["D", file]} if opts[:removed].any?
129
+ cs.changes = temp_arr.to_yaml
130
+
131
+ cs.user = opts[:user]
132
+ cs.updated_at = opts[:date]
133
+ cs.body = opts[:text]
134
+ cs.revision = opts[:revision]
135
+ cs.title = "#{opts[:user]} committed revision #{opts[:revision]}"
136
+
137
+ result = cs.save
138
+
139
+ unless result
140
+ Amp::UI::err cs.errors.errors.inspect
141
+ end
142
+ end
143
+ end
144
+
145
+ private
146
+
147
+ ##
148
+ # Loads the project from the user's list of projects, based on its name.
149
+ def load_project
150
+ @project = Lighthouse::Project.find(:all).select {|p| p.name.downcase == @project_name.downcase}.first
151
+ end
152
+ end
153
+ end
154
+
155
+ # hook :commit do |opts|
156
+ # Amp::LighthouseHook.make do |hook|
157
+ # hook.token = 'e4d6af1951c240e00c216bad3c52cf269cba4a7c'
158
+ # hook.account = 'carbonica'
159
+ # hook.project = 'amp'
160
+ # end
161
+ # end
162
+
163
+ # Amp::LighthouseHook.add_hooks(:commit) do |hook|
164
+ # hook.token = 'e4d6af1951c240e00c216bad3c52cf269cba4a7c'
165
+ # hook.account = 'carbonica'
166
+ # hook.project = 'amp'
167
+ # end
@@ -0,0 +1,147 @@
1
+ module Amp
2
+ module Graphs
3
+
4
+ ##
5
+ # = AncestorGenerator
6
+ # A generator class that will allow us to only calculate one ancestor
7
+ # of a node at a time, so we don't have to process the full list of
8
+ # ancestors for each node twice. Our old, lazy way was, if you have two
9
+ # nodes A and B, and need to find the common ancestor, we would generate
10
+ # *all* nodes in both A, and B's history. If the two have a very close
11
+ # common ancestor (usually the case when doing a branch merge in a rapid
12
+ # development environment), then this is a huge amount of wasted processing.
13
+ # Generators aren't a familiar construct for most ruby developers, and they
14
+ # work via continuations, which are also typically avoided like the plague.
15
+ # Check out 'lib/support/generator.rb' to see how it works.
16
+ #
17
+ # A B
18
+ # | |
19
+ # | |
20
+ # | |
21
+ # |___| <-- target ancestor
22
+ # |
23
+ # | <-- don't need to generate this node, so we take one at a time
24
+ #
25
+ class AncestorGenerator < Generator
26
+
27
+ def initialize(vertex, depth, parent_func)
28
+ @vertex, @depth_hash, @parent_func = vertex, depth, parent_func
29
+ end
30
+
31
+ ##
32
+ # Internal method that, given a vertex, a depth-hash, and a way to
33
+ # find parents, will yield all the ancestors of a node, in order
34
+ # of depth.
35
+ #
36
+ # @param vertex the vertex ID of a node in the graph
37
+ # @param [Hash] depth_hash associates a node_id to its depth in
38
+ # the graph
39
+ # @param [Proc] parent_func a function that calcualtes the parents
40
+ # of a node
41
+ # @yield every single ancestor in a row, from lowest depth to the
42
+ # highest
43
+ # @yieldparam [Hash] a hash, with :node pointing to the ID, and :depth
44
+ # giving the depth of the node
45
+ def traverse_ancestors
46
+ h = PriorityQueue.new
47
+ h[@vertex] = @depth_hash[@vertex]
48
+ seen = {}
49
+ until h.empty?
50
+ node, depth = h.delete_min
51
+ unless seen[node]
52
+ seen[node] = true
53
+ yield({:node => node, :depth => depth})
54
+ @parent_func.call(node).each do |parent|
55
+ h[parent] = @depth_hash[parent]
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Yields each depth in succession from lowest depth to highest depth,
63
+ # with each node in that depth as a hash.
64
+ #
65
+ # @param vertex the base vertex to start from
66
+ # @param depth a hash assigning each node to its depth from the vertex
67
+ # @param [Proc] parent_func a proc that gives the parents of a node
68
+ # @yield each generation - a set of vertices that are a given depth
69
+ # from the node
70
+ # @yieldparam depth the depth that this generation is from the head
71
+ # vertex provided
72
+ # @yieldparam generation the generation, as a hash assigning entries
73
+ # in that generation to _true_.
74
+ #
75
+ def generator_loop
76
+ sg, s = nil, {}
77
+ traverse_ancestors do |hash|
78
+ g, v = hash[:depth], hash[:node]
79
+ if g != sg
80
+ yield_gen [sg, s] if sg
81
+ sg, s = g, {v => true}
82
+ else
83
+ s[v] = true
84
+ end
85
+ end
86
+ yield_gen [sg, s]
87
+ nil
88
+ end
89
+ end
90
+
91
+ class AncestorCalculator
92
+
93
+ ##
94
+ # Returns the closest common ancestor between A and B, given a method
95
+ # that says how to find the parent of a node.
96
+ #
97
+ # @param a the first node
98
+ # @param b the second node (order doesn't matter)
99
+ # @param parent_func a way to determine the parents of a node. Should
100
+ # eventually be made to a block, perhaps.
101
+ # @return the node_id of the least-common ancestor.
102
+ def self.ancestors(a, b, parent_func)
103
+ return a if a == b
104
+ to_visit = [a, b]
105
+ depth = {}
106
+ until to_visit.empty?
107
+ vertex = to_visit.last
108
+ parent_list = parent_func.call(vertex)
109
+ if parent_list.empty?
110
+ depth[vertex] = 0
111
+ to_visit.pop
112
+ else
113
+ parent_list.each do |parent|
114
+ return parent if parent == a || parent == b
115
+ to_visit << parent unless depth[parent]
116
+ end
117
+ if to_visit.last == vertex
118
+ depth[vertex] = parent_list.map {|p| depth[p]}.min - 1
119
+ to_visit.pop
120
+ end
121
+ end
122
+ end
123
+
124
+ x = AncestorGenerator.new(a, depth, parent_func)
125
+ y = AncestorGenerator.new(b, depth, parent_func)
126
+
127
+ gx = x.next
128
+ gy = y.next
129
+
130
+ while gx && gy
131
+ if gx[0] == gy[0]
132
+ gx[1].each do |k,v|
133
+ return k if gy[1].include? k
134
+ end
135
+ gx = x.next
136
+ gy = y.next
137
+ elsif gx[0] > gy[0]
138
+ gy = y.next
139
+ else
140
+ gx = x.next
141
+ end
142
+ end
143
+ return nil
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,261 @@
1
+ module Amp
2
+ module Graphs
3
+ ##
4
+ # = CopyCalculator
5
+ #
6
+ # This module manages finding what files have been copied between two
7
+ # changesets, using a base, ancestor changeset. Closely related to merging.
8
+ # We need this class because Mercurial, by default, allows us to copy files
9
+ # and move files, and be smart enough to follow these copies throughout the
10
+ # version history. Other VCS's just treat the moved file as a brand-new file.
11
+ # Thus, when we update from one changeset to another, we need to follow
12
+ # these copies.
13
+ module CopyCalculator
14
+
15
+ ##
16
+ # Calculates the copies between 2 changesets, using a pre-calculated common ancestor
17
+ # node. This is used during updates as part of Mercurial's ability to track renames
18
+ # without git-style guessing. Unfortunately it does require some amount of calculation.
19
+ # This method returns two hashes in an array: [renames, divergent]. "Renames" are
20
+ # moves from one file to another, and "divergent" are two moves of the same file,
21
+ # only with different end-names. See @example for how divergent works.
22
+ #
23
+ # @todo Add tracking of directory renames!
24
+ #
25
+ # @example
26
+ # if the local changeset renamed "foo" to "bar", and the remote changeset renamed
27
+ # "foo" to "baz", then the "divergent" hash would be:
28
+ # {"foo" => ["bar", "baz"]}
29
+ #
30
+ # @param [Repository] repo The repo for which we are calculating changes. Typically
31
+ # a LocalRepository.
32
+ # @param [Changeset] changeset_local The local (or just 1 of the bases) changeset.
33
+ # @param [Changeset] changeset_remote The remote (or the second base) changeset
34
+ # @param [Changeset] changeset_ancestor The common ancestor between {changeset_local}
35
+ # and {changeset_remote}
36
+ # @param [Boolean] check_dirs (false) Whether or not to analyze for directory renames.
37
+ # this is an expensive operation, so it defaults to false.
38
+ # @return [[Hash, Hash]] This method returns two hashes in an array, where the first
39
+ # is a list of normal file-moves ("foo" renamed to "bar" returns {"foo" => "bar"})
40
+ # and the second is a list of divergent file-moves (see @example)
41
+ #
42
+ def self.find_copies(repo, changeset_local, changeset_remote, changeset_ancestor, check_dirs=false)
43
+ # are we udpating from an empty directory? quite easy.
44
+ if changeset_local.nil? || changeset_remote.nil? ||
45
+ changeset_remote == changeset_local
46
+ return {}, {}
47
+ end
48
+ # avoid silly behavior for parent -> working directory
49
+ if changeset_remote.node == nil && c1.node == repo.dirstate.parents.first
50
+ return repo.dirstate.copies, {}
51
+ end
52
+
53
+ limit = find_limit(repo, changeset_local.revision, changeset_remote.revision)
54
+ man_local = changeset_local.manifest
55
+ man_remote = changeset_remote.manifest
56
+ man_ancestor = changeset_ancestor.manifest
57
+
58
+ # gets the versioned_file for a given file and node ID
59
+ easy_file_lookup = proc do |file, node|
60
+ if node.size == 20
61
+ return repo.versioned_file(file, :file_id => node)
62
+ end
63
+ cs = (changeset_local.revision == nil) ? changeset_local : changeset_remote
64
+ return cs.get_file(file)
65
+ end
66
+
67
+ ctx = easy_file_lookup
68
+ copy = {}
69
+ full_copy = {}
70
+ diverge = {}
71
+
72
+ # check for copies from manifest1 to manifest2
73
+ check_copies = proc do |file, man1, man2|
74
+ vf1 = easy_file_lookup[file, man1[file]]
75
+ find_old_names(vf1, limit) do |old_name|
76
+ full_copy[file] = old_name # remember for dir rename detection
77
+ if man2[old_name] # original file in other manifest?
78
+ # if the original file is unchanged on the other branch,
79
+ # no merge needed
80
+ if man2[old_name] != man_ancestor[old_name]
81
+ vf2 = easy_file_lookup[old_file, man2[old_file]]
82
+ vfa = vf1.ancestor(vf2)
83
+ # related and name changed on only one side?
84
+ if vfa && (vfa.path == file || vfa.path == vf2.path) && (vf1 == vfa || vf2 == vfa)
85
+ copy[file] = old_file
86
+ end
87
+ end
88
+ elsif man_ancestor[old_file]
89
+ (diverge[old_file] ||= []) << file
90
+ end
91
+ end
92
+ end
93
+
94
+ UI.debug(" searching for copies back to rev #{limit}")
95
+
96
+ unmatched_1 = double_intersection(man_local, man_remote, man_ancestor)
97
+ unmatched_2 = double_intersection(man_remote, man_local, man_ancestor)
98
+
99
+ UI.debug(" unmatched files in local:\n #{unmatched_1.join("\n")}") if unmatched_1.any?
100
+ UI.debug(" unmatched files in other:\n #{unmatched_2.join("\n")}") if unmatched_2.any?
101
+
102
+ unmatched_1.each {|file| check_copies[file, man_local, man_remote] }
103
+ unmatched_2.each {|file| check_copies[file, man_remote, man_local] }
104
+
105
+ diverge_2 = {}
106
+ diverge.each do |old_file, file_list|
107
+ if file_list.size == 1
108
+ diverge.delete old_file
109
+ else
110
+ file_list.each {|file| diverge_2[file] = true}
111
+ end
112
+ end
113
+
114
+ if !(full_copy.any?) || !check_dirs
115
+ return copy, diverge
116
+ end
117
+
118
+ # CHECK FOR DIRECTORY RENAMES
119
+ # TODO TODO TODO
120
+ end
121
+
122
+ private
123
+
124
+ ##
125
+ # Find the earliest revision in the repository that is an ancestor of EITHER a OR b,
126
+ # but NOT both. In other words, find the oldest ancestor on a branch.
127
+ #
128
+ # @param [Repository] repo the repository we're calculatizing on
129
+ # @param [Integer] a one changeset's revision #
130
+ # @param [Integer] b the other changeset's revision #
131
+ # @return [Integer] the earliest revision index that is an ancestor of only 1 of the
132
+ # two changesets.
133
+ def self.find_limit(repo, a, b)
134
+ # basic idea:
135
+ # - mark a and b with different sides
136
+ # - if a parent's children are all on the same side, the parent is
137
+ # on that side, otherwise it is on no side
138
+ # - walk the graph in topological order with the help of a heap;
139
+ # - add unseen parents to side map
140
+ # - clear side of any parent that has children on different sides
141
+ # - track number of interesting revs that might still be on a side
142
+ # - track the lowest interesting rev seen
143
+ # - quit when interesting revs is zero
144
+ changelog = repo.changelog
145
+ working = changelog.size # this revision index is 1 higher than the real highest
146
+ a ||= working
147
+ b ||= working
148
+
149
+ side = {a => -1, b => 1}
150
+ visit = PriorityQueue.new # because i don't have any other data structure that
151
+ visit[-a] = -a # maintains a sorted order
152
+ visit[-b] = -b
153
+ interesting = visit.size # could be 1 if a == b
154
+ limit = working
155
+ while interesting > 0
156
+ r, junk = -(visit.delete_min) # get the next lowest revision
157
+ if r == working
158
+ # different way of getting parents in this case
159
+ parents = repo.dirstate.parents.map {|p| changelog.rev(p)}
160
+ else
161
+ # normal way of getting parents
162
+ parents = changelog.parent_indices_for_index(r)
163
+ end
164
+ parents.each do |parent|
165
+ if !side[parent]
166
+ # haven't seen the parent before, so let's put it on a side.
167
+ side[parent] = side[r]
168
+ interesting += 1 if side[parent] != 0 # if it's on a side
169
+ visit[-parent] = -parent
170
+ elsif side[parent] && side[parent] != side[r]
171
+ # if we're here, then the parent has been seen by BOTH sides. so it's no good.
172
+ side[parent] = 0
173
+ interesting -= 1
174
+ end
175
+ end
176
+ # if we're here and side[r] isn't 0, then it's an ancestor to [one and only one]
177
+ # of the 2 root nodes. so keep it.
178
+ if side[r] && side[r] != 0
179
+ limit = r
180
+ interesting -= 1
181
+ end
182
+ end
183
+ limit
184
+ end
185
+
186
+ ##
187
+ # Go back in time until revision {limit}, grabbing old names that {versioned_file}
188
+ # was moved from.
189
+ #
190
+ # @param [VersionedFile] versioned_file the file for which we are finding old names
191
+ # @param [Integer] limit the minimum revision back in time in which we should
192
+ # search for old names
193
+ # @return [Array<String>] old names for the current file.
194
+ def self.find_old_names(versioned_file, limit)
195
+ # wooooo recursion unrolling!
196
+ old = {}
197
+ seen = {}
198
+ orig = versioned_file.path
199
+ visit = [[versioned_file, 0]]
200
+ while visit.any? do
201
+ file, depth = visit.shift
202
+ str = file.to_s
203
+ next if seen[str]
204
+
205
+ seen[str] = true
206
+ if file.path != orig && !old[file.path]
207
+ old[file.path] = [depth, file.path]
208
+ end
209
+ next if file.revision < limit && file.revision != nil
210
+ visit += file.parents.each {|p| [p, depth - 1]}
211
+ end
212
+ old.values.sort.map {|o| o[1]}
213
+ end
214
+
215
+ ##
216
+ # Returns all the parent directories of every file in the provided array,
217
+ # recursively, as a map. Each entry maps a directory to {true}. It's really
218
+ # just a set, but I'm too lazy to use a set. Sorry.
219
+ #
220
+ # @param [Array<String>] files a list of files for which we need all the parent
221
+ # directories
222
+ # @return [Hash] a map: each entry maps a directory name to {true}, because it's
223
+ # really just a set, because I'm too lazy to use a set.
224
+ def self.all_parent_dirs(files)
225
+ dirs = {}
226
+ files.each do |file|
227
+ file = picky_dirname file
228
+ until dirs[file]
229
+ dirs[file] = true
230
+ file = picky_dirname file
231
+ end
232
+ end
233
+ dirs
234
+ end
235
+
236
+ ##
237
+ # This method will find the path of the containing directory for the file
238
+ # pointed to by {path}. We use this instead of File.dirname because we
239
+ # want to return the empty string instead of "." if there is no path separator
240
+ # in the provided string.
241
+ #
242
+ # @param [String] path the path to the file we want the directory name of
243
+ # @return [String] the path of the containing directory of the provided file
244
+ def self.picky_dirname(path)
245
+ Dir.dirname path
246
+ end
247
+
248
+ ##
249
+ # Returns a list of elements in d1 that are not in d2 or d3. We have this method
250
+ # because Mercurial's source has this method.
251
+ #
252
+ # @param [Array] d1 a list of items we wish to filter
253
+ # @param [Array] d2 a list of items we do NOT want in d1
254
+ # @param [Array] d3 a list of items we do NOT want in d1
255
+ # @return [Array] a list of items in d1 that are not present in d2 or d3
256
+ def self.double_intersection(d1, d2, d3)
257
+ d1.reject {|i| d2.include?(i) || d3.include?(i) }
258
+ end
259
+ end
260
+ end
261
+ end