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,6 +1,6 @@
1
1
  module Amp
2
2
  module Graphs
3
-
3
+
4
4
  ##
5
5
  # = AncestorGenerator
6
6
  # A generator class that will allow us to only calculate one ancestor
@@ -143,5 +143,6 @@ module Amp
143
143
  return nil
144
144
  end
145
145
  end
146
+
146
147
  end
147
148
  end
@@ -1,260 +1,263 @@
1
1
  module Amp
2
2
  module Graphs
3
- ##
4
- # = CopyCalculator
5
- #
6
- # This module manages finding what files have been copied between two
7
- # changesets, using a base, ancestor changeset. Closely related to merging.
8
- # We need this class because Mercurial, by default, allows us to copy files
9
- # and move files, and be smart enough to follow these copies throughout the
10
- # version history. Other VCS's just treat the moved file as a brand-new file.
11
- # Thus, when we update from one changeset to another, we need to follow
12
- # these copies.
13
- module CopyCalculator
3
+ module Mercurial
14
4
 
15
5
  ##
16
- # Calculates the copies between 2 changesets, using a pre-calculated common ancestor
17
- # node. This is used during updates as part of Mercurial's ability to track renames
18
- # without git-style guessing. Unfortunately it does require some amount of calculation.
19
- # This method returns two hashes in an array: [renames, divergent]. "Renames" are
20
- # moves from one file to another, and "divergent" are two moves of the same file,
21
- # only with different end-names. See @example for how divergent works.
6
+ # = CopyCalculator
22
7
  #
23
- # @todo Add tracking of directory renames!
24
- #
25
- # @example
26
- # if the local changeset renamed "foo" to "bar", and the remote changeset renamed
27
- # "foo" to "baz", then the "divergent" hash would be:
28
- # {"foo" => ["bar", "baz"]}
29
- #
30
- # @param [Repository] repo The repo for which we are calculating changes. Typically
31
- # a LocalRepository.
32
- # @param [Changeset] changeset_local The local (or just 1 of the bases) changeset.
33
- # @param [Changeset] changeset_remote The remote (or the second base) changeset
34
- # @param [Changeset] changeset_ancestor The common ancestor between {changeset_local}
35
- # and {changeset_remote}
36
- # @param [Boolean] check_dirs (false) Whether or not to analyze for directory renames.
37
- # this is an expensive operation, so it defaults to false.
38
- # @return [[Hash, Hash]] This method returns two hashes in an array, where the first
39
- # is a list of normal file-moves ("foo" renamed to "bar" returns {"foo" => "bar"})
40
- # and the second is a list of divergent file-moves (see @example)
41
- #
42
- def self.find_copies(repo, changeset_local, changeset_remote, changeset_ancestor, check_dirs=false)
43
- # are we udpating from an empty directory? quite easy.
44
- if changeset_local.nil? || changeset_remote.nil? ||
45
- changeset_remote == changeset_local
46
- return {}, {}
47
- end
48
- # avoid silly behavior for parent -> working directory
49
- if changeset_remote.node == nil && c1.node == repo.dirstate.parents.first
50
- return repo.dirstate.copies, {}
51
- end
52
-
53
- limit = find_limit(repo, changeset_local.revision, changeset_remote.revision)
54
- man_local = changeset_local.manifest
55
- man_remote = changeset_remote.manifest
56
- man_ancestor = changeset_ancestor.manifest
8
+ # This module manages finding what files have been copied between two
9
+ # changesets, using a base, ancestor changeset. Closely related to merging.
10
+ # We need this class because Mercurial, by default, allows us to copy files
11
+ # and move files, and be smart enough to follow these copies throughout the
12
+ # version history. Other VCS's just treat the moved file as a brand-new file.
13
+ # Thus, when we update from one changeset to another, we need to follow
14
+ # these copies.
15
+ module CopyCalculator
57
16
 
