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
@@ -1,450 +0,0 @@
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
@@ -1,234 +0,0 @@
1
- module Amp
2
- module Repositories
3
- ##
4
- # = BranchManager
5
- # Michael Scott for Amp.
6
- #
7
- # More seriously, this class handles reading/writing to the branch cache
8
- # and figuring out what the head revisions are for each branch and such.
9
- module BranchManager
10
- include Amp::RevlogSupport::Node
11
-
12
- ##
13
- # Saves the branches with the given "partial" and the last_rev index.
14
- def save_branch_cache(partial, last_rev)
15
- tiprev = self.size - 1
16
- # If our cache is outdated, then update it and re-write it
17
- if last_rev != tiprev
18
- # search for new heads
19
- update_branches!(partial, last_rev+1, tiprev+1)
20
- # write our data out
21
- write_branches!(partial, self.changelog.tip, tiprev)
22
- end
23
- # return our mappings
24
- partial
25
- end
26
-
27
- ##
28
- # Loads the head revisions for each branch. Each branch has at least one, but
29
- # possible more than one, head.
30
- #
31
- # @return [Hash] a map, where the branch names are keys and the values
32
- # are the heads of the branch
33
- def branch_heads
34
- # Gets the mapping of branch names to branch heads, but uses caching to avoid
35
- # doing IO and tedious computation over and over. As long as our tip doesn't
36
- # change, the cache will remain valid.
37
-
38
- # Check our current tip
39
- tip = self.changelog.tip
40
- # Do we have a cache, and if we do, is the saved cache == tip?
41
- if !@branch_cache.nil? && @branch_cache_tip == tip
42
- # if so, cache HIT
43
- return @branch_cache
44
- end
45
-
46
- # nope? cache miss
47
- # save the old tip
48
- oldtip = @branch_cache_tip
49
- # save the new tip
50
- @branch_cache_tip = tip
51
-
52
- # reset the branch cache
53
- @branch_cache = @branch_cache.nil? ? {} : @branch_cache.clear # returns same hash, but empty
54
- # if we didn't have an old cache, or it was invalid, read in the branches again
55
- if oldtip.nil? || self.changelog.node_map[oldtip].nil?
56
- partial, last, last_rev = read_branches!
57
- else
58
- # Otherwise, dig up the cached hash!
59
- last_rev = self.changelog.rev(oldtip)
60
- # Get the last branch cache
61
- partial = @u_branch_cache
62
- end
63
- # Save the branch cache (updating it if we have to)
64
- save_branch_cache(partial, last_rev)
65
-
66
- # Cache our saved hash
67
- @u_branch_cache = partial
68
-
69
- # Copy the partial into the branch_cache
70
- partial.each { |k, v| @branch_cache[k] = v }
71
- @branch_cache
72
- end
73
-
74
- # Returns a dict where branch names map to the tipmost head of
75
- # the branch, open heads come before closed
76
- def branch_tags
77
- tags = {}
78
- branch_heads.each do |branch_node, local_heads|
79
- head = nil
80
- local_heads.reverse_each do |h| # get the tip if its a closed
81
- if !(changelog.read(h)[5].include?("close"))
82
- head = h
83
- break
84
- end
85
- end
86
- head = local_heads.last if head.nil?
87
- tags[branch_node] = head # it's the tip
88
- end
89
- return tags
90
- end
91
-
92
- ##
93
- # Saves the branches, the tip-most node, and the tip-most revision
94
- # to the branch cache.
95
- #
96
- def save_branches(branches, tip, tip_revision)
97
- write_branches!(branches, tip, tip_revision)
98
- end
99
-
100
- private
101
-
102
- ##
103
- # Reads in the branches from the branch.cache file, stored in the root
104
- # of the repository's .hg folder. While the repository could figure out
105
- # what each branch's heads are each time the program is run, that would
106
- # be quite slow. So we cache them in a file, along with the tip of the
107
- # repository, so we know if our cache has become inaccurate.
108
- # The format is very simple:
109
- # [tip_node_id] [tip_revision_number]
110
- # [branch_head_node_id] [branch_name]
111
- # [branch_head_node_id] [branch_name]
112
- # [branch_head_node_id] [branch_name]
113
- #
114
- # Example:
115
- # 0abc3135810abc3135810abc3135810abc313581 603
116
- # 0abc3135810abc3135810abc3135810abc313581 default
117
- # 1234567890123456789012345678901234567890 other_branch
118
- # 0987654321098765432109876543210987654321 other_branch
119
- #
120
- # In the example, other_branch has 2 heads. This is acceptable. The tip of the
121
- # repository is node 0abc3135, revision 603, which is the only head of the default
122
- # branch.
123
- #
124
- # @return [[Hash, String, Integer]] The results are returned in the form of:
125
- # [partial, tip_node_id, tip_rev_index], where +partial+ is a mapping of
126
- # branch names to an array of their heads.
127
- def read_branches!
128
- partial, last, last_rev = {}, nil, nil
129
- lines = nil
130
- invalid = false
131
-
132
- begin
133
- # read in all the lines. This file should be short, so don't worry about
134
- # performance concerns of a File.read() call (this call is actually
135
- # Opener#read, which then calls File.read)
136
- lines = @hg_opener.read("branchheads.cache").split("\n")
137
- rescue SystemCallError # IO Errors, i.e. if there is no branch.cache file
138
- return {}, NULL_ID, NULL_REV
139
- end
140
- # use catch, not exceptions (exceptions are more costly)
141
- valid = catch(:invalid) do
142
- # Read in the tip node and tip revision #
143
- last, last_rev = lines.shift.split(" ", 2)
144
- last, last_rev = last.unhexlify, last_rev.to_i
145
-
146
- # if we aren't matching up with the current repo, then invalidate the cache
147
- if last_rev > self.size || self[last_rev].node != last
148
- throw :invalid, false
149
- end
150
-
151
- # Go through each next line and read in a head-branch pair
152
- lines.each do |line|
153
- # empty = useless line
154
- next if line.nil? || line.empty?
155
- # split on " ", only once so we can have a space in a branch name
156
- node, _label = line.split(" ", 2)
157
- # and assign to our "partial" i.e. our list of branch-heads
158
- partial[_label.strip] ||= []
159
- partial[_label.strip] << node.unhexlify
160
- end
161
- end
162
-
163
- # if invalid was thrown.... bail
164
- unless valid
165
- UI.puts("invalidating branch cache (tip different)")
166
- partial, last, last_rev = {}, NULL_ID, NULL_REV
167
- end
168
-
169
- # Return our results!
170
- [partial, last, last_rev]
171
- end
172
-
173
- ##
174
- # Invalidates the tag cache. Removes all ivars relating to tags.
175
- def invalidate_branch_cache!
176
- @branch_cache = nil
177
- @branch_cache_tip = nil
178
- @u_branch_cache = nil
179
- end
180
-
181
- ##
182
- # Goes through from revision +start+ to revision +stop+ and searches for
183
- # new branch heads for each branch. Annoying, yes. But necessary to keep the
184
- # cache up to date.
185
- #
186
- # @param [Hash] partial the current pairing of branch names to heads. Might
187
- # be incomplete, which is why it's called "partial"
188
- # @param [Integer] start the revision # to start looking new branch heads
189
- # @param [Integer] stop the last revision in which to look for branch heads
190
- def update_branches!(partial, start, stop)
191
- (start..(stop-1)).each do |r|
192
- # get the changeset
193
- changeset = self[r]
194
- # look at its branch
195
- branch = changeset.branch
196
- # get that branch's partial list of heads
197
-
198
- branch_heads = (partial[branch] ||= [])
199
- # add this changeset
200
- branch_heads << changeset.node
201
- # remove our parents from this branch's list of heads if they're in there,
202
- # because if they have children, they aren't heads.
203
- changeset.parents.each do |parent|
204
- # get the node_id
205
- parent_node = parent.node
206
- # remove the parent
207
- branch_heads.delete parent_node if branch_heads.include? parent_node
208
- end
209
- end
210
- end
211
-
212
- ##
213
- # Writes the branches out to the branch cache. Simple as that. See #read_branches!
214
- # for the file format.
215
- #
216
- # @see read_branches!
217
- # @param [Hash] branches a mapping of branch names to arrays of their head node IDs
218
- # @param [String] tip the tip of this repository's node ID
219
- # @param [Integer] tip_revision the index # of this repository's tip
220
- def write_branches!(branches, tip, tip_revision)
221
- @hg_opener.open "branchheads.cache", "w" do |f|
222
- f << "#{tip.hexlify} #{tip_revision}\n"
223
- branches.each do |_label, nodes|
224
- nodes.each {|node| f << "#{node.hexlify} #{_label}\n" }
225
- end
226
- end
227
- rescue SystemCallError
228
-
229
- end
230
- end
231
- MichaelScott = BranchManager # hehehe
232
- end
233
- end
234
-