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,164 @@
1
+ module Amp
2
+ module Merges
3
+
4
+ ##
5
+ # = MergeState
6
+ # MergeState handles the merge/ directory in the repository, in order
7
+ # to keep track of how well the current merge is progressing. There is
8
+ # a file called merge/state that lists all the files that need merging
9
+ # and a little info about whether it has beeen merged or not.
10
+ #
11
+ # You can add a file to the mergestate, iterate over all of them, quickly
12
+ # look up to see if a file is still dirty, and so on.
13
+ class MergeState
14
+ include Enumerable
15
+
16
+ ##
17
+ # Initializes a new mergestate with the given repo, and reads in all the
18
+ # information from merge/state.
19
+ #
20
+ # @param repo the repository being inspected
21
+ def initialize(repo)
22
+ @repo = repo
23
+ read!
24
+ end
25
+
26
+ ##
27
+ # Resets the merge status, by clearing all merge information and files
28
+ #
29
+ # @param node the node we're working with? seems kinda useless
30
+ def reset(node = nil)
31
+ @state = {}
32
+ @local = node if node
33
+ FileUtils.rm_rf @repo.join("merge")
34
+ end
35
+ alias_method :reset!, :reset
36
+
37
+ ##
38
+ # Returns whether the file is part of a merge or not
39
+ #
40
+ # @return [Boolean] if the dirty file in our state and not nil?
41
+ def include?(dirty_file)
42
+ not @state[dirty_file].nil?
43
+ end
44
+
45
+ ##
46
+ # Accesses the the given file's merge status - can be "u" for unmerged,
47
+ # or other stuff we haven't figured out yet.
48
+ #
49
+ # @param [String] dirty_file the path to the file for merging.
50
+ # @return [String] the status as a letter - so far "u" means unmerged or "r"
51
+ # for resolved.
52
+ def [](dirty_file)
53
+ @state[dirty_file] ? @state[dirty_file][0, 1] : ""
54
+ end
55
+
56
+ ##
57
+ # Adds a file to the mergestate, which creates a separate file
58
+ # in the merge directory with all the information. I don't know
59
+ # what these parameters are for yet.
60
+ def add(fcl, fco, fca, fd, flags)
61
+ hash = Digest::SHA1.new.update(fcl.path).hexdigest
62
+ @repo.open("merge/#{hash}", "w") do |file|
63
+ file.write fcl.data
64
+ end
65
+ @state[fd] = ["u", hash, fcl.path, fca.path, fca.file_node.hexlify,
66
+ fco.path, flags]
67
+ save
68
+ end
69
+
70
+ ##
71
+ # Iterates over all the files that are involved in the current
72
+ # merging transaction.
73
+ #
74
+ # @yield each file, sorted by filename, that needs merging.
75
+ # @yieldparam file the filename that needs (or has been) merged.
76
+ # @yieldparam state all the information about the current merge with
77
+ # this file.
78
+ def each
79
+ @state.keys.sort.each do |key|
80
+ yield(key, @state[key])
81
+ end
82
+ end
83
+
84
+ ##
85
+ # Marks the given file with a given state, which is 1 letter. "u" means
86
+ # unmerged, "r" means resolved.
87
+ #
88
+ # @param [String] dirty_file the file path for marking
89
+ # @param [String] state the state - "u" for unmerged, "r" for resolved.
90
+ def mark(dirty_file, state)
91
+ @state[dirty_file][0] = state
92
+ save
93
+ end
94
+
95
+ ##
96
+ # Resolves the given file for a merge between 2 changesets.
97
+ #
98
+ # @param dirty_file the path to the file for merging
99
+ # @param working_changeset the current changeset that is the destination
100
+ # of the merge
101
+ # @param other_changeset the newer changeset, which we're merging to
102
+ def resolve(dirty_file, working_changeset, other_changeset)
103
+ return 0 if self[dirty_file] == "r"
104
+ state, hash, lfile, afile, anode, ofile, flags = @state[dirty_file]
105
+ r = true
106
+ @repo.open("merge/#{hash}") do |file|
107
+ @repo.working_write(dirty_file, file.read, flags)
108
+ working_file = working_changeset[dirty_file]
109
+ other_file = other_changeset[ofile]
110
+ ancestor_file = @repo.versioned_file(afile, :file_id => anode)
111
+ r = UI.file_merge(@repo, @local, lfile, working_file, other_file, ancestor_file)
112
+ end
113
+
114
+ mark(dirty_file, "r") if r.nil? || r == false
115
+ return r
116
+ end
117
+
118
+ ##
119
+ # Public access to writing the file.
120
+ def save
121
+ write!
122
+ end
123
+ alias_method :save!, :save
124
+
125
+ private
126
+
127
+ ##
128
+ # Reads in the merge state and sets up all our instance variables.
129
+ #
130
+ def read!
131
+ @state = {}
132
+ ignore_missing_files do
133
+ local_node = nil
134
+ @repo.open("merge/state") do |file|
135
+ get_node = true
136
+ file.each_line do |line|
137
+ if get_node
138
+ local_node = line.chomp
139
+ get_node = false
140
+ else
141
+ parts = line.chomp.split("\0")
142
+ @state[parts[0]] = parts[1..-1]
143
+ end
144
+ end
145
+ @local = local_node.unhexlify
146
+ end
147
+ end
148
+ end
149
+
150
+ ##
151
+ # Saves the merge state to disk.
152
+ #
153
+ def write!
154
+ @repo.open("merge/state","w") do |file|
155
+ file.write @local.hexlify + "\n"
156
+ @state.each do |key, val|
157
+ file.write "#{([key] + val).join("\0")}\n"
158
+ end
159
+ end
160
+ end
161
+
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,322 @@
1
+ require 'tempfile'
2
+
3
+ module Amp
4
+ module Merges
5
+
6
+ ##
7
+ # This module handles figuring out how to merge files using the user's
8
+ # preferences. It is mixed into the UI class. The UI class must implement
9
+ # the "config" method.
10
+ module MergeUI
11
+
12
+ ##
13
+ # Performs a 3-way merge in the working directory from vf_local to vf_other,
14
+ # using the common ancestor vf_ancestor.
15
+ #
16
+ # @todo change 1s and 0s to bools
17
+ # @todo consistent return type
18
+ #
19
+ # @param [Repository] repo the repository in which we are merging files
20
+ # @param [String] parent_node the node_id of the parent node before the merge
21
+ # @param [String] original_fn the original local filename before the merge
22
+ # @param [WorkingVersionedFile] vf_local the current, working-directory versioned file
23
+ # @param [VersionedFile] vf_other the file's changeset to which we are migrating
24
+ # @param [VersionedFile] vf_ancestor the common ancestor between vf_local and vf_other
25
+ # @return [Boolean] true if there were conflicts during the merge
26
+ def file_merge(repo, parent_node, original_fn, vf_local, vf_other, vf_ancestor)
27
+ is_binary = proc {|ctx| ctx.data.binary? rescue false}
28
+
29
+ return nil if !(vf_other.cmp vf_local.data)
30
+
31
+ path = vf_local.path
32
+ binary = is_binary[vf_local] || is_binary[vf_other] || is_binary[vf_ancestor]
33
+ symlink = (vf_local.flags + vf_other.flags).include? "l"
34
+
35
+ tool, tool_path = pick_tool(repo, path, binary, symlink)
36
+ UI.status("Picked tool #{tool} for #{path} (binary #{binary} symlink #{symlink})")
37
+
38
+ unless tool
39
+ tool = "internal:local"
40
+ if UI.ask("no tool found to merge #{path}\n"+
41
+ "keep (l)ocal or take (o)ther?") != "l"
42
+ tool = "internal:other"
43
+ end
44
+ end
45
+
46
+ case tool
47
+ when "internal:local"
48
+ return 0
49
+ when "internal:other"
50
+ repo.working_write(path, vf_other.data, vf_other.flags)
51
+ return 0
52
+ when "internal:fail"
53
+ return 1
54
+ end
55
+
56
+ a = repo.working_join(path)
57
+ b_file = save_versioned_file_temp("base", vf_ancestor)
58
+ c_file = save_versioned_file_temp("other", vf_other)
59
+ b, c = b_file.path, c_file.path
60
+
61
+ out = ""
62
+ back = a + ".orig" + File.extname(a)
63
+ File.copy(a, back)
64
+
65
+ if original_fn != vf_other.path
66
+ UI.status("merging #{original_fn} and #{vf_other.path} to #{path}")
67
+ else
68
+ UI.status("merging #{path}")
69
+ end
70
+
71
+ if tool_setting(tool, "premerge", !(binary || symlink))
72
+ ret = ThreeWayMerger.three_way_merge(a, b, c, :quiet => true)
73
+ unless ret
74
+ UI.debug("premerge successful")
75
+ File.unlink(back)
76
+ File.safe_unlink(b)
77
+ File.safe_unlink(c)
78
+ return false
79
+ end
80
+ File.copy(back, a) # restore frmo backup and try again
81
+ end
82
+
83
+ environment = {"HG_FILE" => path,
84
+ "HG_MY_NODE" => parent_node.hexlify[0..11],
85
+ "HG_OTHER_NODE" => vf_other.changeset.to_s,
86
+ "HG_MY_ISLINK" => vf_local.flags.include?("l"),
87
+ "HG_OTHER_ISLINK" => vf_other.flags.include?("l"),
88
+ "HG_BASE_ISLINK" => vf_ancestor.flags.include?("l")}
89
+ if tool == "internal:merge"
90
+ ret = ThreeWayMerger.three_way_merge(a, b, c, :label => ['local', 'other'])
91
+ else
92
+ args = tool_setting_string(tool, "args", "$local $base $other")
93
+ if args.include?("$output")
94
+ out, a = a, back # read input from backup, write to original
95
+ end
96
+ replace = {"local" => a, "base" => b, "other" => c, "output" => out}
97
+ args.gsub!(/\$(local|base|other|output)/) { replace[$1]}
98
+ # shelling out
99
+ ret = Amp::Support::system(tool_path+" "+args, :chdir => repo.root, :environ => environment)
100
+ end
101
+ ret = (ret == true ? 1 : (ret == false ? 0 : ret))
102
+ if ret == 0 && tool_setting(tool, "checkconflicts")
103
+ if vf_local.data =~ /^(<<<<<<< .*|=======|>>>>>>> .*)$/
104
+ ret = 1
105
+ end
106
+ end
107
+
108
+ if ret == 0 && tool_setting(tool, "checkchanged")
109
+ if File.stat(repo.working_join(path)) === File.stat(back)
110
+ if UI::yes_or_no "output file #{path} appears unchanged\nwas merge successful?"
111
+ r = 1
112
+ end
113
+ end
114
+ end
115
+
116
+ fix_end_of_lines(repo.working_join(path), back) if tool_setting(tool, "fixeol")
117
+
118
+ if ret == 1
119
+ UI::warn "merging #{path} failed!"
120
+ else
121
+ File.unlink back
122
+ end
123
+ File.unlink b
124
+ File.unlink c
125
+
126
+ !ret.zero? # return
127
+ end
128
+
129
+ private
130
+
131
+ def save_versioned_file_temp(prefix, versioned_file)
132
+ prefix = "#{File.basename versioned_file.path}~#{prefix}"
133
+
134
+ tempfile = Tempfile.new prefix
135
+ path = tempfile.path
136
+ tempfile.write versioned_file.data
137
+ tempfile.close false # DON'T unlink it
138
+
139
+ tempfile
140
+ end
141
+
142
+ ##
143
+ # Converts the end-of-line characters in a file to match the original file.
144
+ # Thus, if we merge from our copy to a new one, and there foreign
145
+ # end-of-line characters got merged in, we want to nuke them and put in our own!
146
+ #
147
+ # @param [String] new_file the path to the newly merged file
148
+ # @param [String] original_file the path to the original file
149
+ def fix_end_of_lines(new_file, original_file)
150
+ new_eol = guess_end_of_line(File.read(original_file))
151
+ if new_eol
152
+ data = File.read(new_file)
153
+ old_eol = guess_end_of_line(data)
154
+ if old_eol
155
+ new_data = data.gsub(old_eol, new_eol)
156
+
157
+ File.open(file, "w") {|f| f.write new_data } if new_data != data
158
+ end
159
+ end
160
+ end
161
+
162
+
163
+ ##
164
+ # Guesses the end-of-line character for a file in a very lazy fashion.
165
+ #
166
+ # @param [String] data the file data to guess from
167
+ # @return [String, nil] the guessed end-of-line character(s).
168
+ def guess_end_of_line(data)
169
+ return nil if data.include?("\0") # binary
170
+ return "\r\n" if data.include?("\r\n") # windows
171
+ return "\r" if data.include?("\r") # old mac
172
+ return "\n" if data.include?("\n") # *nix
173
+ return nil # wtf?
174
+ end
175
+
176
+ ##
177
+ # Picks a merge tool based on the user's settings in hgrc files and environment
178
+ # variables. Returns a hash specifying both the name and path of the
179
+ # merge tool's executable.
180
+ #
181
+ # @todo merge-patterns, line 56 of filemerge.py
182
+ # @param [Repository] repo the repository we are performing a merge upon
183
+ # @param [String] path the path to the file we're merging
184
+ # @param [Boolean] binary is the file a binary file?
185
+ # @param [Boolean] symlink is the file a symlink?
186
+ # @return [Hash] keyed as follows:
187
+ # :name => the name of the chosen tool
188
+ # :path => the path to the tool (if an executable is to be used)
189
+ def pick_tool(repo, path, binary, symlink)
190
+ hgmerge = ENV["HGMERGE"]
191
+ return [hgmerge, hgmerge] if hgmerge
192
+
193
+ # @todo: add merge-patterns support
194
+
195
+ # scan the merge-tools section
196
+ tools = {}
197
+ config["merge-tools"].each do |k, v|
198
+ t = k.split(".").first
199
+ unless tools[t]
200
+ tools[t] = tool_setting_string(t, "priority", "0").to_i
201
+ end
202
+ end
203
+
204
+ # go through the list of tools and sort by priority
205
+ tool_names = tools.keys
206
+ tools = tools.map {|tool, prio| [-prio, tool]}.sort
207
+ # check the [ui] section for a "merge" setting
208
+ uimerge = config["ui","merge"]
209
+ if uimerge
210
+ unless tool_names.include?(uimerge)
211
+ return [uimerge, uimerge]
212
+ end
213
+ tools.unshift([nil, uimerge]) # highest priority
214
+ end
215
+
216
+ # add the "hgmerge" binary
217
+ tools << [nil, "hgmerge"] # the old default, if found
218
+ # check everything in our list, and if we actually find one that works,
219
+ # return it
220
+ tools.each do |priority, tool|
221
+ if check_tool(tool, nil, symlink, binary)
222
+ tool_path = find_tool(tool)
223
+ return [tool, "\"#{tool_path}\""]
224
+ end
225
+ end
226
+ # last but not least, do a simple_merge.
227
+ return (!symlink && !binary) ? "internal:merge" : [nil, nil]
228
+ end
229
+
230
+ ##
231
+ # Quick access to the merge-tools section of the configuration files.
232
+ # A merge tool will set it up with data like this:
233
+ # [merge-tools]
234
+ # awesometool.executable = /usr/bin/awesometool
235
+ # awesometool.regkey = HKEY_USELESS_INFO
236
+ # and so on. This method abstracts away the scheme for encoding this information
237
+ # gets string values from the configuration.
238
+ #
239
+ # @param [String] tool the name of the tool to look up data for
240
+ # @param [String] part the specific information about the tool to look up
241
+ # @param [String] default the default value, if the configuration setting
242
+ # can't be found
243
+ # @return [String] the setting for the given merge tool we're looking up, as
244
+ # as a string.
245
+ def tool_setting_string(tool, part, default="")
246
+ config["merge-tools", "#{tool}.#{part}", String, default]
247
+ end
248
+
249
+ ##
250
+ # Quick access to the merge-tools section of the configuration files.
251
+ # Returns boolean values.
252
+ #
253
+ # @see check_tool_string
254
+ # @param [String] tool the name of the tool to look up data for
255
+ # @param [String] part the specific information about the tool to look up
256
+ # @param [Boolean] default the default value, if the configuration setting
257
+ # can't be found
258
+ # @return [Boolean] the setting for the given merge tool we're looking up, as
259
+ # as a string.
260
+ def tool_setting(tool, part, default=false)
261
+ config["merge-tools", "#{tool}.#{part}", Boolean, default]
262
+ end
263
+
264
+ ##
265
+ # Given the name of a merge tool, attempt to locate an executable file
266
+ # for the tool.
267
+ #
268
+ # @param [String] tool the name of the merge tool to locate
269
+ # @return [String] the path to the executable for the merge tool, or nil
270
+ # if the tool cannot be found
271
+ def find_tool(tool)
272
+ if ["internal:fail", "internal:local", "internal:other"].include?(tool)
273
+ return tool
274
+ end
275
+ # windows stuff
276
+ k = tool_setting_string(tool, "regkey")
277
+ if k && k.any?
278
+ p = File.amp_lookup_reg(k, tool_setting_string(tool, "regname"))
279
+ if p && p.any?
280
+ p = File.amp_find_executable(p + check_tool_string(tool, "regappend"))
281
+ if p
282
+ return p
283
+ end
284
+ end
285
+ end
286
+ # normal *nix lookup
287
+ return File.amp_find_executable(tool_setting_string(tool, "executable", tool))
288
+ end
289
+
290
+ ##
291
+ # Checks to see if a given tool is available given the necessary settings.
292
+ #
293
+ # @todo add GUI check
294
+ # @param [String] tool the name of the tool we want to check
295
+ # @param [String] pat the pattern we matched to get here. Could be nil.
296
+ # @param [Boolean] symlink are we merging across a symlink here?
297
+ # @param [Boolean] binary are we merging a binary file? you crazy?!
298
+ # @return [Boolean] is the given tool available?
299
+ def check_tool(tool, pat, symlink, binary)
300
+ tool_msg = tool
301
+ tool_msg += " specified for " + pat if pat
302
+
303
+ if !(find_tool tool)
304
+ if pat
305
+ warn("couldn't find merge tool #{tool}")
306
+ else
307
+ note("couldn't find merge tool #{tool}")
308
+ end
309
+ elsif symlink && !(tool_setting(tool, "symlink"))
310
+ warn("tool #{tool} can't handle symlinks")
311
+ elsif binary && !(tool_setting(tool, "binary"))
312
+ warn("tool #{tool} can't handle binary files")
313
+ elsif false # TODO: add GUI check
314
+ else
315
+ return true
316
+ end
317
+ return false # we're here if any of the previous checks created a warning
318
+ end
319
+
320
+ end
321
+ end
322
+ end