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,450 @@
1
+ module Amp
2
+ module Merges
3
+
4
+ class MergeAssertion < StandardError; end
5
+ ##
6
+ # SimpleMerge - basic 3-way merging
7
+ #
8
+ # This class takes 2 texts and a common ancestor text, and tries
9
+ # to produce a text incorporating all the changes from ancestor->local
10
+ # and ancestor->remote. It will produce the annoying >>>>>> ====== <<<<<
11
+ # markers just like mercurial/cvs does.
12
+ #
13
+ # For the record, for any methods that don't have comments in the code, I
14
+ # have an excuse: I don't understand the code.
15
+ #
16
+ # p.s. threeway. hehe. three way.
17
+ class ThreeWayMerger
18
+
19
+ def assert(val, msg="Assertion failed")
20
+ raise MergeAssertion.new(msg) unless val
21
+ end
22
+
23
+ # Have there been any conflicts in the merge?
24
+ attr_accessor :conflicts
25
+
26
+ ##
27
+ # Performs a 3-way merge on the 3 files provided. Saves the merged file over the
28
+ # local file. This basically handles the file juggling while applying the instance
29
+ # methods to do merging.
30
+ #
31
+ # @param [String] local path to the original local file
32
+ # @param [String] base path to a (temporary) base file
33
+ # @param [String] other path to a (temporary) target file
34
+ # @param [Hash] opts additional options for merging
35
+ # @return [Boolean] were there conflicts during the merge?
36
+ def self.three_way_merge(local, base, other, opts={})
37
+ name_a = local
38
+ name_b = other
39
+ labels = opts[:labels] || []
40
+
41
+ name_a = labels.shift if labels.any?
42
+ name_b = labels.shift if labels.any?
43
+ raise abort("You can only specify 2 labels") if labels.any?
44
+
45
+ local_text = read_file local
46
+ base_text = read_file base
47
+ other_text = read_file other
48
+ local = Pathname.new(local).realpath
49
+ unless opts[:print]
50
+ # special temp name for our new merged file
51
+ newname = File.amp_make_tmpname local
52
+ out = File.open newname, "w"
53
+
54
+ # add rename method to this object to do atomicity
55
+ def out.rename(local, newname)
56
+ self.close
57
+ File.unlink(local)
58
+ File.move(newname, local)
59
+ end
60
+ else
61
+ out = STDOUT
62
+ end
63
+
64
+ reprocess = !opts[:no_minimal]
65
+ merger = ThreeWayMerger.new(base_text, local_text, other_text)
66
+ merger.merge_lines(:name_a => name_a, :name_b => name_b, :reprocess => reprocess) do |line|
67
+ out.write line
68
+ end
69
+
70
+ out.rename(local, newname) unless opts[:print]
71
+
72
+ if merger.conflicts
73
+ unless opts[:quiet]
74
+ UI.warn("conflicts during merge.")
75
+ end
76
+ return true # yes conflicts
77
+ end
78
+
79
+ false # no conflicts
80
+ end
81
+
82
+ ##
83
+ # Initializes the merger object with the 3 necessary texts, as well as
84
+ # subsections to merge (if we don't want to merge the entire texts).
85
+ #
86
+ # @param [String] base_text the common ancestor text, from which we
87
+ # are merging changes
88
+ # @param [String] a_text one descendent text - typically the local copy
89
+ # of the file
90
+ # @param [String] b_text the other descendent text - typically a copy
91
+ # committed by somebody else.
92
+ # @param [String] base_subset (base_text.split_newlines) the subsection
93
+ # of the common ancestor we are concerned with (if not merging full texts)
94
+ # @param [String] a_subset (a_text.split_newlines) the subsection
95
+ # of the first text we are concerned with (if not merging full texts)
96
+ # @param [String] b_subset (b_text.split_newlines) the subsection
97
+ # of the second text we are concerned with (if not merging full texts)
98
+ def initialize(base_text, a_text, b_text, base=nil, a=nil, b=nil)
99
+ @base_text, @a_text, @b_text = base_text, a_text, b_text
100
+ @base = base || @base_text.split_lines_better
101
+ @a = a || @a_text.split_lines_better
102
+ @b = b || @b_text.split_lines_better
103
+ end
104
+
105
+ ##
106
+ # Merges the texts in a CVS-like form. The start_marker, mid_markers, and end_marker
107
+ # arguments are used to delimit conflicts. Yields lines - doesn't return anything.
108
+ #
109
+ # @yield the merged lines
110
+ # @yieldparam [String] line 1 line that belongs in the merged file.
111
+ def merge_lines(opts = {})
112
+ defaults = {:name_a => nil, :name_b => nil, :name_base => nil,
113
+ :start_marker => "<<<<<<<", :mid_marker => "=======",
114
+ :end_marker => ">>>>>>>", :base_marker => nil, :reprocess => false}
115
+ opts = defaults.merge(opts)
116
+
117
+ @conflicts = false # no conflicts yet!
118
+ # Figure out what our newline character is (silly windows)
119
+ newline = "\n"
120
+ if @a.size > 0
121
+ newline = "\r\n" if @a.first.end_with?("\r\n")
122
+ newline = "\r" if @a.first.end_with?("\r")
123
+ end
124
+
125
+ if opts[:base_marker] && opts[:reprocess]
126
+ raise ArgumentError.new("Can't reprocess and show base markers!")
127
+ end
128
+
129
+ # Add revision names to the markers
130
+ opts[:start_marker] += " #{opts[:name_a]}" if opts[:name_a]
131
+ opts[:end_marker] += " #{opts[:name_b]}" if opts[:name_b]
132
+ opts[:base_marker] += " #{opts[:name_base]}" if opts[:name_base] && opts[:base_marker]
133
+
134
+ merge_method = opts[:reprocess] ? :reprocessed_merge_regions : :merge_regions
135
+ self.send(merge_method) do |*t|
136
+ status = t[0]
137
+ case status
138
+ when :unchanged
139
+ t[1].upto(t[2]-1) {|i| yield @base[i] } # nothing changed, use base
140
+ when :a, :same
141
+ t[1].upto(t[2]-1) {|i| yield @a[i] } # local (A) insertion
142
+ when :b
143
+ t[1].upto(t[2]-1) {|i| yield @b[i] } # remote (B) insertion
144
+ when :conflict
145
+ @conflicts = true # :-( we have conflicts
146
+
147
+ yield opts[:start_marker] + newline # do the <<<<<<
148
+ t[3].upto(t[4]-1) {|i| yield @a[i]} # and the local copy
149
+
150
+ if opts[:base_marker]
151
+ yield base_marker + newline # do the base
152
+ t[1].upto(t[2]-1) {|i| yield @base[i]} # and the base lines
153
+ end
154
+
155
+ yield opts[:mid_marker] + newline # do the =====
156
+ t[5].upto(t[6]-1) {|i| yield @b[i]} # and the remote copy
157
+ yield opts[:end_marker] + newline # and then >>>>>>
158
+ else
159
+ raise ArgumentError.new("invalid region: #{status.inspect}")
160
+ end
161
+ end
162
+
163
+ end
164
+
165
+ ##
166
+ # Yield sequence of line groups. Each one is a tuple:
167
+ #
168
+ # :unchanged, lines
169
+ # Lines unchanged from base
170
+ #
171
+ # :a, lines
172
+ # Lines taken from a
173
+ #
174
+ # :same, lines
175
+ # Lines taken from a (and equal to b)
176
+ #
177
+ # :b, lines
178
+ # Lines taken from b
179
+ #
180
+ # :conflict, base_lines, a_lines, b_lines
181
+ # Lines from base were changed to either a or b and conflict.
182
+ def merge_groups
183
+ merge_regions do |list|
184
+ case list[0]
185
+ when :unchanged
186
+ yield list[0], @base[list[1]..(list[2]-1)]
187
+ when :a, :same
188
+ yield list[0], @a[list[1]..(list[2]-1)]
189
+ when :b
190
+ yield list[0], @b[list[1]..(list[2]-1)]
191
+ when :conflict
192
+ yield list[0], @base[list[1]..(list[2]-1)],
193
+ @a[list[3]..(list[4]-1)],
194
+ @b[list[5]..(list[6]-1)]
195
+ else
196
+ raise ArgumentError.new(list[0])
197
+ end
198
+ end
199
+ end
200
+
201
+ ##
202
+ # Yield sequences of matching and conflicting regions.
203
+ #
204
+ # This returns tuples, where the first value says what kind we
205
+ # have:
206
+ #
207
+ # 'unchanged', start, end
208
+ # Take a region of base[start:end]
209
+ #
210
+ # 'same', astart, aend
211
+ # b and a are different from base but give the same result
212
+ #
213
+ # 'a', start, end
214
+ # Non-clashing insertion from a[start:end]
215
+ #
216
+ # Method is as follows:
217
+ #
218
+ # The two sequences align only on regions which match the base
219
+ # and both descendents. These are found by doing a two-way diff
220
+ # of each one against the base, and then finding the
221
+ # intersections between those regions. These "sync regions"
222
+ # are by definition unchanged in both and easily dealt with.
223
+ #
224
+ # The regions in between can be in any of three cases:
225
+ # conflicted, or changed on only one side.
226
+ #
227
+ # @yield Arrays of regions that require merging
228
+ def merge_regions
229
+ ## NOTE: we use "z" as an abbreviation for "base" or the "ancestor", because
230
+ # we can't very well abbreviate "ancestor" as "a" or "base" as "b".
231
+ idx_z = idx_a = idx_b = 0
232
+
233
+ find_sync_regions.each do |match|
234
+ z_match, z_end = match[:base_start], match[:base_end]
235
+ a_match, a_end = match[:a_start ], match[:a_end ]
236
+ b_match, b_end = match[:b_start ], match[:b_end ]
237
+
238
+ match_len = z_end - z_match
239
+ assert match_len >= 0
240
+ assert match_len == (a_end - a_match), "expected #{match_len}, got #{(a_end - a_match)} (#{a_end} - #{a_match})"
241
+ assert match_len == (b_end - b_match)
242
+
243
+ len_a = a_match - idx_a
244
+ len_b = b_match - idx_b
245
+ len_base = z_match - idx_z
246
+ assert len_a >= 0
247
+ assert len_b >= 0
248
+ assert len_base >= 0
249
+
250
+ if len_a > 0 || len_b > 0
251
+ equal_a = compare_range(@a, idx_a, a_match, @base, idx_z, z_match)
252
+ equal_b = compare_range(@b, idx_b, b_match, @base, idx_z, z_match)
253
+ same = compare_range(@a, idx_a, a_match, @b, idx_b, b_match)
254
+
255
+ if same
256
+ yield :same, idx_a, a_match
257
+ elsif equal_a && !equal_b
258
+ yield :b, idx_b, b_match
259
+ elsif equal_b && !equal_a
260
+ yield :a, idx_a, a_match
261
+ elsif !equal_a && !equal_b
262
+ yield :conflict, idx_z, z_match, idx_a, a_match, idx_b, b_match
263
+ else
264
+ raise AssertionError.new("can't handle a=b=base but unmatched!")
265
+ end
266
+
267
+ idx_a = a_match
268
+ idx_b = b_match
269
+ end
270
+ idx_z = z_match
271
+
272
+ if match_len > 0
273
+ assert idx_a == a_match
274
+ assert idx_b == b_match
275
+ assert idx_z == z_match
276
+
277
+ yield :unchanged, z_match, z_end
278
+
279
+ idx_a = a_end
280
+ idx_b = b_end
281
+ idx_z = z_end
282
+ end
283
+ end
284
+ end
285
+
286
+ ##
287
+ # Take the merge regions yielded by merge_regions, and remove lines where both A and
288
+ # B (local & remote) have made the same changes.
289
+ def reprocessed_merge_regions
290
+ merge_regions do |*region|
291
+ if region[0] != :conflict
292
+ yield *region
293
+ next
294
+ end
295
+ type, idx_z, z_match, idx_a, a_match, idx_b, b_match = region
296
+ a_region = @a[idx_a..(a_match-1)]
297
+ b_region = @b[idx_b..(b_match-1)]
298
+ matches = Amp::Diffs::MercurialDiff.get_matching_blocks(a_region.join, b_region.join)
299
+
300
+ next_a = idx_a
301
+ next_b = idx_b
302
+
303
+ matches[0..-2].each do |block|
304
+ region_ia, region_ib, region_len = block[:start_a], block[:start_b], block[:length]
305
+ region_ia += idx_a
306
+ region_ib += idx_b
307
+
308
+ reg = mismatch_region(next_a, region_ia, next_b, region_ib)
309
+
310
+ yield *reg if reg
311
+ yield :same, region_ia, region_len + region_ia
312
+
313
+ next_a = region_ia + region_len
314
+ next_b = region_ib + region_len
315
+
316
+ end
317
+ reg = mismatch_region(next_a, a_match, next_b, b_match)
318
+ yield *reg if reg
319
+ end
320
+ end
321
+
322
+
323
+
324
+ ##
325
+ # Returns a list of sync'd regions, where both descendents match the base.
326
+ # Generates a list of {:base_start, :base_end, :a_start, :a_end, :b_start, :b_end}
327
+ #
328
+ # @return [Array<Hash>] A list of sync regions, each stored as a hash, with the
329
+ # keys {:base_start, :base_end, :a_start, :a_end, :b_start, :b_end}. There is
330
+ # always a zero-length sync region at the end of any file (because the EOF always
331
+ # matches).
332
+ def find_sync_regions
333
+ idx_a = idx_b = 0
334
+ a_matches = Amp::Diffs::MercurialDiff.get_matching_blocks(@base_text, @a_text)
335
+ b_matches = Amp::Diffs::MercurialDiff.get_matching_blocks(@base_text, @b_text)
336
+
337
+ len_a, len_b = a_matches.size, b_matches.size
338
+ sync_regions = []
339
+
340
+ while idx_a < len_a && idx_b < len_b
341
+ next_a, next_b = a_matches[idx_a], b_matches[idx_b]
342
+
343
+ a_base, a_match, a_len = next_a[:start_a], next_a[:start_b], next_a[:length]
344
+ b_base, b_match, b_len = next_b[:start_a], next_b[:start_b], next_b[:length]
345
+
346
+ intersection = (a_base..(a_base+a_len)) - (b_base..(b_base+b_len))
347
+ if intersection
348
+ # add the sync region
349
+ sync_regions << synced_region_for_intersection(intersection, a_base, b_base, a_match, b_match)
350
+ end
351
+ if (a_base + a_len) < (b_base + b_len)
352
+ idx_a += 1
353
+ else
354
+ idx_b += 1
355
+ end
356
+ end
357
+ # add the EOF-marker
358
+ inter_base = @base.size
359
+ a_base = @a.size
360
+ b_base = @b.size
361
+ sync_regions << {:base_start => inter_base, :base_end => inter_base,
362
+ :a_start => a_base, :a_end => a_base ,
363
+ :b_start => b_base, :b_end => b_base }
364
+
365
+ sync_regions
366
+ end
367
+
368
+ def synced_region_for_intersection(intersection, a_base, b_base, a_match, b_match)
369
+ inter_base = intersection.begin
370
+ inter_end = intersection.end
371
+ inter_len = inter_end - inter_base
372
+
373
+ # found a match of base[inter_base..inter_end] - this may be less than the region
374
+ # that matches in either one. Let's do some assertions
375
+ #assert inter_len <= a_len
376
+ #assert inter_len <= b_len
377
+ assert a_base <= inter_base
378
+ assert b_base <= inter_base
379
+
380
+ # shift section downward or upward
381
+ a_sub = a_match + (inter_base - a_base)
382
+ b_sub = b_match + (inter_base - b_base)
383
+ # end points = base_len + starts
384
+ a_end = a_sub + inter_len
385
+ b_end = b_sub + inter_len
386
+
387
+ # make sure the texts are equal of course....
388
+ assert @base[inter_base..(inter_end-1)] == @a[a_sub..(a_end-1)]
389
+ assert @base[inter_base..(inter_end-1)] == @b[b_sub..(b_end-1)]
390
+
391
+ # return the sync region
392
+ {:base_start => inter_base, :base_end => inter_end,
393
+ :a_start => a_sub, :a_end => a_end ,
394
+ :b_start => b_sub, :b_end => b_end }
395
+ end
396
+
397
+ private
398
+
399
+ def mismatch_region(next_a, region_ia, next_b, region_ib)
400
+ if next_a < region_ia || next_b < region_ib
401
+ return :conflict, nil, nil, next_a, region_ia, next_b, region_ib
402
+ end
403
+ nil
404
+ end
405
+
406
+ ##
407
+ # Reads a file, but raises warnings if it's binary and we shouldn't be
408
+ # working with it.
409
+ #
410
+ # @param [String] filename the path to the file to read
411
+ # @param [Hash] opts the options for handling binary files
412
+ def self.read_file(filename, opts={})
413
+ text = File.read filename
414
+ if text.binary?
415
+ message = "#{filename} appears to be a binary file."
416
+ raise abort(message) unless opts[:text]
417
+ UI.warn(message) unless opts[:quiet]
418
+ end
419
+ text
420
+ end
421
+
422
+ ##
423
+ # Compares arr_a[a_start...a_end] == arr_b[b_start...b_end], without
424
+ # actually cutting up the array and thus allocating memory.
425
+ #
426
+ # @param [Array<Comparable>] arr_a an array of objects that can be compared to arr_b
427
+ # @param [Integer] a_start the index to begin comparison
428
+ # @param [Integer] a_end the index to end comparison (exclusive - arr_a[a_end] is NOT
429
+ # compared to arr_b[b_end])
430
+ # @param [Array<Comparable>] arr_b an array of objects that can be compared to arr_a
431
+ # @param [Integer] b_start the index to begin comparison
432
+ # @param [Integer] b_end the index to end comparison (exclusive - arr_a[a_end] is NOT
433
+ # compared to arr_b[b_end])
434
+ # @return [Boolean] true if arr_a == arr_b, false if arr_a != arr_b
435
+ def compare_range(arr_a, a_start, a_end, arr_b, b_start, b_end)
436
+ return false if (a_end - a_start) != (b_end - b_start)
437
+ idx_a, idx_b = a_start, b_start
438
+ while idx_a < a_end && idx_b < b_end
439
+ return false if arr_a[idx_a] != arr_b[idx_b]
440
+ idx_a += 1
441
+ idx_b += 1
442
+ end
443
+ true
444
+ end
445
+
446
+ end
447
+ end
448
+ end
449
+
450
+ Threesome = Amp::Merges::ThreeWayMerger
@@ -0,0 +1,36 @@
1
+ # alias old_puts puts
2
+ # def puts(*args)
3
+ # return if args.empty?
4
+ # old_puts (['[', caller[0].inspect, ' -- ', *args] << ']').join
5
+ # end
6
+ #
7
+ # alias old_p p
8
+ # def p(*args)
9
+ # args.map! {|a| a.inspect }
10
+ # old_puts (['[', caller[0].inspect, ' -- ', *args] << ']').join
11
+ # end
12
+
13
+ ##
14
+ def show_caller_for(meth, lines, new_meth="__#{meth}__")
15
+ lines = [*lines]
16
+ alias_method "#{new_meth}".to_sym, meth
17
+ self.class_eval(<<-HELP)
18
+ def #{meth}(*args, &block)
19
+ #{lines.join("\n")}
20
+ #{new_meth}(*args, &block)
21
+ end
22
+ HELP
23
+ end
24
+
25
+ $hash = Hash.new {|h, k| h[k] = 0 }
26
+ #
27
+ # String.class_eval do
28
+ # show_caller_for :split_newlines, "$hash[caller[0]] += 1"
29
+ # end
30
+
31
+ if ENV["TESTING"] == "true"
32
+ END {
33
+ require 'pp'
34
+ STDERR.puts $hash.inspect if $hash.any?
35
+ }
36
+ end