amp 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
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,41 @@
1
+ module Amp
2
+ module Repositories
3
+ module Git
4
+
5
+ class GitPicker < GenericRepoPicker
6
+
7
+ def self.pick(config, path='', create=false)
8
+ # hot path so we don't load the HTTP repos!
9
+ unless path[0,4] == "http"
10
+ return LocalRepository.new(find_repo(path), create, config)
11
+ end
12
+ raise "Unknown repository format for Git"
13
+ end
14
+
15
+ def self.repo_in_dir?(path)
16
+ return true if path[0, 4] == "http"
17
+ until File.directory? File.join(path, ".git")
18
+ old_path, path = path, File.dirname(path)
19
+ if path == old_path
20
+ return false
21
+ end
22
+ end
23
+ true
24
+ end
25
+
26
+ ################################
27
+ private
28
+ ################################
29
+ def self.find_repo(path)
30
+ until File.directory? File.join(path, ".git")
31
+ old_path, path = path, File.dirname(path)
32
+ if path == old_path
33
+ raise "No Repository Found"
34
+ end
35
+ end
36
+ path
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,382 @@
1
+ module Amp
2
+ module Diffs
3
+ module Mercurial
4
+
5
+ ##
6
+ # = MercurialDiff
7
+ # Mercurial has it's own implementation of the unified diff, because windows
8
+ # boxes don't have diff -u. Plus code is faster than the shell.
9
+ # Lame. That's ok, it's pretty easy to do. And we can also add flags and
10
+ # change default settings.
11
+ #
12
+ # Mainly, you're only going to use MercurialDiff.unified_diff(). It's usage
13
+ # is described below.
14
+ module MercurialDiff
15
+ extend self
16
+ ##
17
+ # These are the default options you can modify. Grab them, clone them,
18
+ # change them. Notice: You have to *clone* this when you use it, or
19
+ # you will be changing the default options!
20
+ DEFAULT_OPTIONS = {:context => 3, :text => false, :show_func => false,
21
+ :git => false, :no_dates => false, :ignore_ws => false,
22
+ :ignore_ws_amount => false, :ignore_blank_lines => false,
23
+ :pretty => false}
24
+
25
+ ##
26
+ # Clear up whitespace in the text if we have any options relating
27
+ # to getting rid of whitespace.
28
+ #
29
+ # @param [String] text the text to modify
30
+ # @param [Hash] options the options to use when deciding how to clean text
31
+ # @option [Boolean] options :ignore_ws (false) do we ignore all whitespace?
32
+ # this has the net effect of removing all whitespace.
33
+ # @option [Boolean] options :ignore_ws_amount (false) when this option is
34
+ # true, we only remove "excessive" whitespace - more than 1 space or tab.
35
+ # we then substitute it all with 1 space.
36
+ # @option [Boolean] options :ignore_blank_lines (false) when this option
37
+ # is true, we remove all extra blank lines.
38
+ def whitespace_clean(text, options=DEFAULT_OPTIONS)
39
+ if options[:ignore_ws]
40
+ text.gsub!(/[ \t]+/, "") #warnings made me use parens
41
+ elsif options[:ignore_ws_amount]
42
+ text.gsub!(/[ \t]+/, ' ')
43
+ text.gsub!(/[ \t]+\n/, "\n")
44
+ end
45
+ text.gsub!(/\n+/, '') if options[:ignore_blank_lines]
46
+ text
47
+ end
48
+
49
+ ##
50
+ # Given a line, returns a string that represents "adding that line" in a diff,
51
+ # based on the options.
52
+ #
53
+ # @param [String] input the input line
54
+ # @return [String] the output line, in a format indicating it is "added"
55
+ def add_line(input, options)
56
+ options[:pretty] ? "+#{input.chomp}".green+"\n" : "+#{input}"
57
+ end
58
+
59
+ ##
60
+ # Given a line, returns a string that represents "removing that line" in a diff,
61
+ # based on the options.
62
+ #
63
+ # @param [String] input the input line
64
+ # @return [String] the output line, in a format indicating it is "removed"
65
+ def remove_line(input, options)
66
+ options[:pretty] ? "-#{input.chomp}".red+"\n" : "-#{input}"
67
+ end
68
+
69
+ ##
70
+ # Creates a header or something? Not sure what this is used for, no code
71
+ # references it. I think it's for git or something. eh.
72
+ def diff_line(revisions, a, b, options=DEFAULT_OPTIONS)
73
+ options = DEFAULT_OPTIONS.merge options
74
+ parts = ['diff']
75
+
76
+ parts << '--git' if options[:git]
77
+ parts << revisions.map {|r| "-r #{r}"}.join(' ') if revisions && !options[:git]
78
+ if options[:git]
79
+ parts << "a/#{a}"
80
+ parts << "b/#{b}"
81
+ else
82
+ parts << a
83
+ end
84
+ parts.join(' ') + "\n"
85
+ end
86
+
87
+ ##
88
+ # Creates a date tag appropriate for diffs. Not all diff types use
89
+ # dates though (namely git, apparently), so the options matter.
90
+ #
91
+ # @param [Time] date the time that we want to make a spiffy date line for
92
+ # @param [String] fn1 the filename of the file being stamped. Only
93
+ # used if the addtab option is on.
94
+ # @param [Boolean] addtab (false) whether or not to add a tab in the
95
+ # line or not. Only used if we're in git mode or no-date mode.
96
+ # @param options the options to use while creating the date line.
97
+ # @option [Boolean] options :git (false) are we creating a git diff?
98
+ # this will deactivate dates.
99
+ # @option options [Boolean] :nodates (false) should we never print dates?
100
+ def date_tag(date, fn1, addtab = true, options = DEFAULT_OPTIONS)
101
+ return "\t#{date.to_diff}\n" if !(options[:git]) && !(options[:nodates])
102
+ return "\t\n" if addtab && fn1 =~ / /
103
+ return "\n"
104
+ end
105
+
106
+ ##
107
+ # Returns a unified diff based on the 2 blocks of text, their modification
108
+ # times, their filenames, and the options.
109
+ #
110
+ # This is a self-contained replacement for diffs.
111
+ #
112
+ # @param a the original text
113
+ # @param [Time] ad the modification timestamp for the old file
114
+ # @param b the new text
115
+ # @param [Time] bd the modification timestamp for the new file
116
+ # @param fn1 the old filename
117
+ # @param fn2 the new filename
118
+ # @param r not sure what this does
119
+ # @param options the options we will be using. There's a lot of settings,
120
+ # see the descriptions for {whitespace_clean} and {date_tag}.
121
+ def unified_diff(a, ad, b, bd, fn1, fn2, r=nil, options=DEFAULT_OPTIONS)
122
+ return "" if (a.nil? || a.empty?) && (b.nil? || b.empty?)
123
+ options = DEFAULT_OPTIONS.merge(options) # overlay the defaults with the supplied opts
124
+ epoch = Time.at(0)
125
+ if !options[:text] && (!a.nil? && a.binary? || !b.nil? && b.binary?)
126
+ return "" if a.any? && b.any? && a.size == b.size && a == b #DERR
127
+ l = ["Binary file #{fn1} has changed\n"]
128
+ elsif a.nil? || a.empty?
129
+ b = b.split_lines_better
130
+ header = []
131
+ if options[:pretty]
132
+ l1 = a.nil? ? "Added file " : "Changed file "
133
+ l1 += "#{fn2} at #{date_tag(bd,fn1,true,options)}"
134
+ l1 = l1.cyan
135
+ header << l1
136
+ else
137
+ if a.nil?
138
+ header << "--- /dev/null#{date_tag(epoch, fn1, false, options)}"
139
+ else
140
+ header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,options)}"
141
+ end
142
+ header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,options)}"
143
+ header << "@@ -0,0 +1,#{b.size} @@\n"
144
+ end
145
+ l = header + (b.map {|line| add_line(line, options)})
146
+ elsif b.nil? || b.empty?
147
+ a = b.split_lines_better
148
+ header = []
149
+ if options[:pretty]
150
+ l1 = b.nil? ? "Removed file " : "Changed file "
151
+ l1 += "#{fn2} at #{date_tag(bd,fn1,true,options)}"
152
+ l1 = l1.cyan
153
+ header << l1
154
+ else
155
+ header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,options)}"
156
+ if b.nil?
157
+ header << "+++ /dev/null#{date_tag(epoch, fn1, false, options)}"
158
+ else
159
+ header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,options)}"
160
+ end
161
+ header << "@@ -1,#{a.size} +0,0 @@\n"
162
+ end
163
+ l = header + (a.map {|line| remove_line(line, options)})
164
+ else
165
+ al = a.split_lines_better
166
+ bl = b.split_lines_better
167
+ l = bunidiff(a, b, al, bl, "a/"+fn1, "b/"+fn2, options)
168
+ return "" if l.nil? || l.empty?
169
+ if options[:pretty]
170
+ l.shift
171
+ if fn1 == fn2
172
+ l[0] = "Changed file #{fn1.cyan} at #{date_tag(bd,fn1,true,options).lstrip}"
173
+ else
174
+ l[0] = "Moved file from #{fn1.cyan} to #{fn2.cyan}"
175
+ end
176
+ else
177
+ l[0] = "#{l[0][0 .. -3]}#{date_tag(ad,fn1,true,options)}"
178
+ l[1] = "#{l[1][0 .. -3]}#{date_tag(bd,fn1,true,options)}"
179
+ end
180
+ end
181
+
182
+ l.size.times do |ln|
183
+ if l[ln][-1,1] != "\n"
184
+ l[ln] << "\n\\n"
185
+ end
186
+ end
187
+
188
+ if r
189
+ l.unshift diff_line(r, fn1, fn2, options)
190
+ end
191
+
192
+ l.join
193
+ end
194
+
195
+ ##
196
+ # Starts a block ending context for a change - part of the unified diff
197
+ # format.
198
+ def context_end(l, len, options)
199
+ ret = l + options[:context]
200
+ ret = len if ret > len
201
+ ret
202
+ end
203
+
204
+ ##
205
+ # Starts a block starting context for a change - part of the unified diff
206
+ # format.
207
+ def context_start(l, options)
208
+ ret = l - options[:context]
209
+ return 0 if ret < 0
210
+ ret
211
+ end
212
+
213
+ ##
214
+ # Given a hunk of changes, yield each line we need to write to the diff.
215
+ #
216
+ # @param [Hash] hunk specifies a block of lines that changed between
217
+ # the two files.
218
+ # @param header the header for the block, if we have one.
219
+ # @param l1 the original lines - used for context (unified diff format)
220
+ # @param delta the lines that have changed thus far
221
+ # @param options settings for the unified diff action. unused mostly here.
222
+ def yield_hunk(hunk, header, l1, delta, options)
223
+ header.each {|x| yield x} if header && header.any?
224
+ delta = hunk[:delta]
225
+ astart, a2, bstart, b2 = hunk[:start_a], hunk[:end_a], hunk[:start_b], hunk[:end_b]
226
+ aend = context_end(a2,l1.size,options)
227
+ alen = aend - astart
228
+ blen = b2 - bstart + aend - a2
229
+
230
+ # i seriously don't know what this does.
231
+ func = ""
232
+ if options[:show_func]
233
+ (astart - 1).downto(0) do |x|
234
+ t = l1[x].rstrip
235
+ if t =~ /\w/
236
+ func = ' ' + t[0 .. 39]
237
+ break
238
+ end
239
+ end
240
+ end
241
+
242
+ # yield the header
243
+ if options[:pretty]
244
+ yield "From original lines #{astart + 1}-#{alen+astart+1}".yellow + "\n"
245
+ else
246
+ yield "@@ -%d,%d +%d,%d @@%s\n" % [astart + 1, alen,
247
+ bstart + 1, blen, func]
248
+ end
249
+
250
+ # then yield each line of changes
251
+ delta.each {|x| yield x}
252
+ # then yield some context or something?
253
+ a2.upto(aend-1) {|x| yield ' ' + l1[x] }
254
+ end
255
+
256
+ ##
257
+ # Helper method for creating unified diffs.
258
+ #
259
+ # @param [String] t1 original text
260
+ # @param [String] t2 new text
261
+ # @param [String] l1 the original text broke into lines?
262
+ # @param [String] l2 the new etxt broken into lines?
263
+ # @param [String] header1 the original file's header
264
+ # @param [String] header2 the new file's header
265
+ # @param opts options for the method
266
+ def bunidiff(t1,t2, l1, l2, header1, header2, opts=DEFAULT_OPTIONS)
267
+ header = [ "--- #{header1}\t\n", "+++ #{header2}\t\n" ]
268
+
269
+ diff = BinaryDiff.blocks(t1,t2)
270
+ hunk = nil
271
+ return_hunks = []
272
+ saved_delta = []
273
+ delta = []
274
+ diff.size.times do |i|
275
+ s = (i > 0) ? diff[i-1] : {:start_a => 0, :end_a => 0, :start_b => 0, :end_b => 0}
276
+ saved_delta += delta unless delta.empty?
277
+ delta = []
278
+ s1 = diff[i]
279
+ a1 = s[:end_a]
280
+ a2 = s1[:start_a]
281
+ b1 = s[:end_b]
282
+ b2 = s1[:start_b]
283
+
284
+ old = (a2 == 0) ? [] : l1[a1..(a2-1)]
285
+ newb = (b2 == 0) ? [] : l2[b1..(b2-1)] #stands for new "b"
286
+
287
+ next if old.empty? && newb.empty?
288
+ if opts[:ignore_ws] || opts[:ignore_blank_lines] || opts[:ignore_ws_amount]
289
+ next if whitespace_clean(old.join,opts) == whitespace_clean(newb.join,opts)
290
+ end
291
+
292
+ astart = context_start(a1,opts)
293
+ bstart = context_start(b1,opts)
294
+ prev = nil
295
+ if hunk
296
+ if astart < hunk[:end_a] + opts[:context] + 1
297
+ prev = hunk
298
+ astart = hunk[:end_a]
299
+ bstart = hunk[:end_b]
300
+ else
301
+ yield_hunk(hunk, header, l1, delta, opts) {|x| return_hunks << x}
302
+
303
+ header = nil
304
+ end
305
+ end
306
+ # move this inside previous nested if statements
307
+ if prev
308
+ hunk[:end_a] = a2
309
+ hunk[:end_b] = b2
310
+ delta = hunk[:delta]
311
+ else
312
+ hunk = {:start_a => astart, :end_a => a2, :start_b => bstart, :end_b => b2, :delta => delta}
313
+ end
314
+
315
+ hunk[:delta] += l1[astart..(a1-1)].map {|x| ' ' + x } if a1 > 0
316
+ hunk[:delta] += old.map {|x| remove_line(x, opts) }
317
+ hunk[:delta] += newb.map {|x| add_line(x, opts) }
318
+
319
+ end
320
+ saved_delta += delta
321
+
322
+ yield_hunk(hunk, header, l1, saved_delta, opts) {|x| return_hunks << x} if hunk
323
+ return_hunks
324
+ end
325
+
326
+ ##
327
+ # Unpacks a binary-compressed patch.
328
+ #
329
+ # @param [String] binary the packed binary text to unpack
330
+ def patch_text(binary)
331
+ pos = 0
332
+ t = []
333
+ while pos < binary.size
334
+ p1, p2, l = binary[pos..(pos+11)].unpack("NNN")
335
+ pos += 12
336
+ t << binary[pos..(pos + l - 1)]
337
+ pos += l
338
+ end
339
+ t.join
340
+ end
341
+
342
+ ##
343
+ # Applies the patch _bin_ to the text _a_.
344
+ #
345
+ # @param [String] a the text to patch
346
+ # @param [String] bin the binary patch to apply
347
+ def patch(a, bin)
348
+ MercurialPatch.apply_patches(a, [bin])
349
+ end
350
+
351
+ ##
352
+ # Gets the matching blocks between the two texts.
353
+ #
354
+ # @param [String] a the original text
355
+ # @param [String] b the final text
356
+ # @return [[Hash]] The blocks of changes between the two
357
+ def get_matching_blocks(a, b)
358
+ an = a.split_lines_better
359
+ bn = b.split_lines_better
360
+
361
+ SequenceMatcher.new(an, bn).get_matching_blocks
362
+ end
363
+
364
+ ##
365
+ # Returns the obvious header for when we create a new file
366
+ #
367
+ # @param [Fixnum] length the length of the file
368
+ # @return [String] the obvious header
369
+ def trivial_diff_header(length)
370
+ [0, 0, length].pack("NNN")
371
+ end
372
+
373
+ ##
374
+ # Returns a text diff between a and b. This returns the packed, binary
375
+ # kind of diff.
376
+ def text_diff a,b
377
+ BinaryDiff.bdiff a,b
378
+ end
379
+ end
380
+ end
381
+ end
382
+ end
@@ -0,0 +1 @@
1
+ amp_c_extension 'amp/mercurial_patch/CMercurialPatch', 'pure_ruby/ruby_mercurial_patch'
@@ -0,0 +1,294 @@
1
+ # I have seen few files so poorly organized such as patch.py
2
+ # What. The. Fuck. This is going to take so long to make.
3
+ module Amp
4
+ module Patch
5
+ module Mercurial
6
+
7
+ class PatchError < StandardError; end
8
+ class NoHunkError < PatchError; end
9
+
10
+ class Patch
11
+
12
+ ##
13
+ # The filename of the patch
14
+ attr_accessor :file_name
15
+
16
+ ##
17
+ # The opener used to open the patch
18
+ attr_accessor :opener
19
+
20
+ ##
21
+ # @todo - add comment
22
+ attr_accessor :lines
23
+
24
+ ##
25
+ # does the patch exist?
26
+ attr_accessor :exists
27
+ alias_method :exists?, :exists
28
+
29
+ ##
30
+ # Is the file in the filesystem
31
+ attr_accessor :missing
32
+ alias_method :missing?, :missing
33
+
34
+ ##
35
+ # @todo - add comment
36
+ attr_accessor :hash
37
+
38
+ ##
39
+ # Is this dirty and does it need to be resynced with something?
40
+ attr_accessor :dirty
41
+ alias_method :dirty?, :dirty
42
+
43
+ ##
44
+ # @todo - add comment
45
+ attr_accessor :offset
46
+
47
+ ##
48
+ # has this been printed? (duh)
49
+ attr_accessor :printed
50
+ alias_method :printed?, :printed
51
+
52
+ ##
53
+ # @todo - add comment
54
+ attr_accessor :hunks
55
+
56
+ def initialize(file_name, opener, missing=false)
57
+ @file_name = file_name
58
+ @opener = opener
59
+ @lines = []
60
+ @exists = false
61
+ @hash = {}
62
+ @dirty = false
63
+ @offset = 0
64
+ @rejected = []
65
+ @printed = false
66
+ @hunks = 0
67
+
68
+ ##
69
+ # If the patch is in the filesystem
70
+ # then we should read it and accurately set its existence
71
+ unless @missing = missing
72
+ begin
73
+ readlines!
74
+ @exists = true
75
+ rescue IOError
76
+ end
77
+ else
78
+ UI::warn "unable to find '#{@file_name}' for patching"
79
+ end
80
+ end
81
+
82
+ ##
83
+ # Loads up the patch info from +@file_name+
84
+ # into +@lines+
85
+ def readlines!
86
+ @opener.open @file_name do |f|
87
+ @lines = f.readlines
88
+ end
89
+ end
90
+
91
+ ##
92
+ # Mysteriously and safely disappear...
93
+ #
94
+ # @return [Boolean] success marker
95
+ def unlink; File.safe_unlink @file_name; end
96
+
97
+ ##
98
+ # Print out the patch to STDOUT, or STDERR if +warn+ is true.
99
+ #
100
+ # @param [Boolean] warn should we be printing to STDERR?
101
+ def print(warn=false)
102
+ return if printed? # no need to print it twice
103
+
104
+ @printed = true if warn
105
+ message = "patching file #{@file_name}"
106
+ warn ? UI::warn message : UI::note message
107
+ end
108
+
109
+ ##
110
+ # From the Python: looks through the hash and finds candidate lines. The
111
+ # result is a list of line numbers sorted based on distance from linenum.
112
+ #
113
+ # I wish I knew how to make sense of that sentence.
114
+ #
115
+ # @todo Look into removing an unnecessary `- number`.
116
+ # @param [String] line
117
+ # @param [Integer] number the line number
118
+ # @return [Array] the lines that matchish.
119
+ def find_lines(line, number)
120
+ return [] unless @hash.include? line
121
+
122
+ # really, we're just getting the lines and sorting them
123
+ # is the `- number` even necessary?
124
+ @hash[line].sort {|a, b| (a - number).abs <=> (b - number).abs }
125
+ end
126
+
127
+ ##
128
+ # I have no clue what song I am listening to but it is SOOOOO GOOD!!!!!!!!
129
+ # "This time baby, I'll be bullet proof"
130
+ # If I had working internet now, I'd be googling the lyrics.
131
+ #
132
+ # Oh right, the method. I don't know what this does... YET
133
+ #
134
+ # @todo Figure out what this does
135
+ def hash_lines
136
+ @hash = Hash.new {|h, k| h[k] = [] }
137
+ (0 ... @lines.size).each do |x|
138
+ s = @lines[x]
139
+ @hash[s] << x
140
+ end
141
+ end
142
+
143
+ ##
144
+ # our rejects are a little different from patch(1). This always
145
+ # creates rejects in the same form as the original patch. A file
146
+ # header is inserted so that you can run the reject through patch again
147
+ # without having to type the filename.
148
+ def write_rejects
149
+ return if @rejects.empty?
150
+ fname = @file_name + '.rej'
151
+
152
+ UI::warn("#{@rejects.size} out of #{@hunks} hunks FAILED --" +
153
+ "saving rejects to file #{fname}")
154
+
155
+ # i have never written code as horrid as this
156
+ # please help me
157
+ lz = []
158
+ base = File.dirname @file_name
159
+ lz << "--- #{base}\n+++ #{base}\n"
160
+ @rejects.each do |r|
161
+ r.hunk.each do |l|
162
+ lz << l
163
+ lz << "\n\n" if l.last.chr != "\n"
164
+ end
165
+ end
166
+
167
+ write fname, lz, true
168
+ end
169
+
170
+ ##
171
+ # Write +linez+ to +fname+. We won't be doing any writing if
172
+ # nothing has been changed, but this can be overridden with the
173
+ # force parameter.
174
+ #
175
+ # @param [String] dest the filename to write to
176
+ # @param [Array<String>] linez an array of the lines to write
177
+ # @param [Boolean] force force a write
178
+ def write(dest=@file_name, linez=@lines, force=false)
179
+ return unless dirty? || force
180
+
181
+ @opener.open dest, 'w' do |f|
182
+ f.write linez.join("\n")
183
+ end
184
+ end
185
+
186
+ ##
187
+ # A more restrictive version of {write}
188
+ def write_patch
189
+ write @file_name, @lines, true
190
+ end
191
+
192
+ ##
193
+ # Closing rites. Write the patch and then write the rejects.
194
+ def close
195
+ write_patch
196
+ write_rejects
197
+ end
198
+
199
+ ##
200
+ # Apply the current hunk +hunk+. Also, should we reverse the hunk? Consult +reverse+.
201
+ #
202
+ # @param
203
+ # @param
204
+ def apply(hunk, reverse)
205
+ unless hunk.complete?
206
+ raise PatchError.new("bad hunk #%d %s (%d %d %d %d)" %
207
+ [hunk.number, hunk.desc, hunk.a.size,
208
+ hunk.len_a, hunk.b.size, hunk.len_b])
209
+ end
210
+
211
+ @hunks += 1 # It's clear we're adding a new hunk.
212
+
213
+ # Obey reversal rules.
214
+ hunk.reverse if reverse
215
+
216
+ # Does the file already exist? Better tell someone
217
+ UI::warn "file #{@file_name} already exists" if exists? && hunk.create_file?
218
+
219
+ # Is this a misfit?
220
+ (@rejects << hunk; return -1) if missing? || (exists? && hunk.create_file?)
221
+
222
+ # Deal with GitHunks
223
+ if hunk.is_a? GitHunk
224
+ if hunk.remove_file?
225
+ File.safe_unlink @file_name
226
+ else
227
+ @lines = hunk.new
228
+ @offset += hunk.new.size
229
+ @dirty = true
230
+ end
231
+
232
+ return 0
233
+ end
234
+
235
+ # fast case first, no offsets, no fuzz
236
+ old = hunk.old
237
+
238
+ # patch starts counting at 1 unless we are adding the file
239
+ start = hunk.start_a == 0 ? 0 : h.start_a + @offset - 1
240
+
241
+ orig_start = start
242
+ if DiffHelpers::test_hunk(old, @lines, start) == 0
243
+ if hunk.remove_file?
244
+ File.safe_unlink @file_name
245
+ else
246
+ @lines[start .. (start + hunk.len_a)] = hunk.new
247
+ @offset += hunk.len_b - hunk.len_a
248
+ @dirty = true
249
+ end
250
+
251
+ return 0
252
+ end # end if
253
+ end # end def
254
+
255
+ # Ok, We couldn't match the hunk. Let's look for offsets and fuzz it
256
+ # as well as use proper punctuation for the 'let us' contraction.
257
+ hash_lines
258
+
259
+ # if the hunk tried to put something at the bottom of the file
260
+ # then override the start line and use eof here
261
+ search_start = hunk[-1][0].chr != ' ' ? @lines.size : orig_start
262
+
263
+ 0.upto(2) do |fuzz_len|
264
+ [true, false].each do |top_only|
265
+ old = hunk.old fuzz_len, top_only
266
+ # Continue at patch.py:407
267
+ # ...
268
+ # ...
269
+ end
270
+ end # end upto
271
+
272
+ end # end class Patch
273
+
274
+ class PatchMeta
275
+ end
276
+
277
+ class Hunk
278
+ end
279
+
280
+ class GitHunk
281
+ end
282
+
283
+ class BinaryHunk
284
+ end
285
+
286
+ class SymLinkHunk
287
+ end
288
+
289
+ class LineReader
290
+ end
291
+
292
+ end
293
+ end
294
+ end