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,90 @@
1
+ # With ziprequire you can load ruby modules from a zip file. This means
2
+ # ruby's module include path can include zip-files.
3
+ #
4
+ # The following example creates a zip file with a single entry
5
+ # <code>log/simplelog.rb</code> that contains a single function
6
+ # <code>simpleLog</code>:
7
+ #
8
+ # require 'zip/zipfilesystem'
9
+ #
10
+ # Zip::ZipFile.open("my.zip", true) {
11
+ # |zf|
12
+ # zf.file.open("log/simplelog.rb", "w") {
13
+ # |f|
14
+ # f.puts "def simpleLog(v)"
15
+ # f.puts ' Kernel.puts "INFO: #{v}"'
16
+ # f.puts "end"
17
+ # }
18
+ # }
19
+ #
20
+ # To use the ruby module stored in the zip archive simply require
21
+ # <code>zip/ziprequire</code> and include the <code>my.zip</code> zip
22
+ # file in the module search path. The following command shows one
23
+ # way to do this:
24
+ #
25
+ # ruby -rzip/ziprequire -Imy.zip -e " require 'log/simplelog'; simpleLog 'Hello world' "
26
+
27
+ #$: << 'data/rubycode.zip' << 'data/rubycode2.zip'
28
+
29
+
30
+ need{ 'zip' }
31
+
32
+ class ZipList #:nodoc:all
33
+ def initialize(zipFileList)
34
+ @zipFileList = zipFileList
35
+ end
36
+
37
+ def get_input_stream(entry, &aProc)
38
+ @zipFileList.each {
39
+ |zfName|
40
+ Zip::ZipFile.open(zfName) {
41
+ |zf|
42
+ begin
43
+ return zf.get_input_stream(entry, &aProc)
44
+ rescue Errno::ENOENT
45
+ end
46
+ }
47
+ }
48
+ raise Errno::ENOENT,
49
+ "No matching entry found in zip files '#{@zipFileList.join(', ')}' "+
50
+ " for '#{entry}'"
51
+ end
52
+ end
53
+
54
+
55
+ module Kernel #:nodoc:all
56
+ alias :oldRequire :require
57
+
58
+ def require(moduleName)
59
+ zip_require(moduleName) || oldRequire(moduleName)
60
+ end
61
+
62
+ def zip_require(moduleName)
63
+ return false if already_loaded?(moduleName)
64
+ get_resource(ensure_rb_extension(moduleName)) {
65
+ |zis|
66
+ eval(zis.read); $" << moduleName
67
+ }
68
+ return true
69
+ rescue Errno::ENOENT => ex
70
+ return false
71
+ end
72
+
73
+ def get_resource(resourceName, &aProc)
74
+ zl = ZipList.new($:.grep(/\.zip$/))
75
+ zl.get_input_stream(resourceName, &aProc)
76
+ end
77
+
78
+ def already_loaded?(moduleName)
79
+ moduleRE = Regexp.new("^"+moduleName+"(\.rb|\.so|\.dll|\.o)?$")
80
+ $".detect { |e| e =~ moduleRE } != nil
81
+ end
82
+
83
+ def ensure_rb_extension(aString)
84
+ aString.sub(/(\.rb)?$/i, ".rb")
85
+ end
86
+ end
87
+
88
+ # Copyright (C) 2002 Thomas Sondergaard
89
+ # rubyzip is free software; you can redistribute it and/or
90
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,97 @@
1
+ # = Amp
2
+ module Amp
3
+ # = Encoding
4
+ module Encoding
5
+ # This class provides methods for encoding and decoding to base85, a storage format used
6
+ # by Mercurial. Base85 is like base64, only with some extra characters to improve compression.
7
+ # This is a direct port of the python file base85.py in the Mercurial distribution.
8
+ class Base85
9
+ # The allowable Base 85 characters (encoding)
10
+ B85chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~'
11
+ # The lookup table to go from character -> decimal (decoding)
12
+ B85dec = {}
13
+ # Prepare the decoding table
14
+ B85chars.size.times do |i|
15
+ B85dec[B85chars[i,1]] = i
16
+ end
17
+
18
+ ##
19
+ # Encodes the given string in Base85, and possibly pad it. This code makes sense to me. Fuck I hate
20
+ # python.
21
+ #
22
+ # @param [String] str the string to be encoded
23
+ # @param [Boolean] pad whether or not to pad the resulting output
24
+ # @return [String] Base85 encoded string
25
+ def self.encode str, pad=false
26
+ l = str.size
27
+ r = l % 4
28
+ str += "\0" * (4-r)
29
+ longs = str.size >> 2
30
+ out = []
31
+ words = str.unpack("N#{longs}")
32
+
33
+ words.each do |word|
34
+ word, r = word.divmod 85
35
+ e = B85chars[r].chr
36
+ word, r = word.divmod 85
37
+ d = B85chars[r].chr
38
+ word, r = word.divmod 85
39
+ c = B85chars[r].chr
40
+ word, r = word.divmod 85
41
+ b = B85chars[r].chr
42
+ word, r = word.divmod 85
43
+ a = B85chars[r].chr
44
+
45
+ out += [a,b,c,d,e]
46
+ end
47
+
48
+ out = out.join("")
49
+ return out if pad
50
+
51
+ olen = l % 4
52
+ olen += 1 if olen > 0
53
+ olen += l / 4 * 5
54
+
55
+ out[0 .. olen-1]
56
+ end
57
+
58
+ ##
59
+ # Decodes a base85 encoded string and returns it. This code sort of mystifies me.
60
+ # Slash looking at it I'm not sure why we don't just code it in C. Maybe we will eventually.
61
+ # Fucking python coders.
62
+ #
63
+ # @param [String] text the base85 encoded string to decode
64
+ # @return [String] the decoded text
65
+ def self.decode text
66
+ l = text.size
67
+ out = []
68
+ i = 0
69
+ while i < text.size
70
+ chunk = text[i .. i+4]
71
+ acc = 0
72
+ chunk.size.times do |j|
73
+ acc = acc * 85 + B85dec[chunk[j].chr]
74
+ end
75
+ out << acc
76
+ i += 5
77
+ end
78
+
79
+ cl = l % 5
80
+ if cl > 0
81
+ acc *= 85 ** (5 - cl)
82
+ if cl > 1
83
+ acc += 0xffffff >> (cl - 2) * 8
84
+ end
85
+ out[-1] = acc
86
+ end
87
+
88
+ out = out.pack("N#{out.size}")
89
+ if cl > 0
90
+ out = out[0 .. (-1 * (5-cl) - 1)]
91
+ end
92
+ out
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,82 @@
1
+ module Amp
2
+ ##
3
+ # Binary diffs
4
+ module Diffs
5
+
6
+ ##
7
+ # Methods for producing a binary diff file for 2 input strings. Direct port
8
+ # from pure/bdiff.py in the mercurial source.
9
+ module BinaryDiff
10
+
11
+ ##
12
+ # Produces a binary diff file from the input strings, str1 and str2. Works by
13
+ # getting the list of matching blocks (using {SequenceMatcher}) and filling in
14
+ # the gaps between them. Basically.
15
+ #
16
+ # @param [String] str1 the source string/file
17
+ # @param [String] str2 the destination string/file
18
+ # @return [String] A binary string representing the diff between the two strings/files
19
+ def bdiff(str1, str2)
20
+ # break 'em up into lines
21
+ a = []
22
+ str1.each_line {|l| a << l}
23
+ b = []
24
+ str2.each_line {|l| b << l}
25
+
26
+ if a.nil? || a.empty?
27
+ s = b.join
28
+ return [0,0,s.size].pack("NNN") + s
29
+ end
30
+
31
+ bin = []
32
+ byte_offsets = [0]
33
+ a.each {|line| byte_offsets << (byte_offsets.last + line.size) }
34
+ # Get all the sections of a and b that actually match each other.
35
+ matched_blocks = SequenceMatcher.new(a, b).get_matching_blocks
36
+ la = lb = 0
37
+ matched_blocks.each do |block|
38
+ am, bm, size = block[:start_a], block[:start_b], block[:length]
39
+
40
+ # At this point, a[la..am-1] does NOT equal b[lb..bm-1]. We know this
41
+ # because the block we just got is a *matching* block. It tells us where
42
+ # the two strings are the *same*. So a[la..am-1] is the source text, and
43
+ # b[lb..bm-1] is the destination text of our diff. Thus, we say "replace
44
+ # the text in a from (la..am) with the following text we got from b".
45
+ # Since the array byte_offsets[] contains the actual byte offsets of each line,
46
+ # our diff is stored as [start_a_text_to_replace, end_a_text_to_replace,
47
+ # size_of_replacement_text, replacement_text].
48
+ s = b[lb .. (bm-1)].join unless lb == bm && lb == 0
49
+ s = "" if lb == bm && lb == 0
50
+ bin << [byte_offsets[la], byte_offsets[am], s.size].pack("NNN") + s if am > la || s.any?
51
+ la = am + size
52
+ lb = bm + size
53
+ end
54
+ bin.join
55
+ end
56
+ module_function :bdiff
57
+
58
+ ##
59
+ # Breaks the 2 input strings into blocks that match each other. Uses
60
+ # {SequenceMatcher} and just manipulates the output a little.
61
+ #
62
+ # @param [String] str1 the source string
63
+ # @param [String] str2 the destination string
64
+ # @return [[Hash]] The matching blocks, with keys :start_a, :start_b, :end_a, :end_b
65
+ def blocks(str1, str2)
66
+ an = str1.split_lines_better
67
+ bn = str2.split_lines_better
68
+
69
+ matches = Diffs::SequenceMatcher.new(an, bn).get_matching_blocks
70
+ matches.map do |match|
71
+ {:start_a => match[:start_a], :end_a => match[:start_a] + match[:length],
72
+ :start_b => match[:start_b], :end_b => match[:start_b] + match[:length] }
73
+ end
74
+ end
75
+ module_function :blocks
76
+
77
+ def self.blocks_as_array(str1, str2)
78
+ blocks(str1,str2).map {|h| [h[:start_a], h[:end_a], h[:start_b], h[:end_b]]}
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,166 @@
1
+ module Amp
2
+ ##
3
+ # = Difflib
4
+ # Port of the Python Difflib. Only ports what is necessary. The cool part is that
5
+ # this works for anything that has []! So it'll find matches in an array of lines
6
+ # or just strings.
7
+ module Diffs
8
+ ##
9
+ # Port of the Python SequenceMatcher, leaving out parts that Mercurial doesn't use.
10
+ class SequenceMatcher
11
+
12
+ ##
13
+ # Initializes the sequence matcher.
14
+ #
15
+ # @param [String] seq1 The source data
16
+ # @param [String] seq2 The "destination" data
17
+ def initialize(seq1='', seq2='')
18
+ @a = @b = nil
19
+ set_seqs(seq1,seq2)
20
+ end
21
+ ##
22
+ # Initializes the 2 sequences and prepares the rest of the matcher
23
+ #
24
+ # @param [#each] seq1 The source input sequence. Can be anything responding to #each.
25
+ # @param [#each] seq2 The destination sequence - what we transform seq1 into.
26
+ def set_seqs(seq1,seq2); set_seq1(seq1); set_seq2(seq2); end
27
+
28
+ ##
29
+ # Initializes the source sequence and resets the matching blocks.
30
+ #
31
+ # @param [#each] seq1 The source input sequence.
32
+ def set_seq1(seq1)
33
+ return if @a == seq1
34
+ @a = seq1
35
+ @matching_blocks = nil
36
+ end
37
+ ##
38
+ # Initializes the destination sequence and resets the matching blocks. It also prepares
39
+ # some optimization data.
40
+ #
41
+ # @param [#each] seq2 The destination input sequence.
42
+ def set_seq2(seq2)
43
+ return if @b == seq2
44
+ @b = seq2
45
+ @matching_blocks = nil
46
+ build_b2j
47
+ end
48
+
49
+ ##
50
+ # Sets up some optimization stuff. internal use only. I fucking hate python but ok here's what it
51
+ # does... it goes through the destination sequence, setting the indices into the destination where
52
+ # you can find each line. So it'll save the fact that "return nil" appears on lines 3, 14, and 20.
53
+ # at the same time it's looking for "popular" entries, that are ignored to save runtime later. They
54
+ # get removed from the list.
55
+ # The original source respected a "junk" parameter, which was also removed from the list. But
56
+ # Mercurial doesn't use the junk parameter, so I stripped that code.
57
+ def build_b2j
58
+ n = @b.size
59
+ @b2j = {}
60
+ populardict = {}
61
+ n.times do |i|
62
+ elt = @b[i,1]
63
+ if @b2j[elt]
64
+ indices = @b2j[elt]
65
+ if n >= 2000 && (indices.size * 100 > n)
66
+ populardict[elt] = true
67
+ indices.clear
68
+ else
69
+ indices << i
70
+ end
71
+ else
72
+ @b2j[elt] = [i]
73
+ end
74
+ end
75
+
76
+ populardict.each do |k,v|
77
+ @b2j.delete k
78
+ end
79
+ end
80
+
81
+ ##
82
+ # Finds the longest match in the 2 sequences between the 2 ranges provided.
83
+ #
84
+ # @param alo don't look at any part of the source before this
85
+ # @param ahi don't look at any part of the source after this
86
+ # @param blo don't look at any part of the destination before this
87
+ # @param bhi don't look at any part of the destination after this
88
+ # @return [[Integer,Integer,Integer]] The return is of the form
89
+ # [start_source, start_destination, length_of_common_sequence].
90
+ # source[start_source + i] == destination[start_destination + i] for all 0 <= i < length_of_common_sequence
91
+ def find_longest_match(alo, ahi, blo, bhi)
92
+ j2len = {}
93
+ besti, bestj, bestsize = alo, blo, 0
94
+ alo.upto(ahi-1) do |i|
95
+ newj2len = {}
96
+ @b2j[@a[i,1]] && @b2j[@a[i,1]].each do |j|
97
+ next if j < blo
98
+ break if j >= bhi
99
+ k = newj2len[j] = j2len[j-1].to_i + 1
100
+ besti, bestj, bestsize = i-k+1, j-k+1, k if k > bestsize
101
+ end
102
+ j2len = newj2len
103
+ end
104
+ # we ignored popular elements before. they're being added here.
105
+ while besti > alo && bestj > blo && @a[besti-1,1] == @b[bestj-1,1]
106
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize + 1
107
+ end
108
+ # we ignored popular elements before. they're being added here.
109
+ while besti+bestsize < ahi && bestj+bestsize < bhi && @a[besti+bestsize,1] == @b[bestj+bestsize,1]
110
+ bestsize += 1
111
+ end
112
+ return [besti, bestj, bestsize]
113
+ end
114
+
115
+ ##
116
+ # This will go through and find all the matching blocks in the
117
+ # 2 sequences, picking out the best sequences using find_longest_match.
118
+ #
119
+ # @return [[Hash]] A list of hashes, each of which represents 1 matching block.
120
+ def get_matching_blocks
121
+ return @matching_blocks if @matching_blocks
122
+ la, lb = @a.size, @b.size
123
+
124
+ # This is best done recursively, but if you have a large file it can blow
125
+ # the stack. We ignore popular lines here to speed things up. I guess.
126
+ queue = [[0, la, 0, lb]]
127
+ @matching_blocks = []
128
+ while queue.any?
129
+ alo, ahi, blo, bhi = queue.shift
130
+ i, j, k = x = find_longest_match(alo, ahi, blo, bhi)
131
+ if k > 0
132
+ @matching_blocks << x
133
+ if alo < i && blo < j
134
+ queue << [alo, i, blo, j]
135
+ end
136
+ if i+k < ahi && j+k < bhi
137
+ queue << [i+k, ahi, j+k, bhi]
138
+ end
139
+ end
140
+ end
141
+ # Sort 'em from beginning of the sequences to the end
142
+ @matching_blocks.sort!
143
+ i1 = j1 = k1 = 0
144
+ non_adjacent = []
145
+ # If any "popular" entries were ignored, let's add them to the sequence now.
146
+ @matching_blocks.each do |i2, j2, k2|
147
+ if i1 + k1 == i2 && j1 + k1 == j2
148
+ k1 += k2
149
+ else
150
+ non_adjacent << [i1, j1, k1] if k1 > 0
151
+ i1, j1, k1 = i2, j2, k2
152
+ end
153
+ end
154
+ # Add the last found sequence if there is one
155
+ non_adjacent << [i1, j1, k1] if k1 > 0
156
+ # Add the terminator sequence
157
+ non_adjacent << [la, lb, 0]
158
+ # Make the output ruby-like, we don't need fucking tuples
159
+ @matching_blocks = non_adjacent.map do |i, j, k|
160
+ {:start_a => i, :start_b => j, :length => k}
161
+ end
162
+ @matching_blocks
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,378 @@
1
+ module Amp
2
+ module Diffs
3
+ ##
4
+ # = MercurialDiff
5
+ # Mercurial has it's own implementation of the unified diff, because windows
6
+ # boxes don't have diff -u. Plus code is faster than the shell.
7
+ # Lame. That's ok, it's pretty easy to do. And we can also add flags and
8
+ # change default settings.
9
+ #
10
+ # Mainly, you're only going to use MercurialDiff.unified_diff(). It's usage
11
+ # is described below.
12
+ module MercurialDiff
13
+ extend self
14
+ ##
15
+ # These are the default options you can modify. Grab them, clone them,
16
+ # change them. Notice: You have to *clone* this when you use it, or
17
+ # you will be changing the default options!
18
+ DEFAULT_OPTIONS = {:context => 3, :text => false, :show_func => false,
19
+ :git => false, :no_dates => false, :ignore_ws => false,
20
+ :ignore_ws_amount => false, :ignore_blank_lines => false,
21
+ :pretty => false}
22
+
23
+ ##
24
+ # Clear up whitespace in the text if we have any options relating
25
+ # to getting rid of whitespace.
26
+ #
27
+ # @param [String] text the text to modify
28
+ # @param [Hash] options the options to use when deciding how to clean text
29
+ # @option [Boolean] options :ignore_ws (false) do we ignore all whitespace?
30
+ # this has the net effect of removing all whitespace.
31
+ # @option [Boolean] options :ignore_ws_amount (false) when this option is
32
+ # true, we only remove "excessive" whitespace - more than 1 space or tab.
33
+ # we then substitute it all with 1 space.
34
+ # @option [Boolean] options :ignore_blank_lines (false) when this option
35
+ # is true, we remove all extra blank lines.
36
+ def whitespace_clean(text, options=DEFAULT_OPTIONS)
37
+ if options[:ignore_ws]
38
+ text.gsub!(/[ \t]+/, "") #warnings made me use parens
39
+ elsif options[:ignore_ws_amount]
40
+ text.gsub!(/[ \t]+/, ' ')
41
+ text.gsub!(/[ \t]+\n/, "\n")
42
+ end
43
+ text.gsub!(/\n+/, '') if options[:ignore_blank_lines]
44
+ text
45
+ end
46
+
47
+ ##
48
+ # Given a line, returns a string that represents "adding that line" in a diff,
49
+ # based on the options.
50
+ #
51
+ # @param [String] input the input line
52
+ # @return [String] the output line, in a format indicating it is "added"
53
+ def add_line(input, options)
54
+ options[:pretty] ? "+#{input.chomp}".green+"\n" : "+#{input}"
55
+ end
56
+
57
+ ##
58
+ # Given a line, returns a string that represents "removing that line" in a diff,
59
+ # based on the options.
60
+ #
61
+ # @param [String] input the input line
62
+ # @return [String] the output line, in a format indicating it is "removed"
63
+ def remove_line(input, options)
64
+ options[:pretty] ? "-#{input.chomp}".red+"\n" : "-#{input}"
65
+ end
66
+
67
+ ##
68
+ # Creates a header or something? Not sure what this is used for, no code
69
+ # references it. I think it's for git or something. eh.
70
+ def diff_line(revisions, a, b, options=DEFAULT_OPTIONS)
71
+ options = DEFAULT_OPTIONS.merge options
72
+ parts = ['diff']
73
+
74
+ parts << '--git' if options[:git]
75
+ parts << revisions.map {|r| "-r #{r}"}.join(' ') if revisions && !options[:git]
76
+ if options[:git]
77
+ parts << "a/#{a}"
78
+ parts << "b/#{b}"
79
+ else
80
+ parts << a
81
+ end
82
+ parts.join(' ') + "\n"
83
+ end
84
+
85
+ ##
86
+ # Creates a date tag appropriate for diffs. Not all diff types use
87
+ # dates though (namely git, apparently), so the options matter.
88
+ #
89
+ # @param [Time] date the time that we want to make a spiffy date line for
90
+ # @param [String] fn1 the filename of the file being stamped. Only
91
+ # used if the addtab option is on.
92
+ # @param [Boolean] addtab (false) whether or not to add a tab in the
93
+ # line or not. Only used if we're in git mode or no-date mode.
94
+ # @param options the options to use while creating the date line.
95
+ # @option [Boolean] options :git (false) are we creating a git diff?
96
+ # this will deactivate dates.
97
+ # @option options [Boolean] :nodates (false) should we never print dates?
98
+ def date_tag(date, fn1, addtab = true, options = DEFAULT_OPTIONS)
99
+ return "\t#{date.to_diff}\n" if !(options[:git]) && !(options[:nodates])
100
+ return "\t\n" if addtab && fn1 =~ / /
101
+ return "\n"
102
+ end
103
+
104
+ ##
105
+ # Returns a unified diff based on the 2 blocks of text, their modification
106
+ # times, their filenames, and the options.
107
+ #
108
+ # This is a self-contained replacement for diffs.
109
+ #
110
+ # @param a the original text
111
+ # @param [Time] ad the modification timestamp for the old file
112
+ # @param b the new text
113
+ # @param [Time] bd the modification timestamp for the new file
114
+ # @param fn1 the old filename
115
+ # @param fn2 the new filename
116
+ # @param r not sure what this does
117
+ # @param options the options we will be using. There's a lot of settings,
118
+ # see the descriptions for {whitespace_clean} and {date_tag}.
119
+ def unified_diff(a, ad, b, bd, fn1, fn2, r=nil, options=DEFAULT_OPTIONS)
120
+ return "" if (a.nil? || a.empty?) && (b.nil? || b.empty?)
121
+ epoch = Time.at(0)
122
+ if !options[:text] && (!a.nil? && a.binary? || !b.nil? && b.binary?)
123
+ return "" if a.any? && b.any? && a.size == b.size && a == b #DERR
124
+ l = ["Binary file #{fn1} has changed\n"]
125
+ elsif a.nil? || a.empty?
126
+ b = b.split_lines_better
127
+ header = []
128
+ if options[:pretty]
129
+ l1 = a.nil? ? "Added file " : "Changed file "
130
+ l1 += "#{fn2} at #{date_tag(bd,fn1,true,options)}"
131
+ l1 = l1.cyan
132
+ header << l1
133
+ else
134
+ if a.nil?
135
+ header << "--- /dev/null#{date_tag(epoch, fn1, false, options)}"
136
+ else
137
+ header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,options)}"
138
+ end
139
+ header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,options)}"
140
+ header << "@@ -0,0 +1,#{b.size} @@\n"
141
+ end
142
+ l = header + (b.map {|line| add_line(line, options)})
143
+ elsif b.nil? || b.empty?
144
+ a = b.split_lines_better
145
+ header = []
146
+ if options[:pretty]
147
+ l1 = b.nil? ? "Removed file " : "Changed file "
148
+ l1 += "#{fn2} at #{date_tag(bd,fn1,true,options)}"
149
+ l1 = l1.cyan
150
+ header << l1
151
+ else
152
+ header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,options)}"
153
+ if b.nil?
154
+ header << "+++ /dev/null#{date_tag(epoch, fn1, false, options)}"
155
+ else
156
+ header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,options)}"
157
+ end
158
+ header << "@@ -1,#{a.size} +0,0 @@\n"
159
+ end
160
+ l = header + (a.map {|line| remove_line(line, options)})
161
+ else
162
+ al = a.split_lines_better
163
+ bl = b.split_lines_better
164
+ l = bunidiff(a, b, al, bl, "a/"+fn1, "b/"+fn2, options)
165
+ return "" if l.nil? || l.empty?
166
+ if options[:pretty]
167
+ l.shift
168
+ if fn1 == fn2
169
+ l[0] = "Changed file #{fn1.cyan} at #{date_tag(bd,fn1,true,options).lstrip}"
170
+ else
171
+ l[0] = "Moved file from #{fn1.cyan} to #{fn2.cyan}"
172
+ end
173
+ else
174
+ l[0] = "#{l[0][0 .. -3]}#{date_tag(ad,fn1,true,options)}"
175
+ l[1] = "#{l[1][0 .. -3]}#{date_tag(bd,fn1,true,options)}"
176
+ end
177
+ end
178
+
179
+ l.size.times do |ln|
180
+ if l[ln][-1,1] != "\n"
181
+ l[ln] << "\n\\n"
182
+ end
183
+ end
184
+
185
+ if r
186
+ l.unshift diff_line(r, fn1, fn2, options)
187
+ end
188
+
189
+ l.join
190
+ end
191
+
192
+ ##
193
+ # Starts a block ending context for a change - part of the unified diff
194
+ # format.
195
+ def context_end(l, len, options)
196
+ ret = l + options[:context]
197
+ ret = len if ret > len
198
+ ret
199
+ end
200
+
201
+ ##
202
+ # Starts a block starting context for a change - part of the unified diff
203
+ # format.
204
+ def context_start(l, options)
205
+ ret = l - options[:context]
206
+ return 0 if ret < 0
207
+ ret
208
+ end
209
+
210
+ ##
211
+ # Given a hunk of changes, yield each line we need to write to the diff.
212
+ #
213
+ # @param [Hash] hunk specifies a block of lines that changed between
214
+ # the two files.
215
+ # @param header the header for the block, if we have one.
216
+ # @param l1 the original lines - used for context (unified diff format)
217
+ # @param delta the lines that have changed thus far
218
+ # @param options settings for the unified diff action. unused mostly here.
219
+ def yield_hunk(hunk, header, l1, delta, options)
220
+ header.each {|x| yield x} if header && header.any?
221
+ delta = hunk[:delta]
222
+ astart, a2, bstart, b2 = hunk[:start_a], hunk[:end_a], hunk[:start_b], hunk[:end_b]
223
+ aend = context_end(a2,l1.size,options)
224
+ alen = aend - astart
225
+ blen = b2 - bstart + aend - a2
226
+
227
+ # i seriously don't know what this does.
228
+ func = ""
229
+ if options[:show_func]
230
+ (astart - 1).downto(0) do |x|
231
+ t = l1[x].rstrip
232
+ if t =~ /\w/
233
+ func = ' ' + t[0 .. 39]
234
+ break
235
+ end
236
+ end
237
+ end
238
+
239
+ # yield the header
240
+ if options[:pretty]
241
+ yield "From original lines #{astart + 1}-#{alen+astart+1}".yellow + "\n"
242
+ else
243
+ yield "@@ -%d,%d +%d,%d @@%s\n" % [astart + 1, alen,
244
+ bstart + 1, blen, func]
245
+ end
246
+
247
+ # then yield each line of changes
248
+ delta.each {|x| yield x}
249
+ # then yield some context or something?
250
+ a2.upto(aend-1) {|x| yield ' ' + l1[x] }
251
+ end
252
+
253
+ ##
254
+ # Helper method for creating unified diffs.
255
+ #
256
+ # @param [String] t1 original text
257
+ # @param [String] t2 new text
258
+ # @param [String] l1 the original text broke into lines?
259
+ # @param [String] l2 the new etxt broken into lines?
260
+ # @param [String] header1 the original file's header
261
+ # @param [String] header2 the new file's header
262
+ # @param opts options for the method
263
+ def bunidiff(t1,t2, l1, l2, header1, header2, opts=DEFAULT_OPTIONS)
264
+ header = [ "--- #{header1}\t\n", "+++ #{header2}\t\n" ]
265
+
266
+ diff = BinaryDiff.blocks(t1,t2)
267
+ hunk = nil
268
+ return_hunks = []
269
+ saved_delta = []
270
+ delta = []
271
+ diff.size.times do |i|
272
+ s = (i > 0) ? diff[i-1] : {:start_a => 0, :end_a => 0, :start_b => 0, :end_b => 0}
273
+ saved_delta += delta unless delta.empty?
274
+ delta = []
275
+ s1 = diff[i]
276
+ a1 = s[:end_a]
277
+ a2 = s1[:start_a]
278
+ b1 = s[:end_b]
279
+ b2 = s1[:start_b]
280
+
281
+ old = (a2 == 0) ? [] : l1[a1..(a2-1)]
282
+ newb = (b2 == 0) ? [] : l2[b1..(b2-1)] #stands for new "b"
283
+
284
+ next if old.empty? && newb.empty?
285
+ if opts[:ignore_ws] || opts[:ignore_blank_lines] || opts[:ignore_ws_amount]
286
+ next if whitespace_clean(old.join,opts) == whitespace_clean(newb.join,opts)
287
+ end
288
+
289
+ astart = context_start(a1,opts)
290
+ bstart = context_start(b1,opts)
291
+ prev = nil
292
+ if hunk
293
+ if astart < hunk[:end_a] + opts[:context] + 1
294
+ prev = hunk
295
+ astart = hunk[:end_a]
296
+ bstart = hunk[:end_b]
297
+ else
298
+ yield_hunk(hunk, header, l1, delta, opts) {|x| return_hunks << x}
299
+
300
+ header = nil
301
+ end
302
+ end
303
+ # move this inside previous nested if statements
304
+ if prev
305
+ hunk[:end_a] = a2
306
+ hunk[:end_b] = b2
307
+ delta = hunk[:delta]
308
+ else
309
+ hunk = {:start_a => astart, :end_a => a2, :start_b => bstart, :end_b => b2, :delta => delta}
310
+ end
311
+
312
+ hunk[:delta] += l1[astart..(a1-1)].map {|x| ' ' + x } if a1 > 0
313
+ hunk[:delta] += old.map {|x| remove_line(x, opts) }
314
+ hunk[:delta] += newb.map {|x| add_line(x, opts) }
315
+
316
+ end
317
+ saved_delta += delta
318
+
319
+ yield_hunk(hunk, header, l1, saved_delta, opts) {|x| return_hunks << x} if hunk
320
+ return_hunks
321
+ end
322
+
323
+ ##
324
+ # Unpacks a binary-compressed patch.
325
+ #
326
+ # @param [String] binary the packed binary text to unpack
327
+ def patch_text(binary)
328
+ pos = 0
329
+ t = []
330
+ while pos < binary.size
331
+ p1, p2, l = binary[pos..(pos+11)].unpack("NNN")
332
+ pos += 12
333
+ t << binary[pos..(pos + l - 1)]
334
+ pos += l
335
+ end
336
+ t.join
337
+ end
338
+
339
+ ##
340
+ # Applies the patch _bin_ to the text _a_.
341
+ #
342
+ # @param [String] a the text to patch
343
+ # @param [String] bin the binary patch to apply
344
+ def patch(a, bin)
345
+ MercurialPatch.apply_patches(a, [bin])
346
+ end
347
+
348
+ ##
349
+ # Gets the matching blocks between the two texts.
350
+ #
351
+ # @param [String] a the original text
352
+ # @param [String] b the final text
353
+ # @return [[Hash]] The blocks of changes between the two
354
+ def get_matching_blocks(a, b)
355
+ an = a.split_lines_better
356
+ bn = b.split_lines_better
357
+
358
+ SequenceMatcher.new(an, bn).get_matching_blocks
359
+ end
360
+
361
+ ##
362
+ # Returns the obvious header for when we create a new file
363
+ #
364
+ # @param [Fixnum] length the length of the file
365
+ # @return [String] the obvious header
366
+ def trivial_diff_header(length)
367
+ [0, 0, length].pack("NNN")
368
+ end
369
+
370
+ ##
371
+ # Returns a text diff between a and b. This returns the packed, binary
372
+ # kind of diff.
373
+ def text_diff a,b
374
+ BinaryDiff.bdiff a,b
375
+ end
376
+ end
377
+ end
378
+ end