amp 0.5.2 → 0.5.3

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 (232) hide show
  1. data/.gitignore +12 -0
  2. data/.hgignore +3 -0
  3. data/AUTHORS +1 -1
  4. data/Manifest.txt +99 -38
  5. data/README.md +3 -3
  6. data/Rakefile +53 -18
  7. data/SCHEDULE.markdown +5 -1
  8. data/TODO.markdown +120 -149
  9. data/ampfile.rb +3 -1
  10. data/bin/amp +4 -1
  11. data/ext/amp/bz2/extconf.rb +1 -1
  12. data/ext/amp/mercurial_patch/extconf.rb +1 -1
  13. data/ext/amp/mercurial_patch/mpatch.c +4 -3
  14. data/ext/amp/priority_queue/extconf.rb +1 -1
  15. data/ext/amp/support/extconf.rb +1 -1
  16. data/ext/amp/support/support.c +1 -1
  17. data/lib/amp.rb +125 -67
  18. data/lib/amp/commands/command.rb +12 -10
  19. data/lib/amp/commands/command_support.rb +8 -1
  20. data/lib/amp/commands/commands/help.rb +2 -20
  21. data/lib/amp/commands/commands/init.rb +14 -2
  22. data/lib/amp/commands/commands/templates.rb +6 -4
  23. data/lib/amp/commands/commands/version.rb +15 -1
  24. data/lib/amp/commands/commands/workflow.rb +3 -3
  25. data/lib/amp/commands/commands/workflows/git/add.rb +3 -3
  26. data/lib/amp/commands/commands/workflows/git/copy.rb +1 -1
  27. data/lib/amp/commands/commands/workflows/git/rm.rb +4 -2
  28. data/lib/amp/commands/commands/workflows/hg/add.rb +1 -1
  29. data/lib/amp/commands/commands/workflows/hg/addremove.rb +2 -2
  30. data/lib/amp/commands/commands/workflows/hg/annotate.rb +8 -2
  31. data/lib/amp/commands/commands/workflows/hg/bisect.rb +253 -0
  32. data/lib/amp/commands/commands/workflows/hg/branch.rb +1 -1
  33. data/lib/amp/commands/commands/workflows/hg/branches.rb +3 -3
  34. data/lib/amp/commands/commands/workflows/hg/bundle.rb +3 -3
  35. data/lib/amp/commands/commands/workflows/hg/clone.rb +4 -5
  36. data/lib/amp/commands/commands/workflows/hg/commit.rb +37 -1
  37. data/lib/amp/commands/commands/workflows/hg/copy.rb +2 -1
  38. data/lib/amp/commands/commands/workflows/hg/debug/index.rb +1 -1
  39. data/lib/amp/commands/commands/workflows/hg/diff.rb +3 -8
  40. data/lib/amp/commands/commands/workflows/hg/forget.rb +5 -4
  41. data/lib/amp/commands/commands/workflows/hg/identify.rb +6 -6
  42. data/lib/amp/commands/commands/workflows/hg/import.rb +1 -1
  43. data/lib/amp/commands/commands/workflows/hg/incoming.rb +2 -2
  44. data/lib/amp/commands/commands/workflows/hg/log.rb +5 -4
  45. data/lib/amp/commands/commands/workflows/hg/merge.rb +1 -1
  46. data/lib/amp/commands/commands/workflows/hg/move.rb +5 -3
  47. data/lib/amp/commands/commands/workflows/hg/outgoing.rb +1 -1
  48. data/lib/amp/commands/commands/workflows/hg/push.rb +6 -7
  49. data/lib/amp/commands/commands/workflows/hg/remove.rb +2 -2
  50. data/lib/amp/commands/commands/workflows/hg/resolve.rb +6 -23
  51. data/lib/amp/commands/commands/workflows/hg/root.rb +1 -2
  52. data/lib/amp/commands/commands/workflows/hg/status.rb +21 -12
  53. data/lib/amp/commands/commands/workflows/hg/tag.rb +2 -2
  54. data/lib/amp/commands/commands/workflows/hg/untrack.rb +12 -0
  55. data/lib/amp/commands/commands/workflows/hg/verify.rb +13 -3
  56. data/lib/amp/commands/commands/workflows/hg/what_changed.rb +18 -0
  57. data/lib/amp/commands/dispatch.rb +12 -13
  58. data/lib/amp/dependencies/amp_support.rb +1 -1
  59. data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +1 -0
  60. data/lib/amp/dependencies/maruku.rb +136 -0
  61. data/lib/amp/dependencies/maruku/attributes.rb +227 -0
  62. data/lib/amp/dependencies/maruku/defaults.rb +71 -0
  63. data/lib/amp/dependencies/maruku/errors_management.rb +92 -0
  64. data/lib/amp/dependencies/maruku/helpers.rb +260 -0
  65. data/lib/amp/dependencies/maruku/input/charsource.rb +326 -0
  66. data/lib/amp/dependencies/maruku/input/extensions.rb +69 -0
  67. data/lib/amp/dependencies/maruku/input/html_helper.rb +189 -0
  68. data/lib/amp/dependencies/maruku/input/linesource.rb +111 -0
  69. data/lib/amp/dependencies/maruku/input/parse_block.rb +615 -0
  70. data/lib/amp/dependencies/maruku/input/parse_doc.rb +234 -0
  71. data/lib/amp/dependencies/maruku/input/parse_span_better.rb +746 -0
  72. data/lib/amp/dependencies/maruku/input/rubypants.rb +225 -0
  73. data/lib/amp/dependencies/maruku/input/type_detection.rb +147 -0
  74. data/lib/amp/dependencies/maruku/input_textile2/t2_parser.rb +163 -0
  75. data/lib/amp/dependencies/maruku/maruku.rb +33 -0
  76. data/lib/amp/dependencies/maruku/output/to_ansi.rb +223 -0
  77. data/lib/amp/dependencies/maruku/output/to_html.rb +991 -0
  78. data/lib/amp/dependencies/maruku/output/to_markdown.rb +164 -0
  79. data/lib/amp/dependencies/maruku/output/to_s.rb +56 -0
  80. data/lib/amp/dependencies/maruku/string_utils.rb +191 -0
  81. data/lib/amp/dependencies/maruku/structures.rb +167 -0
  82. data/lib/amp/dependencies/maruku/structures_inspect.rb +87 -0
  83. data/lib/amp/dependencies/maruku/structures_iterators.rb +61 -0
  84. data/lib/amp/dependencies/maruku/textile2.rb +1 -0
  85. data/lib/amp/dependencies/maruku/toc.rb +199 -0
  86. data/lib/amp/dependencies/maruku/usage/example1.rb +33 -0
  87. data/lib/amp/dependencies/maruku/version.rb +40 -0
  88. data/lib/amp/dependencies/priority_queue.rb +2 -1
  89. data/lib/amp/dependencies/python_config.rb +2 -1
  90. data/lib/amp/graphs/ancestor.rb +2 -1
  91. data/lib/amp/graphs/copies.rb +236 -233
  92. data/lib/amp/help/entries/__default__.erb +31 -0
  93. data/lib/amp/help/entries/commands.erb +6 -0
  94. data/lib/amp/help/entries/mdtest.md +35 -0
  95. data/lib/amp/help/entries/silly +3 -0
  96. data/lib/amp/help/help.rb +288 -0
  97. data/lib/amp/profiling_hacks.rb +5 -3
  98. data/lib/amp/repository/abstract/abstract_changeset.rb +97 -0
  99. data/lib/amp/repository/abstract/abstract_local_repo.rb +181 -0
  100. data/lib/amp/repository/abstract/abstract_staging_area.rb +180 -0
  101. data/lib/amp/repository/abstract/abstract_versioned_file.rb +100 -0
  102. data/lib/amp/repository/abstract/common_methods/changeset.rb +75 -0
  103. data/lib/amp/repository/abstract/common_methods/local_repo.rb +277 -0
  104. data/lib/amp/repository/abstract/common_methods/staging_area.rb +233 -0
  105. data/lib/amp/repository/abstract/common_methods/versioned_file.rb +71 -0
  106. data/lib/amp/repository/generic_repo_picker.rb +78 -0
  107. data/lib/amp/repository/git/repo_format/changeset.rb +336 -0
  108. data/lib/amp/repository/git/repo_format/staging_area.rb +192 -0
  109. data/lib/amp/repository/git/repo_format/versioned_file.rb +119 -0
  110. data/lib/amp/repository/git/repositories/local_repository.rb +164 -0
  111. data/lib/amp/repository/git/repository.rb +41 -0
  112. data/lib/amp/repository/mercurial/encoding/mercurial_diff.rb +382 -0
  113. data/lib/amp/repository/mercurial/encoding/mercurial_patch.rb +1 -0
  114. data/lib/amp/repository/mercurial/encoding/patch.rb +294 -0
  115. data/lib/amp/repository/mercurial/encoding/pure_ruby/ruby_mercurial_patch.rb +124 -0
  116. data/lib/amp/repository/mercurial/merging/merge_ui.rb +327 -0
  117. data/lib/amp/repository/mercurial/merging/simple_merge.rb +452 -0
  118. data/lib/amp/repository/mercurial/repo_format/branch_manager.rb +266 -0
  119. data/lib/amp/repository/mercurial/repo_format/changeset.rb +768 -0
  120. data/lib/amp/repository/mercurial/repo_format/dir_state.rb +716 -0
  121. data/lib/amp/repository/mercurial/repo_format/journal.rb +218 -0
  122. data/lib/amp/repository/mercurial/repo_format/lock.rb +210 -0
  123. data/lib/amp/repository/mercurial/repo_format/merge_state.rb +228 -0
  124. data/lib/amp/repository/mercurial/repo_format/staging_area.rb +367 -0
  125. data/lib/amp/repository/mercurial/repo_format/store.rb +487 -0
  126. data/lib/amp/repository/mercurial/repo_format/tag_manager.rb +322 -0
  127. data/lib/amp/repository/mercurial/repo_format/updatable.rb +543 -0
  128. data/lib/amp/repository/mercurial/repo_format/updater.rb +848 -0
  129. data/lib/amp/repository/mercurial/repo_format/verification.rb +433 -0
  130. data/lib/amp/repository/mercurial/repositories/bundle_repository.rb +216 -0
  131. data/lib/amp/repository/mercurial/repositories/http_repository.rb +386 -0
  132. data/lib/amp/repository/mercurial/repositories/local_repository.rb +2034 -0
  133. data/lib/amp/repository/mercurial/repository.rb +119 -0
  134. data/lib/amp/repository/mercurial/revlogs/bundle_revlogs.rb +249 -0
  135. data/lib/amp/repository/mercurial/revlogs/changegroup.rb +217 -0
  136. data/lib/amp/repository/mercurial/revlogs/changelog.rb +339 -0
  137. data/lib/amp/repository/mercurial/revlogs/file_log.rb +152 -0
  138. data/lib/amp/repository/mercurial/revlogs/index.rb +500 -0
  139. data/lib/amp/repository/mercurial/revlogs/manifest.rb +201 -0
  140. data/lib/amp/repository/mercurial/revlogs/node.rb +20 -0
  141. data/lib/amp/repository/mercurial/revlogs/revlog.rb +1026 -0
  142. data/lib/amp/repository/mercurial/revlogs/revlog_support.rb +129 -0
  143. data/lib/amp/repository/mercurial/revlogs/versioned_file.rb +597 -0
  144. data/lib/amp/repository/repository.rb +11 -88
  145. data/lib/amp/server/extension/amp_extension.rb +3 -3
  146. data/lib/amp/server/fancy_http_server.rb +1 -1
  147. data/lib/amp/server/fancy_views/_browser.haml +1 -1
  148. data/lib/amp/server/fancy_views/_diff_file.haml +1 -8
  149. data/lib/amp/server/fancy_views/changeset.haml +2 -2
  150. data/lib/amp/server/fancy_views/file.haml +1 -1
  151. data/lib/amp/server/fancy_views/file_diff.haml +1 -1
  152. data/lib/amp/support/amp_ui.rb +13 -29
  153. data/lib/amp/support/generator.rb +1 -1
  154. data/lib/amp/support/loaders.rb +1 -2
  155. data/lib/amp/support/logger.rb +10 -16
  156. data/lib/amp/support/match.rb +18 -4
  157. data/lib/amp/support/mercurial/ignore.rb +151 -0
  158. data/lib/amp/support/openers.rb +8 -3
  159. data/lib/amp/support/support.rb +91 -46
  160. data/lib/amp/templates/{blank.commit.erb → mercurial/blank.commit.erb} +0 -0
  161. data/lib/amp/templates/{blank.log.erb → mercurial/blank.log.erb} +0 -0
  162. data/lib/amp/templates/{default.commit.erb → mercurial/default.commit.erb} +0 -0
  163. data/lib/amp/templates/{default.log.erb → mercurial/default.log.erb} +0 -0
  164. data/lib/amp/templates/template.rb +18 -18
  165. data/man/amp.1 +51 -0
  166. data/site/src/about/commands.haml +1 -1
  167. data/site/src/css/amp.css +1 -1
  168. data/site/src/index.haml +3 -3
  169. data/tasks/man.rake +39 -0
  170. data/tasks/stats.rake +1 -10
  171. data/tasks/yard.rake +1 -50
  172. data/test/dirstate_tests/test_dir_state.rb +10 -8
  173. data/test/functional_tests/annotate.out +31 -0
  174. data/test/functional_tests/test_functional.rb +155 -63
  175. data/test/localrepo_tests/ampfile.rb +12 -0
  176. data/test/localrepo_tests/test_local_repo.rb +56 -57
  177. data/test/manifest_tests/test_manifest.rb +3 -5
  178. data/test/merge_tests/test_merge.rb +3 -3
  179. data/test/revlog_tests/test_revlog.rb +14 -6
  180. data/test/store_tests/test_fncache_store.rb +19 -19
  181. data/test/test_19_compatibility.rb +46 -0
  182. data/test/test_base85.rb +2 -2
  183. data/test/test_bdiff.rb +2 -2
  184. data/test/test_changegroup.rb +59 -0
  185. data/test/test_commands.rb +2 -2
  186. data/test/test_difflib.rb +2 -2
  187. data/test/test_generator.rb +34 -0
  188. data/test/test_ignore.rb +203 -0
  189. data/test/test_journal.rb +18 -13
  190. data/test/test_match.rb +2 -2
  191. data/test/test_mdiff.rb +3 -3
  192. data/test/test_mpatch.rb +3 -3
  193. data/test/test_multi_io.rb +40 -0
  194. data/test/test_support.rb +18 -2
  195. data/test/test_templates.rb +38 -0
  196. data/test/test_ui.rb +79 -0
  197. data/test/testutilities.rb +56 -0
  198. metadata +168 -49
  199. data/ext/amp/bz2/mkmf.log +0 -38
  200. data/lib/amp/encoding/mercurial_diff.rb +0 -378
  201. data/lib/amp/encoding/mercurial_patch.rb +0 -1
  202. data/lib/amp/encoding/patch.rb +0 -292
  203. data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +0 -123
  204. data/lib/amp/merges/merge_state.rb +0 -164
  205. data/lib/amp/merges/merge_ui.rb +0 -322
  206. data/lib/amp/merges/simple_merge.rb +0 -450
  207. data/lib/amp/repository/branch_manager.rb +0 -234
  208. data/lib/amp/repository/dir_state.rb +0 -950
  209. data/lib/amp/repository/journal.rb +0 -203
  210. data/lib/amp/repository/lock.rb +0 -207
  211. data/lib/amp/repository/repositories/bundle_repository.rb +0 -214
  212. data/lib/amp/repository/repositories/http_repository.rb +0 -377
  213. data/lib/amp/repository/repositories/local_repository.rb +0 -2661
  214. data/lib/amp/repository/store.rb +0 -485
  215. data/lib/amp/repository/tag_manager.rb +0 -319
  216. data/lib/amp/repository/updatable.rb +0 -532
  217. data/lib/amp/repository/verification.rb +0 -431
  218. data/lib/amp/repository/versioned_file.rb +0 -475
  219. data/lib/amp/revlogs/bundle_revlogs.rb +0 -246
  220. data/lib/amp/revlogs/changegroup.rb +0 -217
  221. data/lib/amp/revlogs/changelog.rb +0 -338
  222. data/lib/amp/revlogs/changeset.rb +0 -521
  223. data/lib/amp/revlogs/file_log.rb +0 -165
  224. data/lib/amp/revlogs/index.rb +0 -493
  225. data/lib/amp/revlogs/manifest.rb +0 -195
  226. data/lib/amp/revlogs/node.rb +0 -18
  227. data/lib/amp/revlogs/revlog.rb +0 -1045
  228. data/lib/amp/revlogs/revlog_support.rb +0 -126
  229. data/lib/amp/support/ignore.rb +0 -144
  230. data/site/Rakefile +0 -38
  231. data/test/test_amp.rb +0 -9
  232. data/test/test_helper.rb +0 -15