58
- # gets the versioned_file for a given file and node ID
59
- easy_file_lookup = proc do |file, node|
60
- if node.size == 20
61
- return repo.versioned_file(file, :file_id => node)
17
+ ##
18
+ # Calculates the copies between 2 changesets, using a pre-calculated common ancestor
19
+ # node. This is used during updates as part of Mercurial's ability to track renames
20
+ # without git-style guessing. Unfortunately it does require some amount of calculation.
21
+ # This method returns two hashes in an array: [renames, divergent]. "Renames" are
22
+ # moves from one file to another, and "divergent" are two moves of the same file,
23
+ # only with different end-names. See @example for how divergent works.
24
+ #
25
+ # @todo Add tracking of directory renames!
26
+ #
27
+ # @example
28
+ # if the local changeset renamed "foo" to "bar", and the remote changeset renamed
29
+ # "foo" to "baz", then the "divergent" hash would be:
30
+ # {"foo" => ["bar", "baz"]}
31
+ #
32
+ # @param [Repository] repo The repo for which we are calculating changes. Typically
33
+ # a LocalRepository.
34
+ # @param [Changeset] changeset_local The local (or just 1 of the bases) changeset.
35
+ # @param [Changeset] changeset_remote The remote (or the second base) changeset
36
+ # @param [Changeset] changeset_ancestor The common ancestor between {changeset_local}
37
+ # and {changeset_remote}
38
+ # @param [Boolean] check_dirs (false) Whether or not to analyze for directory renames.
39
+ # this is an expensive operation, so it defaults to false.
40
+ # @return [[Hash, Hash]] This method returns two hashes in an array, where the first
41
+ # is a list of normal file-moves ("foo" renamed to "bar" returns {"foo" => "bar"})
42
+ # and the second is a list of divergent file-moves (see @example)
43
+ #
44
+ def self.find_copies(repo, changeset_local, changeset_remote, changeset_ancestor, check_dirs=false)
45
+ # are we udpating from an empty directory? quite easy.
46
+ if changeset_local.nil? || changeset_remote.nil? ||
47
+ changeset_remote == changeset_local
48
+ return {}, {}
62
49
  end
63
- cs = (changeset_local.revision == nil) ? changeset_local : changeset_remote
64
- return cs.get_file(file)
65
- end
66
-
67
- ctx = easy_file_lookup
68
- copy = {}
69
- full_copy = {}
70
- diverge = {}
71
-
72
- # check for copies from manifest1 to manifest2
73
- check_copies = proc do |file, man1, man2|
74
- vf1 = easy_file_lookup[file, man1[file]]
75
- find_old_names(vf1, limit) do |old_name|
76
- full_copy[file] = old_name # remember for dir rename detection
77
- if man2[old_name] # original file in other manifest?
78
- # if the original file is unchanged on the other branch,
79
- # no merge needed
80
- if man2[old_name] != man_ancestor[old_name]
81
- vf2 = easy_file_lookup[old_file, man2[old_file]]
82
- vfa = vf1.ancestor(vf2)
83
- # related and name changed on only one side?
84
- if vfa && (vfa.path == file || vfa.path == vf2.path) && (vf1 == vfa || vf2 == vfa)
85
- copy[file] = old_file
50
+ # avoid silly behavior for parent -> working directory
51
+ if changeset_remote.node == nil && c1.node == repo.dirstate.parents.first
52
+ return repo.dirstate.copies, {}
53
+ end
54
+
55
+ limit = find_limit(repo, changeset_local.revision, changeset_remote.revision)
56
+ man_local = changeset_local.manifest_entry
57
+ man_remote = changeset_remote.manifest_entry
58
+ man_ancestor = changeset_ancestor.manifest_entry
59
+
60
+ # gets the versioned_file for a given file and node ID
61
+ easy_file_lookup = proc do |file, node|
62
+ if node.size == 20
63
+ return repo.versioned_file(file, :file_id => node)
64
+ end
65
+ cs = (changeset_local.revision == nil) ? changeset_local : changeset_remote
66
+ return cs.get_file(file)
67
+ end
68
+
69
+ ctx = easy_file_lookup
70
+ copy = {}
71
+ full_copy = {}
72
+ diverge = {}
73
+
74
+ # check for copies from manifest1 to manifest2
75
+ check_copies = proc do |file, man1, man2|
76
+ vf1 = easy_file_lookup[file, man1[file]]
77
+ find_old_names(vf1, limit) do |old_name|
78
+ full_copy[file] = old_name # remember for dir rename detection
79
+ if man2[old_name] # original file in other manifest?
80
+ # if the original file is unchanged on the other branch,
81
+ # no merge needed
82
+ if man2[old_name] != man_ancestor[old_name]
83
+ vf2 = easy_file_lookup[old_file, man2[old_file]]
84
+ vfa = vf1.ancestor(vf2)
85
+ # related and name changed on only one side?
86
+ if vfa && (vfa.path == file || vfa.path == vf2.path) && (vf1 == vfa || vf2 == vfa)
87
+ copy[file] = old_file
88
+ end
86
89
  end
90
+ elsif man_ancestor[old_file]
91
+ (diverge[old_file] ||= []) << file
87
92
  end
