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,203 @@
1
+ module Amp
2
+ class StandardErrorReporter
3
+ def self.report str
4
+ STDERR.puts str
5
+ end
6
+ end
7
+
8
+ ##
9
+ # Provides a journal interface so when a large number of transactions
10
+ # are occurring, and any one could fail, we can rollback the changes.
11
+ class Journal
12
+ DEFAULT_OPTS = {:reporter => StandardErrorReporter, :after_close => nil}
13
+
14
+ attr_accessor :report, :journal, :after_close
15
+
16
+ ##
17
+ # @return [Amp::Journal]
18
+ def self.start(file, opts=DEFAULT_OPTS)
19
+ journal = Journal.new opts[:reporter], file, &opts[:after_close]
20
+
21
+ if block_given?
22
+ begin
23
+ yield journal
24
+ ensure
25
+ journal.close
26
+ end
27
+ end
28
+
29
+ journal
30
+ end
31
+
32
+ ##
33
+ # Initializes the journal to get ready for some transactions.
34
+ #
35
+ # @param [#report] reporter an object that will keep track of any alerts
36
+ # we have to send out. Must respond to #report.
37
+ # @param [String] journal the path to the journal file to use
38
+ # @param [Integer] createmode An octal number that sets the filemode
39
+ # of the journal file we'll be using
40
+ # @param [Proc] after_close A proc to call (with no args) after we
41
+ # close (finish) the transaction.
42
+ def initialize(reporter=StandardErrorReporter, journal="journal#{rand(10000)}", createmode=nil, &after_close)
43
+ @count = 1
44
+ @reporter = reporter
45
+ @after_close = after_close
46
+ @entries = []
47
+ @map = {}
48
+ @journal_file = journal
49
+
50
+ @file = open(@journal_file, "w")
51
+
52
+ FileUtils.chmod(createmode & 0666, @journal_file) unless createmode.nil?
53
+ end
54
+
55
+ ##
56
+ # Kills the journal - used when shit goes down and we gotta give up
57
+ # on the transactions.
58
+ def delete
59
+ if @journal_file
60
+ abort if @entries.any?
61
+ @file.close
62
+ FileUtils.safe_unlink @journal_file
63
+ end
64
+ end
65
+
66
+ ##
67
+ # Adds an entry to the journal. Since all our files are just being appended
68
+ # to all the time, all we really need is to keep track of how long the file
69
+ # was when we last knew it to be safe. In other words, if the file started
70
+ # off at 20 bytes, then an error happened, we just truncate it to 20 bytes.
71
+ #
72
+ # All params should be contained in the array
73
+ #
74
+ # @param file the name of the file we're modifying and need to track
75
+ # @param offset the length of the file we're storing
76
+ # @param data any extra data to hold onto
77
+ def add_entry(array)
78
+ file, offset, data = array[0], array[1], array[2]
79
+ return if @map[file]
80
+ @entries << {:file => file, :offset => offset, :data => data}
81
+ @map[file] = @entries.size - 1
82
+
83
+ # tell the journal how to truncate this revision
84
+ @file.write("#{file}\0#{offset}\n")
85
+ @file.flush
86
+ end
87
+
88
+ ##
89
+ # Alias for {add_entry}
90
+ alias :<< :add_entry
91
+
92
+ ##
93
+ # Finds the entry for a given file's path
94
+ # @param [String] file the path to the file
95
+ # @return [Hash] A hash with the values :file, :offset, and :data, as
96
+ # they were when they were stored by {add_entry} or {update}
97
+ def find_file(file)
98
+ return @entries[@map[file]] if @map[file]
99
+ nil
100
+ end
101
+
102
+ ##
103
+ # Alias for {find_file}
104
+ alias :find :find_file
105
+
106
+ ##
107
+ # Updates an entry's data, based on the filename. The file must already
108
+ # have been journaled.
109
+ #
110
+ # @param [String] file the file to update
111
+ # @param [Fixnum] offset the new offset to store
112
+ # @param [String] data the new data to store
113
+ def replace(file, offset, data=nil)
114
+ raise IndexError.new("journal lookup failed #{file}") unless @map[file]
115
+ index = @map[file]
116
+ @entries[index] = {:file => file, :offset => offset, :data => data}
117
+ @file.write("#{file}\0#{offset}\n")
118
+ @file.flush
119
+ end
120
+
121
+ ##
122
+ # Alias for {replace}
123
+ alias :update :replace
124
+
125
+ ##
126
+ # No godly idea what this is for
127
+ def nest
128
+ @count += 1
129
+ self
130
+ end
131
+
132
+ ##
133
+ # Is the journal running right now?
134
+ def running?
135
+ @count > 0
136
+ end
137
+
138
+ ##
139
+ # Closes up the journal. Will call the after_close proc passed
140
+ # during instantiation.
141
+ def close
142
+ @count -= 1
143
+ return if @count != 0
144
+ @file.close
145
+ @entries = []
146
+ if @after_close
147
+ @after_close.call
148
+ else
149
+ FileUtils.safe_unlink(@journal_file)
150
+ end
151
+ @journal_file = nil
152
+ end
153
+
154
+ ##
155
+ # Abort, abort! abandon ship! This rolls back any changes we've made
156
+ # during the current journalling session.
157
+ def abort
158
+ return unless @entries && @entries.any?
159
+ @reporter.report "transaction abort!\n"
160
+ @entries.each do |hash|
161
+ file, offset = hash[:file], hash[:offset]
162
+ begin
163
+ fp = open(File.join(".hg","store",file), "a")
164
+ fp.truncate offset
165
+ fp.close
166
+ rescue
167
+ @reporter.report "Failed to truncate #{File.join(".hg","store",file)}\n"
168
+ end
169
+ end
170
+ @entries = []
171
+ @reporter.report "rollback completed\n"
172
+ end
173
+
174
+ ##
175
+ # If we crashed during an abort, the journal file is gonna be sitting aorund
176
+ # somewhere. So, we should rollback any changes it left lying around.
177
+ #
178
+ # @param [String] file the journal file to use during the rollback
179
+ def self.rollback(file)
180
+ files = {}
181
+ fp = open(file)
182
+ fp.each_line do |line|
183
+ file, offset = line.split("\0")
184
+ files[file] = offset.to_i
185
+ end
186
+ fp.close
187
+ files.each do |file, offset|
188
+ if o > 0
189
+ fp = open(file, "a")
190
+ fp.truncate o.to_i
191
+ fp.close
192
+ else
193
+ fp = open(f)
194
+ fn = fp.path
195
+ fp.close
196
+ FileUtils.safe_unlink fn
197
+ end
198
+ end
199
+ FileUtils.safe_unlink file
200
+ end
201
+ end
202
+ end
203
+
@@ -0,0 +1,207 @@
1
+ module Amp
2
+ module Repositories
3
+ ##
4
+ # = Lock
5
+ # Manages a given lock file, indicating that the enclosing folder should not
6
+ # be modified. Typically used during destructive operations on a repo (such as
7
+ # a commit or push).
8
+ #
9
+ # We must be compatible with Mercurial's lock format, unfortunately. Doesn't life
10
+ # suck?
11
+ #####
12
+ ##### From Mercurial code, explaining their format:
13
+ #####
14
+ #
15
+ # lock is symlink on platforms that support it, file on others.
16
+ #
17
+ # symlink is used because create of directory entry and contents
18
+ # are atomic even over nfs.
19
+ #
20
+ # old-style lock: symlink to pid
21
+ # new-style lock: symlink to hostname:pid
22
+ class Lock
23
+ @@host = nil
24
+
25
+ ##
26
+ # Initializes the lock to a given file name, and creates the lock, effectively
27
+ # locking the containing directory.
28
+ #
29
+ # @param [String] file the path to the the lock file to create
30
+ # @param [Hash<Symbol => Object>] opts the options to use when creating the lock
31
+ # @option [Integer] options :timeout (-1) the length of time to keep trying to create the lock.
32
+ # defaults to -1 (indefinitely)
33
+ # @option [Proc, #call] options :release_fxn (nil) A proc to run when the
34
+ # lock is released
35
+ # @option [String] options :desc (nil) A description of the lock
36
+ def initialize(file, opts={:timeout => -1})
37
+ @file = file
38
+ @held = false
39
+ @timeout = opts[:timeout]
40
+ @release_fxn = opts[:release_fxn]
41
+ @description = opts[:desc]
42
+ apply_lock
43
+ end
44
+
45
+ ##
46
+ # Applies the lock. Will sleep the thread for +timeout+ time trying to apply the lock before
47
+ # giving up and raising an error.
48
+ def apply_lock
49
+ timeout = @timeout
50
+ while true do
51
+ begin
52
+ # try_lock will raise of there is already a lock.
53
+ try_lock
54
+ return true
55
+ rescue LockHeld => e
56
+ # We'll put up with this exception for @timeout times, then give up.
57
+ if timeout != 0
58
+ sleep(1)
59
+ timeout > 0 && timeout -= 1
60
+ next
61
+ end
62
+ # Timeout's up? Raise an exception.
63
+ raise LockHeld.new(Errno::ETIMEDOUT::Errno, e.filename, @desc, e.locker)
64
+ end
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Attempts to apply the lock. Raises if unsuccessful. Contains the logic for actually naming
70
+ # the lock.
71
+ def try_lock
72
+ if @@host.nil?
73
+ @@host = Socket.gethostname
74
+ end
75
+ lockname = "#{@@host}:#{Process.pid}"
76
+ while !@held
77
+ begin
78
+ make_a_lock(@file, lockname)
79
+ @held = true
80
+ rescue Errno::EEXIST
81
+ locker = test_lock
82
+ unless locker.nil?
83
+ raise LockHeld.new(Errno::EAGAIN::Errno, @file, @desc, locker)
84
+ end
85
+ rescue SystemCallError => e
86
+ raise LockUnavailable.new(e.errno, e.to_s, @file, @desc)
87
+ end
88
+ end
89
+ end
90
+
91
+ ##
92
+ # Creates a lock at the given location, with info about the locking process. Uses
93
+ # a symlink if possible, because even over NFS, creating a symlink is atomic. Nice.
94
+ # Otherwise, it will call make_a_lock_in_file on inferior OS's (cough windows cough)
95
+ # and put the data in there.
96
+ #
97
+ # The symlink is actually a non-working symlink - it points the filename (such as "hglock")
98
+ # to the data, even though the data is not an actual file. So hglock -> "medgar:25043" is
99
+ # a sort-of possible lock this method would create.
100
+ #
101
+ # @param [String] file the filename of the lock
102
+ # @param [String] info the info to store in the lock
103
+ def make_a_lock(file, info)
104
+ begin
105
+ File.symlink(info, file)
106
+ rescue Errno::EEXIST
107
+ raise
108
+ rescue
109
+ make_a_lock_in_file(file, info)
110
+ end
111
+ end
112
+
113
+ ##
114
+ # Creates a lock at the given location, storing the info about the locking process in
115
+ # an actual lock file. These locks are not preferred, because symlinks are atomic even
116
+ # over NFS. Anyway, very simple. Create the file, write in the info, close 'er up.
117
+ # That's 1 line in ruby, folks.
118
+ #
119
+ # @see make_a_lock
120
+ # @param [String] file the filename of the lock
121
+ # @param [String] info the info to store in the lock
122
+ def make_a_lock_in_file(file, info)
123
+ File.open(file, "w+") {|out| out.write info }
124
+ end
125
+
126
+ ##
127
+ # Reads in the data associated with a lock file.
128
+ #
129
+ # @param [String] file the path to the lock file to read
130
+ # @return [String] the data in the lock. In the format "#{locking_host}:#{locking_pid}"
131
+ def read_lock(file)
132
+ begin
133
+ return File.readlink(file)
134
+ rescue Errno::EINVAL, Errno::ENOSYS
135
+ return File.read(file)
136
+ end
137
+ end
138
+
139
+ ##
140
+ # Checks to see if there is a process running with id +pid+.
141
+ #
142
+ # @param [Fixnum] pid the process ID to look up
143
+ # @return [Boolean] is there a process with the given pid?
144
+ def test_pid(pid)
145
+ return true if Platform::OS == :vms
146
+
147
+ begin
148
+ # Doesn't actually kill it
149
+ Process.kill(0, pid)
150
+ true
151
+ rescue Errno::ESRCH::Errno
152
+ true
153
+ rescue
154
+ false
155
+ end
156
+ end
157
+
158
+
159
+ ##
160
+ # Text from mercurial code:
161
+ #
162
+ # return id of locker if lock is valid, else None.
163
+ #
164
+ # If old-style lock, we cannot tell what machine locker is on.
165
+ # with new-style lock, if locker is on this machine, we can
166
+ # see if locker is alive. If locker is on this machine but
167
+ # not alive, we can safely break lock.
168
+ #
169
+ # The lock file is only deleted when None is returned.
170
+ def test_lock
171
+ locker = read_lock(@file)
172
+ host, pid = locker.split(":", 1)
173
+ return locker if pid.nil? || host != @@host
174
+
175
+ pid = pid.to_i
176
+ return locker if pid == 0
177
+
178
+ return locker if test_pid pid
179
+
180
+ # if locker dead, break lock. must do this with another lock
181
+ # held, or can race and break valid lock.
182
+ begin
183
+ the_lock = Lock.new(@file + ".break")
184
+ the_lock.try_lock
185
+ File.unlink(@file)
186
+ the_lock.release
187
+ rescue LockError
188
+ return locker
189
+ end
190
+ end
191
+
192
+ ##
193
+ # Releases the lock, signalling that it is now safe to modify the directory in which
194
+ # the lock is found.
195
+ def release
196
+ if @held
197
+ @held = false
198
+ @release_fxn.call if @release_fxn
199
+
200
+ File.unlink(@file) rescue ""
201
+ end
202
+ end
203
+
204
+
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,214 @@
1
+ module Amp
2
+ module Repositories
3
+
4
+ ##
5
+ # = BundleRepository
6
+ # This class represents a read-only repository that combines both local
7
+ # repository data with a bundle file. The bundle file contains un-merged-in
8
+ # changesets - this is useful for, say, previewing the results of a pull
9
+ # action.
10
+ #
11
+ # A bundle is stored in the following manner:
12
+ # - Changelog entries
13
+ # - Manifest entries
14
+ # - Modified File entry #1
15
+ # - Modified File entry #2
16
+ # - ...
17
+ # - Modified file entry #N
18
+ class BundleRepository < LocalRepository
19
+ def initialize(path="", config=nil, bundle_name="")
20
+ @temp_parent = nil
21
+ # Figure out what to do here - if there's no current local repository, that
22
+ # takes some special work.
23
+ begin
24
+ super(path, false, config) # don't create, just look for a repository
25
+ rescue
26
+ # Ok, no local repository. Let's make one really quickly.
27
+ @temp_parent = File.join(Dir.tmpdir, File.amp_make_tmpname("bundlerepo"))
28
+ File.mkdir(@temp_parent)
29
+ tmprepo = LocalRepository.new(@temp_parent, true, config) # true -> create
30
+ super(@temp_parent, false, config) # and proceed as scheduled!
31
+ end
32
+
33
+ # Set up our URL variable, if anyone asks us what it is
34
+ if path
35
+ @url = "bundle:#{path}+#{bundle_name}"
36
+ else
37
+ @url = "bundle:#{bundle_name}"
38
+ end
39
+
40
+ @temp_file = nil
41
+ @bundle_file = File.open(bundle_name, "r")
42
+
43
+ @bundle_file.seek(0, IO::SEEK_END)
44
+ Amp::UI.debug "Bundle File Size: #{@bundle_file.tell}"
45
+ @bundle_file.seek(0, IO::SEEK_SET)
46
+
47
+ # OK, now for the fun part - check the header to see if we're compressed.
48
+ header = @bundle_file.read(6)
49
+ # And switch based on that header
50
+ if !header.start_with?("HG")
51
+ # Not even an HG file. FML. Bail
52
+ raise abort("#{bundle_name}: not a Mercurial bundle file")
53
+ elsif not header.start_with?("HG10")
54
+ # Not a version we understand, bail
55
+ raise abort("#{bundle_name}: unknown bundle version")
56
+ elsif header == "HG10BZ" || header == "HG10GZ"
57
+ # Compressed! We'll have to save to a new file, because this could get messy.
58
+ temp_file = Tempfile.new("hg-bundle-hg10un", @root)
59
+ @temp_file_path = temp_file.path
60
+ # Are we BZip, or GZip?
61
+ case header
62
+ when "HG10BZ"
63
+ # fuck BZip. Seriously.
64
+ headerio = StringIO.new "BZ", (ruby_19? ? "w+:ASCII-8BIT" : "w+")
65
+ input = Amp::Support::MultiIO.new(headerio, @bundle_file)
66
+ decomp = BZ2::Reader.new(input)
67
+ when "HG10GZ"
68
+ # Gzip is much nicer.
69
+ decomp = Zlib::GzipReader.new(@bundle_file)
70
+ end
71
+
72
+ # We're writing this in an uncompressed fashion, of course.
73
+ @temp_file.write("HG10UN")
74
+ # While we can uncompressed....
75
+ while !r.eof? do
76
+ # Write the uncompressed data to our new file!
77
+ @temp_file.write decomp.read(4096)
78
+ end
79
+ # and close 'er up
80
+ @temp_file.close
81
+
82
+ # Close the compressed bundle file
83
+ @bundle_file.close
84
+ # And re-open the uncompressed bundle file!
85
+ @bundle_file = File.open(@temp_file_path, "r")
86
+ # Skip the header.
87
+ @bundle_file.seek(6)
88
+ elsif header == "HG10UN"
89
+ # uncompressed, do nothing
90
+ else
91
+ # We have no idae what's going on
92
+ raise abort("#{bundle_name}: unknown bundle compression type")
93
+ end
94
+ # This hash stores pairs of {filename => position_in_bundle_file_of_this_file}
95
+ @bundle_files_positions = {}
96
+ end
97
+
98
+ ##
99
+ # Gets the changelog of the repository. This is different from {LocalRepository#changelog}
100
+ # in that it uses a {BundleChangeLog}. Also, since the manifest is stored in the bundle
101
+ # directly after the changelog, by checking our position in the bundle file, we can save
102
+ # where the bundle_file is stored.
103
+ #
104
+ # @return [BundleChangeLog] the changelog for this repository.
105
+ def changelog
106
+ @changelog ||= Bundles::BundleChangeLog.new(@store.opener, @bundle_file)
107
+ @manifest_start ||= @bundle_file.tell
108
+ @changelog
109
+ end
110
+
111
+ ##
112
+ # Gets the manifest of the repository. This is different from {LocalRepository#manifest}
113
+ # in that it uses a {BundleManifest}. The file logs are stored in the bundle directly
114
+ # after the manifest, so once we load the manifest, we save where the file logs start
115
+ # when we are done loading the manifest.
116
+ #
117
+ # This has the side-effect of loading the changelog, if it hasn't been loaded already -#
118
+ # this is necessary because the manifest changesets are stored after the changelog changesets,
119
+ # and we must fully load the changelog changesets to know where to look for the manifest changesets.
120
+ #
121
+ # Don't look at me, I didn't design the file format.
122
+ #
123
+ # @return [BundleChangeLog] the changelog for this repository.
124
+ def manifest
125
+ return @manifest if @manifest
126
+ @bundle_file.seek manifest_start
127
+ @manifest ||= Bundles::BundleManifest.new @store.opener, @bundle_file, proc {|n| changelog.rev(n) }
128
+ @file_start ||= @bundle_file.tell
129
+ @manifest
130
+ end
131
+
132
+ ##
133
+ # Returns the position in the bundle file where the manifest changesets are located.
134
+ # This involves loading the changelog first - see {#manifest}
135
+ #
136
+ # @return [Integer] the position in the bundle file where we can find the manifest
137
+ # changesets.
138
+ def manifest_start
139
+ changelog && @manifest_start
140
+ end
141
+
142
+ ##
143
+ # Returns the position in the bundle file where the file log changesets are located.
144
+ # This involves loading the changelog and the manifest first - see {#manifest}.
145
+ #
146
+ # @return [Integer] the position in the bundle file where we can find the file-log
147
+ # changesets.
148
+ def file_start
149
+ manifest && @file_start
150
+ end
151
+
152
+ ##
153
+ # Gets the file-log for the given path, so we can look at an individual
154
+ # file's history, for example. However, we need to be cognizant of files that
155
+ # traverse the local repository's history as well as the bundle file.
156
+ #
157
+ # @param [String] f the path to the file
158
+ # @return [FileLog] a filelog (a type of revision log) for the given file
159
+ def file(filename)
160
+
161
+ # Load the file-log positions now - we didn't do this in the constructor for a reason
162
+ # (if they don't ask for them, don't load them!)
163
+ if @bundle_files_positions.empty?
164
+ # Jump to the file position
165
+ @bundle_file.seek file_start
166
+ while true
167
+ # get a changegroup chunk - it'll be the filename
168
+ chunk = RevlogSupport::ChangeGroup.get_chunk @bundle_file
169
+ # no filename? bail
170
+ break if chunk.nil? || chunk.empty?
171
+
172
+ # Now that we've read the filename, we're at the start of the changelogs for that
173
+ # file. So let's save this position for later.
174
+ @bundle_files_positions[chunk] = @bundle_file.tell
175
+ # Then read chunks until we get to the next file!
176
+ RevlogSupport::ChangeGroup.each_chunk(@bundle_file) {|c|}
177
+ end
178
+ end
179
+
180
+ # Remove leading slash
181
+ filename = filename.shift("/")
182
+
183
+ # Does this file cross local history as well as the bundle?
184
+ if @bundle_files_positions[filename]
185
+ # If so, we'll need to make a BundleFileLog. Meh.
186
+ @bundle_file.seek @bundle_files_positions[filename]
187
+ Bundles::BundleFileLog.new @store.opener, filename, @bundle_file, proc {|n| changelog.rev(n) }
188
+ else
189
+ # Nope? Make a normal FileLog!
190
+ FileLog.new(@store.opener, filename)
191
+ end
192
+ end
193
+
194
+ ##
195
+ # Gets the URL for this repository - unused, I believe.
196
+ #
197
+ # @return [String] the URL for the repository
198
+ def url; @url; end
199
+
200
+ ##
201
+ # Closes the repository - in this case, it closes the bundle_file. Analogous to closing
202
+ # an SSHRepository's socket.
203
+ def close
204
+ @bundle_file.close
205
+ end
206
+
207
+ # We can't copy files. Read-only.
208
+ def can_copy?; false; end
209
+ # Gets the current working directory. Not sure why we need this.
210
+ def get_cwd; Dir.pwd; end
211
+
212
+ end
213
+ end
214
+ end