@@ -0,0 +1,124 @@
1
+ module Amp
2
+ module Diffs
3
+ module Mercurial
4
+
5
+ ##
6
+ # This handles applying patches in mercurial. yay!!!!
7
+ module MercurialPatch
8
+
9
+ ##
10
+ # This attempts to apply a series of patches in time proportional to
11
+ # the total size of the patches, rather than patches * len(text). This
12
+ # means rather than shuffling strings around, we shuffle around
13
+ # pointers to fragments with fragment lists.
14
+ #
15
+ # When the fragment lists get too long, we collapse them. To do this
16
+ # efficiently, we do all our operations inside a buffer created by
17
+ # mmap and simply use memmove. This avoids creating a bunch of large
18
+ # temporary string buffers.
19
+ #
20
+ # UPDATE 2AM BEFORE I GO BACK TO SCHOOL
21
+ # I FUCKING HATE PYTHON
22
+ def self.apply_patches(source, patches)
23
+ return source if patches.empty?
24
+ patch_lens = patches.map {|patch| patch.size}
25
+ pl = patch_lens.sum
26
+ bl = source.size + pl
27
+ tl = bl + bl + pl
28
+ b1, b2 = 0, bl
29
+
30
+ return a if tl == 0 #empty patches. lame.
31
+
32
+ output = StringIO.new "",(ruby_19? ? "r+:ASCII-8BIT" : "r+")
33
+ output.write source
34
+
35
+ frags = [[source.size, b1]]
36
+
37
+ pos = b2 + bl
38
+ output.seek pos
39
+ patches.each {|patch| output.write(patch)}
40
+ patch_lens.each do |plen|
41
+ if frags.size > 128
42
+ b2, b1 = b1, b2
43
+ frags = [self.collect(output,b1,frags)]
44
+ end
45
+ newarr = []
46
+ endpt = pos + plen
47
+ last = 0
48
+ while pos < endpt
49
+ output.seek pos
50
+ p1, p2, l = output.read(12).unpack("NNN")
51
+ self.pull(newarr, frags, p1 - last)
52
+ self.pull([], frags, p2 - p1)
53
+ newarr << [l, pos + 12]
54
+ pos += l + 12
55
+ last = p2
56
+ end
57
+ frags = newarr + frags
58
+ end
59
+
60
+ t = self.collect output, b2, frags
61
+ output.seek t[1]
62
+ output.read t[0]
63
+ end
64
+
65
+
66
+ def self.patched_size(orig, delta)
67
+ outlen, last, bin = 0, 0, 0
68
+ binend = delta.size
69
+ data = 12 # size of the delta instruction values (3 longs)
70
+ while data <= binend
71
+ decode = delta[bin..(bin+11)]
72
+ start, endpt, length = decode.unpack("NNN")
73
+ break if start > endpt
74
+
75
+ bin = data + length
76
+ data = bin + 12
77
+ outlen += start - last
78
+ last = endpt
79
+ outlen += length
80
+ end
81
+
82
+ raise "patch cannot be decoded" if bin != binend
83
+
84
+ outlen += orig - last
85
+ outlen
86
+ end
87
+
88
+ def self.copy_block(io, destination, source, count)
89
+ io.seek(source)
90
+ buf = io.read(count)
91
+ io.seek(destination)
92
+ io.write(buf)
93
+ end
94
+
95
+ ##
96
+ #
97
+ def self.pull(dst, src, l)
98
+ until l == 0
99
+ f = src.shift
100
+ if f[0] > l
101
+ src.unshift [f[0] - l, f[1] + l]
102
+ dst << [l, f[1]]
103
+ return
104
+ end
105
+ dst << f
106
+ l -= f[0]
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Takes the fragments we've accumulated and applies them all to the IO.
112
+ def self.collect(io, buf, list)
113
+ start = buf
114
+ list.each do |l, p|
115
+ self.copy_block(io, buf, p, l)
116
+ buf += l
117
+ end
118
+ [buf - start, start]
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,327 @@
1
+ require 'tempfile'
2
+
3
+ module Amp
4
+ module Merges
5
+ module Mercurial
6
+
7
+ ##
8
+ # This module handles figuring out how to merge files using the user's
9
+ # preferences. It is mixed into the UI class. The UI class must implement
10
+ # the "config" method.
11
+ module MergeUI
12
+ extend self
13
+
14
+ ##
15
+ # Performs a 3-way merge in the working directory from vf_local to vf_other,
16
+ # using the common ancestor vf_ancestor.
17
+ #
18
+ # @todo change 1s and 0s to bools
19
+ # @todo consistent return type
20
+ #
21
+ # @param [Repository] repo the repository in which we are merging files
22
+ # @param [String] parent_node the node_id of the parent node before the merge
23
+ # @param [String] original_fn the original local filename before the merge
24
+ # @param [WorkingVersionedFile] vf_local the current, working-directory versioned file
25
+ # @param [VersionedFile] vf_other the file's changeset to which we are migrating
26
+ # @param [VersionedFile] vf_ancestor the common ancestor between vf_local and vf_other
27
+ # @return [Boolean] true if there were conflicts during the merge
28
+ def file_merge(repo, parent_node, original_fn, vf_local, vf_other, vf_ancestor)
29
+ is_binary = proc {|ctx| ctx.data.binary? rescue false}
30
+
31
+ return nil if !(vf_other.cmp vf_local.data)
32
+
33
+ path = vf_local.path
34
+ binary = is_binary[vf_local] || is_binary[vf_other] || is_binary[vf_ancestor]
35
+ symlink = (vf_local.flags + vf_other.flags).include? "l"
36
+
37
+ tool, tool_path = pick_tool(repo, path, binary, symlink)
38
+ UI.status("Picked tool #{tool} for #{path} (binary #{binary} symlink #{symlink})")
39
+
40
+ unless tool
41
+ tool = "internal:local"
42
+ if UI.ask("no tool found to merge #{path}\n"+
43
+ "keep (l)ocal or take (o)ther?") != "l"
44
+ tool = "internal:other"
45
+ end
46
+ end
47
+
48
+ case tool
49
+ when "internal:local"
50
+ return 0
51
+ when "internal:other"
52
+ repo.working_write(path, vf_other.data, vf_other.flags)
53
+ return 0
54
+ when "internal:fail"
55
+ return 1
56
+ end
57
+
58
+ a = repo.working_join(path)
59
+ b_file = save_versioned_file_temp("base", vf_ancestor)
60
+ c_file = save_versioned_file_temp("other", vf_other)
61
+ b, c = b_file.path, c_file.path
62
+
63
+ out = ""
64
+ back = a + ".orig" + File.extname(a)
65
+ File.copy(a, back)
66
+
67
+ if original_fn != vf_other.path
68
+ UI.status("merging #{original_fn} and #{vf_other.path} to #{path}")
69
+ else
70
+ UI.status("merging #{path}")
71
+ end
72
+
73
+ if tool_setting(tool, "premerge", !(binary || symlink))
74
+ ret = ThreeWayMerger.three_way_merge(a, b, c, :quiet => true)
75
+ unless ret
76
+ UI.debug("premerge successful")
77
+ File.unlink(back)
78
+ File.safe_unlink(b)
79
+ File.safe_unlink(c)
80
+ return false
81
+ end
82
+ File.copy(back, a) # restore frmo backup and try again
83
+ end
84
+
85
+ environment = {"HG_FILE" => path,
86
+ "HG_MY_NODE" => parent_node.hexlify[0..11],
87
+ "HG_OTHER_NODE" => vf_other.changeset.to_s,
88
+ "HG_MY_ISLINK" => vf_local.flags.include?("l"),
89
+ "HG_OTHER_ISLINK" => vf_other.flags.include?("l"),
90
+ "HG_BASE_ISLINK" => vf_ancestor.flags.include?("l")}
91
+ if tool == "internal:merge"
92
+ ret = ThreeWayMerger.three_way_merge(a, b, c, :label => ['local', 'other'])
93
+ else
94
+ args = tool_setting_string(tool, "args", "$local $base $other")
95
+ if args.include?("$output")
96
+ out, a = a, back # read input from backup, write to original
97
+ end
98
+ replace = {"local" => a, "base" => b, "other" => c, "output" => out}
99
+ args.gsub!(/\$(local|base|other|output)/) { replace[$1]}
100
+ # shelling out
101
+ ret = Amp::Support::system(tool_path+" "+args, :chdir => repo.root, :environ => environment)
102
+ end
103
+ ret = (ret == true ? 1 : (ret == false ? 0 : ret))
104
+ if ret == 0 && tool_setting(tool, "checkconflicts")
105
+ if vf_local.data =~ /^(<<<<<<< .*|=======|>>>>>>> .*)$/
106
+ ret = 1
107
+ end
108
+ end
109
+
110
+ if ret == 0 && tool_setting(tool, "checkchanged")
111
+ if File.stat(repo.working_join(path)) === File.stat(back)
112
+ if UI::yes_or_no "output file #{path} appears unchanged\nwas merge successful?"
113
+ r = 1
114
+ end
115
+ end
116
+ end
117
+
118
+ fix_end_of_lines(repo.working_join(path), back) if tool_setting(tool, "fixeol")
119
+
120
+ if ret == 1
121
+ UI::warn "merging #{path} failed!"
122
+ else
123
+ File.unlink back
124
+ end
125
+ File.unlink b
126
+ File.unlink c
127
+
128
+ !ret.zero? # return
129
+ end
130
+
131
+ private
132
+
133
+ def save_versioned_file_temp(prefix, versioned_file)
134
+ prefix = "#{File.basename versioned_file.path}~#{prefix}"
135
+
136
+ tempfile = Tempfile.new prefix
137
+ path = tempfile.path
138
+ tempfile.write versioned_file.data
139
+ tempfile.close false # DON'T unlink it
140
+
141
+ tempfile
142
+ end
143
+
144
+ ##
145
+ # Converts the end-of-line characters in a file to match the original file.
146
+ # Thus, if we merge from our copy to a new one, and there foreign
147
+ # end-of-line characters got merged in, we want to nuke them and put in our own!
148
+ #
149
+ # @param [String] new_file the path to the newly merged file
150
+ # @param [String] original_file the path to the original file
151
+ def fix_end_of_lines(new_file, original_file)
152
+ new_eol = guess_end_of_line(File.read(original_file))
153
+ if new_eol
154
+ data = File.read(new_file)
155
+ old_eol = guess_end_of_line(data)
156
+ if old_eol
157
+ new_data = data.gsub(old_eol, new_eol)
158
+
159
+ File.open(file, "w") {|f| f.write new_data } if new_data != data
160
+ end
161
+ end
162
+ end
163
+
164
+
165
+ ##
166
+ # Guesses the end-of-line character for a file in a very lazy fashion.
167
+ #
168
+ # @param [String] data the file data to guess from
169
+ # @return [String, nil] the guessed end-of-line character(s).
170
+ def guess_end_of_line(data)
171
+ return nil if data.include?("\0") # binary
172
+ return "\r\n" if data.include?("\r\n") # windows
173
+ return "\r" if data.include?("\r") # old mac
174
+ return "\n" if data.include?("\n") # *nix
175
+ return nil # wtf?
176
+ end
177
+
178
+ def config; UI.config; end
179
+
180
+ ##
181
+ # Picks a merge tool based on the user's settings in hgrc files and environment
182
+ # variables. Returns a hash specifying both the name and path of the
183
+ # merge tool's executable.
184
+ #
185
+ # @todo merge-patterns, line 56 of filemerge.py
186
+ # @param [Repository] repo the repository we are performing a merge upon
187
+ # @param [String] path the path to the file we're merging
188
+ # @param [Boolean] binary is the file a binary file?
189
+ # @param [Boolean] symlink is the file a symlink?
190
+ # @return [Hash] keyed as follows:
191
+ # :name => the name of the chosen tool
192
+ # :path => the path to the tool (if an executable is to be used)
193
+ def pick_tool(repo, path, binary, symlink)
194
+ hgmerge = ENV["HGMERGE"]
195
+ return [hgmerge, hgmerge] if hgmerge
196
+
197
+ # @todo: add merge-patterns support
198
+
199
+ # scan the merge-tools section
200
+ tools = {}
201
+ config["merge-tools"].each do |k, v|
202
+ t = k.split(".").first
203
+ unless tools[t]
204
+ tools[t] = tool_setting_string(t, "priority", "0").to_i
205
+ end
206
+ end
207
+
208
+ # go through the list of tools and sort by priority
209
+ tool_names = tools.keys
210
+ tools = tools.map {|tool, prio| [-prio, tool]}.sort
211
+ # check the [ui] section for a "merge" setting
212
+ uimerge = config["ui","merge"]
213
+ if uimerge
214
+ unless tool_names.include?(uimerge)
215
+ return [uimerge, uimerge]
216
+ end
217
+ tools.unshift([nil, uimerge]) # highest priority
218
+ end
219
+
220
+ # add the "hgmerge" binary
221
+ tools << [nil, "hgmerge"] # the old default, if found
222
+ # check everything in our list, and if we actually find one that works,
223
+ # return it
224
+ tools.each do |priority, tool|
225
+ if check_tool(tool, nil, symlink, binary)
226
+ tool_path = find_tool(tool)
227
+ return [tool, "\"#{tool_path}\""]
228
+ end
229
+ end
230
+ # last but not least, do a simple_merge.
231
+ return (!symlink && !binary) ? "internal:merge" : [nil, nil]
232
+ end
233
+
234
+ ##
235
+ # Quick access to the merge-tools section of the configuration files.
236
+ # A merge tool will set it up with data like this:
237
+ # [merge-tools]
238
+ # awesometool.executable = /usr/bin/awesometool
239
+ # awesometool.regkey = HKEY_USELESS_INFO
240
+ # and so on. This method abstracts away the scheme for encoding this information
241
+ # gets string values from the configuration.
242
+ #
243
+ # @param [String] tool the name of the tool to look up data for
244
+ # @param [String] part the specific information about the tool to look up
245
+ # @param [String] default the default value, if the configuration setting
246
+ # can't be found
247
+ # @return [String] the setting for the given merge tool we're looking up, as
248
+ # as a string.
249
+ def tool_setting_string(tool, part, default="")
250
+ config["merge-tools", "#{tool}.#{part}", String, default]
251
+ end
252
+
253
+ ##
254
+ # Quick access to the merge-tools section of the configuration files.
255
+ # Returns boolean values.
256
+ #
257
+ # @see check_tool_string
258
+ # @param [String] tool the name of the tool to look up data for
259
+ # @param [String] part the specific information about the tool to look up
260
+ # @param [Boolean] default the default value, if the configuration setting
261
+ # can't be found
262
+ # @return [Boolean] the setting for the given merge tool we're looking up, as
263
+ # as a string.
264
+ def tool_setting(tool, part, default=false)
265
+ config["merge-tools", "#{tool}.#{part}", Boolean, default]
266
+ end
267
+
268
+ ##
269
+ # Given the name of a merge tool, attempt to locate an executable file
270
+ # for the tool.
271
+ #
272
+ # @param [String] tool the name of the merge tool to locate
273
+ # @return [String] the path to the executable for the merge tool, or nil
274
+ # if the tool cannot be found
275
+ def find_tool(tool)
276
+ if ["internal:fail", "internal:local", "internal:other"].include?(tool)
277
+ return tool
278
+ end
279
+ # windows stuff
280
+ k = tool_setting_string(tool, "regkey")
281
+ if k && k.any?
282
+ p = File.amp_lookup_reg(k, tool_setting_string(tool, "regname"))
283
+ if p && p.any?
284
+ p = File.amp_find_executable(p + check_tool_string(tool, "regappend"))
285
+ if p
286
+ return p
287
+ end
288
+ end
289
+ end
290
+ # normal *nix lookup
291
+ return File.amp_find_executable(tool_setting_string(tool, "executable", tool))
292
+ end
293
+
294
+ ##
295
+ # Checks to see if a given tool is available given the necessary settings.
296
+ #
297
+ # @todo add GUI check
298
+ # @param [String] tool the name of the tool we want to check
299
+ # @param [String] pat the pattern we matched to get here. Could be nil.
300
+ # @param [Boolean] symlink are we merging across a symlink here?
301
+ # @param [Boolean] binary are we merging a binary file? you crazy?!
302
+ # @return [Boolean] is the given tool available?
303
+ def check_tool(tool, pat, symlink, binary)
304
+ tool_msg = tool
305
+ tool_msg += " specified for " + pat if pat
306
+
307
+ if !(find_tool tool)
308
+ if pat
309
+ UI.warn("couldn't find merge tool #{tool}")
310
+ else
311
+ UI.note("couldn't find merge tool #{tool}")
312
+ end
313
+ elsif symlink && !(tool_setting(tool, "symlink"))
314
+ UI.warn("tool #{tool} can't handle symlinks")
315
+ elsif binary && !(tool_setting(tool, "binary"))
316
+ UI.warn("tool #{tool} can't handle binary files")
317
+ elsif false # TODO: add GUI check
318
+ else
319
+ return true
320
+ end
321
+ return false # we're here if any of the previous checks created a warning
322
+ end
323
+
324
+ end
325
+ end
326
+ end
327
+ end