88
- elsif man_ancestor[old_file]
89
- (diverge[old_file] ||= []) << file
90
93
  end
91
94
  end
92
- end
93
-
94
- UI.debug(" searching for copies back to rev #{limit}")
95
-
96
- unmatched_1 = double_intersection(man_local, man_remote, man_ancestor)
97
- unmatched_2 = double_intersection(man_remote, man_local, man_ancestor)
98
-
99
- UI.debug(" unmatched files in local:\n #{unmatched_1.join("\n")}") if unmatched_1.any?
100
- UI.debug(" unmatched files in other:\n #{unmatched_2.join("\n")}") if unmatched_2.any?
101
-
102
- unmatched_1.each {|file| check_copies[file, man_local, man_remote] }
103
- unmatched_2.each {|file| check_copies[file, man_remote, man_local] }
104
-
105
- diverge_2 = {}
106
- diverge.each do |old_file, file_list|
107
- if file_list.size == 1
108
- diverge.delete old_file
109
- else
110
- file_list.each {|file| diverge_2[file] = true}
95
+
96
+ UI.debug(" searching for copies back to rev #{limit}")
97
+
98
+ unmatched_1 = double_intersection(man_local, man_remote, man_ancestor)
99
+ unmatched_2 = double_intersection(man_remote, man_local, man_ancestor)
100
+
101
+ UI.debug(" unmatched files in local:\n #{unmatched_1.join("\n")}") if unmatched_1.any?
102
+ UI.debug(" unmatched files in other:\n #{unmatched_2.join("\n")}") if unmatched_2.any?
103
+
104
+ unmatched_1.each {|file| check_copies[file, man_local, man_remote] }
105
+ unmatched_2.each {|file| check_copies[file, man_remote, man_local] }
106
+
107
+ diverge_2 = {}
108
+ diverge.each do |old_file, file_list|
109
+ if file_list.size == 1
110
+ diverge.delete old_file
111
+ else
112
+ file_list.each {|file| diverge_2[file] = true}
113
+ end
111
114
  end
115
+
116
+ if !(full_copy.any?) || !check_dirs
117
+ return copy, diverge
118
+ end
119
+
120
+ # CHECK FOR DIRECTORY RENAMES
121
+ # TODO TODO TODO
112
122
  end
113
123
 
114
- if !(full_copy.any?) || !check_dirs
115
- return copy, diverge
116
- end
117
-
118
- # CHECK FOR DIRECTORY RENAMES
119
- # TODO TODO TODO
120
- end
121
-
122
- private
123
-
124
- ##
125
- # Find the earliest revision in the repository that is an ancestor of EITHER a OR b,
126
- # but NOT both. In other words, find the oldest ancestor on a branch.
127
- #
128
- # @param [Repository] repo the repository we're calculatizing on
129
- # @param [Integer] a one changeset's revision #
130
- # @param [Integer] b the other changeset's revision #
131
- # @return [Integer] the earliest revision index that is an ancestor of only 1 of the
132
- # two changesets.
133
- def self.find_limit(repo, a, b)
134
- # basic idea:
135
- # - mark a and b with different sides
136
- # - if a parent's children are all on the same side, the parent is
137
- # on that side, otherwise it is on no side
138
- # - walk the graph in topological order with the help of a heap;
139
- # - add unseen parents to side map
140
- # - clear side of any parent that has children on different sides
141
- # - track number of interesting revs that might still be on a side
142
- # - track the lowest interesting rev seen
143
- # - quit when interesting revs is zero
144
- changelog = repo.changelog
145
- working = changelog.size # this revision index is 1 higher than the real highest
146
- a ||= working
147
- b ||= working
124
+ private
148
125
 
149
- side = {a => -1, b => 1}
150
- visit = PriorityQueue.new # because i don't have any other data structure that
151
- visit[-a] = -a # maintains a sorted order
152
- visit[-b] = -b
153
- interesting = visit.size # could be 1 if a == b
154
- limit = working
155
- while interesting > 0
156
- r, junk = -(visit.delete_min) # get the next lowest revision
157
- if r == working
158
- # different way of getting parents in this case
159
- parents = repo.dirstate.parents.map {|p| changelog.rev(p)}
160
- else
161
- # normal way of getting parents
162
- parents = changelog.parent_indices_for_index(r)
163
- end
164
- parents.each do |parent|
165
- if !side[parent]
166
- # haven't seen the parent before, so let's put it on a side.
167
- side[parent] = side[r]
168
- interesting += 1 if side[parent] != 0 # if it's on a side
169
- visit[-parent] = -parent
170
- elsif side[parent] && side[parent] != side[r]
171
- # if we're here, then the parent has been seen by BOTH sides. so it's no good.
172
- side[parent] = 0
126
+ ##
127
+ # Find the earliest revision in the repository that is an ancestor of EITHER a OR b,
128
+ # but NOT both. In other words, find the oldest ancestor on a branch.
129
+ #
130
+ # @param [Repository] repo the repository we're calculatizing on
131
+ # @param [Integer] a one changeset's revision #
132
+ # @param [Integer] b the other changeset's revision #
133
+ # @return [Integer] the earliest revision index that is an ancestor of only 1 of the
134
+ # two changesets.
135
+ def self.find_limit(repo, a, b)
136
+ # basic idea:
137
+ # - mark a and b with different sides
138
+ # - if a parent's children are all on the same side, the parent is
139
+ # on that side, otherwise it is on no side
140
+ # - walk the graph in topological order with the help of a heap;
141
+ # - add unseen parents to side map
142
+ # - clear side of any parent that has children on different sides
143
+ # - track number of interesting revs that might still be on a side
144
+ # - track the lowest interesting rev seen
145
+ # - quit when interesting revs is zero
146
+ changelog = repo.changelog
147
+ working = changelog.size # this revision index is 1 higher than the real highest
148
+ a ||= working
149
+ b ||= working
150
+
151
+ side = {a => -1, b => 1}
152
+ visit = PriorityQueue.new # because i don't have any other data structure that
153
+ visit[-a] = -a # maintains a sorted order
154
+ visit[-b] = -b
155
+ interesting = visit.size # could be 1 if a == b
156
+ limit = working
157
+ while interesting > 0
158
+ r, junk = -(visit.delete_min) # get the next lowest revision
159
+ if r == working
160
+ # different way of getting parents in this case
161
+ parents = repo.dirstate.parents.map {|p| changelog.rev(p)}
162
+ else
163
+ # normal way of getting parents
164
+ parents = changelog.parent_indices_for_index(r)
165
+ end
166
+ parents.each do |parent|
167
+ if !side[parent]
168
+ # haven't seen the parent before, so let's put it on a side.
169
+ side[parent] = side[r]
170
+ interesting += 1 if side[parent] != 0 # if it's on a side
171
+ visit[-parent] = -parent
172
+ elsif side[parent] && side[parent] != side[r]
173
+ # if we're here, then the parent has been seen by BOTH sides. so it's no good.
174
+ side[parent] = 0
175
+ interesting -= 1
176
+ end
177
+ end
178
+ # if we're here and side[r] isn't 0, then it's an ancestor to [one and only one]
179
+ # of the 2 root nodes. so keep it.
180
+ if side[r] && side[r] != 0
181
+ limit = r
173
182
  interesting -= 1
174
183
  end
175
184
  end
176
- # if we're here and side[r] isn't 0, then it's an ancestor to [one and only one]
177
- # of the 2 root nodes. so keep it.
178
- if side[r] && side[r] != 0
179
- limit = r
180
- interesting -= 1
181
- end
185
+ limit
182
186
  end
183
- limit
184
- end
185
-
186
- ##
187
- # Go back in time until revision {limit}, grabbing old names that {versioned_file}
188
- # was moved from.
189
- #
190
- # @param [VersionedFile] versioned_file the file for which we are finding old names
191
- # @param [Integer] limit the minimum revision back in time in which we should
192
- # search for old names
193
- # @return [Array<String>] old names for the current file.
194
- def self.find_old_names(versioned_file, limit)
195
- # wooooo recursion unrolling!
196
- old = {}
197
- seen = {}
198
- orig = versioned_file.path
199
- visit = [[versioned_file, 0]]
200
- while visit.any? do
201
- file, depth = visit.shift
202
- str = file.to_s
203
- next if seen[str]
204
-
205
- seen[str] = true
206
- if file.path != orig && !old[file.path]
207
- old[file.path] = [depth, file.path]
187
+
188
+ ##
189
+ # Go back in time until revision {limit}, grabbing old names that {versioned_file}
190
+ # was moved from.
191
+ #
192
+ # @param [VersionedFile] versioned_file the file for which we are finding old names
193
+ # @param [Integer] limit the minimum revision back in time in which we should
194
+ # search for old names
195
+ # @return [Array<String>] old names for the current file.
196
+ def self.find_old_names(versioned_file, limit)
197
+ # wooooo recursion unrolling!
198
+ old = {}
199
+ seen = {}
200
+ orig = versioned_file.path
201
+ visit = [[versioned_file, 0]]
202
+ while visit.any? do
203
+ file, depth = visit.shift
204
+ str = file.to_s
205
+ next if seen[str]
206
+
207
+ seen[str] = true
208
+ if file.path != orig && !old[file.path]
209
+ old[file.path] = [depth, file.path]
210
+ end
211
+ next if file.revision < limit && file.revision != nil
212
+ visit += file.parents.each {|p| [p, depth - 1]}
208
213
  end
209
- next if file.revision < limit && file.revision != nil
210
- visit += file.parents.each {|p| [p, depth - 1]}
214
+ old.values.sort.map {|o| o[1]}
211
215
  end
212
- old.values.sort.map {|o| o[1]}
213
- end
214
-
215
- ##
216
- # Returns all the parent directories of every file in the provided array,
217
- # recursively, as a map. Each entry maps a directory to {true}. It's really
218
- # just a set, but I'm too lazy to use a set. Sorry.
219
- #
220
- # @param [Array<String>] files a list of files for which we need all the parent
221
- # directories
222
- # @return [Hash] a map: each entry maps a directory name to {true}, because it's
223
- # really just a set, because I'm too lazy to use a set.
224
- def self.all_parent_dirs(files)
225
- dirs = {}
226
- files.each do |file|
227
- file = picky_dirname file
228
- until dirs[file]
229
- dirs[file] = true
216
+
217
+ ##
218
+ # Returns all the parent directories of every file in the provided array,
219
+ # recursively, as a map. Each entry maps a directory to {true}. It's really
220
+ # just a set, but I'm too lazy to use a set. Sorry.
221
+ #
222
+ # @param [Array<String>] files a list of files for which we need all the parent
223
+ # directories
224
+ # @return [Hash] a map: each entry maps a directory name to {true}, because it's
225
+ # really just a set, because I'm too lazy to use a set.
226
+ def self.all_parent_dirs(files)
227
+ dirs = {}
228
+ files.each do |file|
230
229
  file = picky_dirname file
230
+ until dirs[file]
231
+ dirs[file] = true
232
+ file = picky_dirname file
233
+ end
231
234
  end
235
+ dirs
236
+ end
237
+
238
+ ##
239
+ # This method will find the path of the containing directory for the file
240
+ # pointed to by {path}. We use this instead of File.dirname because we
241
+ # want to return the empty string instead of "." if there is no path separator
242
+ # in the provided string.
243
+ #
244
+ # @param [String] path the path to the file we want the directory name of
245
+ # @return [String] the path of the containing directory of the provided file
246
+ def self.picky_dirname(path)
247
+ Dir.dirname path
248
+ end
249
+
250
+ ##
251
+ # Returns a list of elements in d1 that are not in d2 or d3. We have this method
252
+ # because Mercurial's source has this method.
253
+ #
254
+ # @param [Array] d1 a list of items we wish to filter
255
+ # @param [Array] d2 a list of items we do NOT want in d1
256
+ # @param [Array] d3 a list of items we do NOT want in d1
257
+ # @return [Array] a list of items in d1 that are not present in d2 or d3
258
+ def self.double_intersection(d1, d2, d3)
259
+ d1.reject {|i| d2.include?(i) || d3.include?(i) }
232
260
  end
233
- dirs
234
- end
235
-
236
- ##
237
- # This method will find the path of the containing directory for the file
238
- # pointed to by {path}. We use this instead of File.dirname because we
239
- # want to return the empty string instead of "." if there is no path separator
240
- # in the provided string.
241
- #
242
- # @param [String] path the path to the file we want the directory name of
243
- # @return [String] the path of the containing directory of the provided file
244
- def self.picky_dirname(path)
245
- Dir.dirname path
246
- end
247
-
248
- ##
249
- # Returns a list of elements in d1 that are not in d2 or d3. We have this method
250
- # because Mercurial's source has this method.
251
- #
252
- # @param [Array] d1 a list of items we wish to filter
253
- # @param [Array] d2 a list of items we do NOT want in d1
254
- # @param [Array] d3 a list of items we do NOT want in d1
255
- # @return [Array] a list of items in d1 that are not present in d2 or d3
256
- def self.double_intersection(d1, d2, d3)
257
- d1.reject {|i| d2.include?(i) || d3.include?(i) }
258
261
  end
259
262
  end
260
263
  end