amp 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. data/.gitignore +12 -0
  2. data/.hgignore +3 -0
  3. data/AUTHORS +1 -1
  4. data/Manifest.txt +99 -38
  5. data/README.md +3 -3
  6. data/Rakefile +53 -18
  7. data/SCHEDULE.markdown +5 -1
  8. data/TODO.markdown +120 -149
  9. data/ampfile.rb +3 -1
  10. data/bin/amp +4 -1
  11. data/ext/amp/bz2/extconf.rb +1 -1
  12. data/ext/amp/mercurial_patch/extconf.rb +1 -1
  13. data/ext/amp/mercurial_patch/mpatch.c +4 -3
  14. data/ext/amp/priority_queue/extconf.rb +1 -1
  15. data/ext/amp/support/extconf.rb +1 -1
  16. data/ext/amp/support/support.c +1 -1
  17. data/lib/amp.rb +125 -67
  18. data/lib/amp/commands/command.rb +12 -10
  19. data/lib/amp/commands/command_support.rb +8 -1
  20. data/lib/amp/commands/commands/help.rb +2 -20
  21. data/lib/amp/commands/commands/init.rb +14 -2
  22. data/lib/amp/commands/commands/templates.rb +6 -4
  23. data/lib/amp/commands/commands/version.rb +15 -1
  24. data/lib/amp/commands/commands/workflow.rb +3 -3
  25. data/lib/amp/commands/commands/workflows/git/add.rb +3 -3
  26. data/lib/amp/commands/commands/workflows/git/copy.rb +1 -1
  27. data/lib/amp/commands/commands/workflows/git/rm.rb +4 -2
  28. data/lib/amp/commands/commands/workflows/hg/add.rb +1 -1
  29. data/lib/amp/commands/commands/workflows/hg/addremove.rb +2 -2
  30. data/lib/amp/commands/commands/workflows/hg/annotate.rb +8 -2
  31. data/lib/amp/commands/commands/workflows/hg/bisect.rb +253 -0
  32. data/lib/amp/commands/commands/workflows/hg/branch.rb +1 -1
  33. data/lib/amp/commands/commands/workflows/hg/branches.rb +3 -3
  34. data/lib/amp/commands/commands/workflows/hg/bundle.rb +3 -3
  35. data/lib/amp/commands/commands/workflows/hg/clone.rb +4 -5
  36. data/lib/amp/commands/commands/workflows/hg/commit.rb +37 -1
  37. data/lib/amp/commands/commands/workflows/hg/copy.rb +2 -1
  38. data/lib/amp/commands/commands/workflows/hg/debug/index.rb +1 -1
  39. data/lib/amp/commands/commands/workflows/hg/diff.rb +3 -8
  40. data/lib/amp/commands/commands/workflows/hg/forget.rb +5 -4
  41. data/lib/amp/commands/commands/workflows/hg/identify.rb +6 -6
  42. data/lib/amp/commands/commands/workflows/hg/import.rb +1 -1
  43. data/lib/amp/commands/commands/workflows/hg/incoming.rb +2 -2
  44. data/lib/amp/commands/commands/workflows/hg/log.rb +5 -4
  45. data/lib/amp/commands/commands/workflows/hg/merge.rb +1 -1
  46. data/lib/amp/commands/commands/workflows/hg/move.rb +5 -3
  47. data/lib/amp/commands/commands/workflows/hg/outgoing.rb +1 -1
  48. data/lib/amp/commands/commands/workflows/hg/push.rb +6 -7
  49. data/lib/amp/commands/commands/workflows/hg/remove.rb +2 -2
  50. data/lib/amp/commands/commands/workflows/hg/resolve.rb +6 -23
  51. data/lib/amp/commands/commands/workflows/hg/root.rb +1 -2
  52. data/lib/amp/commands/commands/workflows/hg/status.rb +21 -12
  53. data/lib/amp/commands/commands/workflows/hg/tag.rb +2 -2
  54. data/lib/amp/commands/commands/workflows/hg/untrack.rb +12 -0
  55. data/lib/amp/commands/commands/workflows/hg/verify.rb +13 -3
  56. data/lib/amp/commands/commands/workflows/hg/what_changed.rb +18 -0
  57. data/lib/amp/commands/dispatch.rb +12 -13
  58. data/lib/amp/dependencies/amp_support.rb +1 -1
  59. data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +1 -0
  60. data/lib/amp/dependencies/maruku.rb +136 -0
  61. data/lib/amp/dependencies/maruku/attributes.rb +227 -0
  62. data/lib/amp/dependencies/maruku/defaults.rb +71 -0
  63. data/lib/amp/dependencies/maruku/errors_management.rb +92 -0
  64. data/lib/amp/dependencies/maruku/helpers.rb +260 -0
  65. data/lib/amp/dependencies/maruku/input/charsource.rb +326 -0
  66. data/lib/amp/dependencies/maruku/input/extensions.rb +69 -0
  67. data/lib/amp/dependencies/maruku/input/html_helper.rb +189 -0
  68. data/lib/amp/dependencies/maruku/input/linesource.rb +111 -0
  69. data/lib/amp/dependencies/maruku/input/parse_block.rb +615 -0
  70. data/lib/amp/dependencies/maruku/input/parse_doc.rb +234 -0
  71. data/lib/amp/dependencies/maruku/input/parse_span_better.rb +746 -0
  72. data/lib/amp/dependencies/maruku/input/rubypants.rb +225 -0
  73. data/lib/amp/dependencies/maruku/input/type_detection.rb +147 -0
  74. data/lib/amp/dependencies/maruku/input_textile2/t2_parser.rb +163 -0
  75. data/lib/amp/dependencies/maruku/maruku.rb +33 -0
  76. data/lib/amp/dependencies/maruku/output/to_ansi.rb +223 -0
  77. data/lib/amp/dependencies/maruku/output/to_html.rb +991 -0
  78. data/lib/amp/dependencies/maruku/output/to_markdown.rb +164 -0
  79. data/lib/amp/dependencies/maruku/output/to_s.rb +56 -0
  80. data/lib/amp/dependencies/maruku/string_utils.rb +191 -0
  81. data/lib/amp/dependencies/maruku/structures.rb +167 -0
  82. data/lib/amp/dependencies/maruku/structures_inspect.rb +87 -0
  83. data/lib/amp/dependencies/maruku/structures_iterators.rb +61 -0
  84. data/lib/amp/dependencies/maruku/textile2.rb +1 -0
  85. data/lib/amp/dependencies/maruku/toc.rb +199 -0
  86. data/lib/amp/dependencies/maruku/usage/example1.rb +33 -0
  87. data/lib/amp/dependencies/maruku/version.rb +40 -0
  88. data/lib/amp/dependencies/priority_queue.rb +2 -1
  89. data/lib/amp/dependencies/python_config.rb +2 -1
  90. data/lib/amp/graphs/ancestor.rb +2 -1
  91. data/lib/amp/graphs/copies.rb +236 -233
  92. data/lib/amp/help/entries/__default__.erb +31 -0
  93. data/lib/amp/help/entries/commands.erb +6 -0
  94. data/lib/amp/help/entries/mdtest.md +35 -0
  95. data/lib/amp/help/entries/silly +3 -0
  96. data/lib/amp/help/help.rb +288 -0
  97. data/lib/amp/profiling_hacks.rb +5 -3
  98. data/lib/amp/repository/abstract/abstract_changeset.rb +97 -0
  99. data/lib/amp/repository/abstract/abstract_local_repo.rb +181 -0
  100. data/lib/amp/repository/abstract/abstract_staging_area.rb +180 -0
  101. data/lib/amp/repository/abstract/abstract_versioned_file.rb +100 -0
  102. data/lib/amp/repository/abstract/common_methods/changeset.rb +75 -0
  103. data/lib/amp/repository/abstract/common_methods/local_repo.rb +277 -0
  104. data/lib/amp/repository/abstract/common_methods/staging_area.rb +233 -0
  105. data/lib/amp/repository/abstract/common_methods/versioned_file.rb +71 -0
  106. data/lib/amp/repository/generic_repo_picker.rb +78 -0
  107. data/lib/amp/repository/git/repo_format/changeset.rb +336 -0
  108. data/lib/amp/repository/git/repo_format/staging_area.rb +192 -0
  109. data/lib/amp/repository/git/repo_format/versioned_file.rb +119 -0
  110. data/lib/amp/repository/git/repositories/local_repository.rb +164 -0
  111. data/lib/amp/repository/git/repository.rb +41 -0
  112. data/lib/amp/repository/mercurial/encoding/mercurial_diff.rb +382 -0
  113. data/lib/amp/repository/mercurial/encoding/mercurial_patch.rb +1 -0
  114. data/lib/amp/repository/mercurial/encoding/patch.rb +294 -0
  115. data/lib/amp/repository/mercurial/encoding/pure_ruby/ruby_mercurial_patch.rb +124 -0
  116. data/lib/amp/repository/mercurial/merging/merge_ui.rb +327 -0
  117. data/lib/amp/repository/mercurial/merging/simple_merge.rb +452 -0
  118. data/lib/amp/repository/mercurial/repo_format/branch_manager.rb +266 -0
  119. data/lib/amp/repository/mercurial/repo_format/changeset.rb +768 -0
  120. data/lib/amp/repository/mercurial/repo_format/dir_state.rb +716 -0
  121. data/lib/amp/repository/mercurial/repo_format/journal.rb +218 -0
  122. data/lib/amp/repository/mercurial/repo_format/lock.rb +210 -0
  123. data/lib/amp/repository/mercurial/repo_format/merge_state.rb +228 -0
  124. data/lib/amp/repository/mercurial/repo_format/staging_area.rb +367 -0
  125. data/lib/amp/repository/mercurial/repo_format/store.rb +487 -0
  126. data/lib/amp/repository/mercurial/repo_format/tag_manager.rb +322 -0
  127. data/lib/amp/repository/mercurial/repo_format/updatable.rb +543 -0
  128. data/lib/amp/repository/mercurial/repo_format/updater.rb +848 -0
  129. data/lib/amp/repository/mercurial/repo_format/verification.rb +433 -0
  130. data/lib/amp/repository/mercurial/repositories/bundle_repository.rb +216 -0
  131. data/lib/amp/repository/mercurial/repositories/http_repository.rb +386 -0
  132. data/lib/amp/repository/mercurial/repositories/local_repository.rb +2034 -0
  133. data/lib/amp/repository/mercurial/repository.rb +119 -0
  134. data/lib/amp/repository/mercurial/revlogs/bundle_revlogs.rb +249 -0
  135. data/lib/amp/repository/mercurial/revlogs/changegroup.rb +217 -0
  136. data/lib/amp/repository/mercurial/revlogs/changelog.rb +339 -0
  137. data/lib/amp/repository/mercurial/revlogs/file_log.rb +152 -0
  138. data/lib/amp/repository/mercurial/revlogs/index.rb +500 -0
  139. data/lib/amp/repository/mercurial/revlogs/manifest.rb +201 -0
  140. data/lib/amp/repository/mercurial/revlogs/node.rb +20 -0
  141. data/lib/amp/repository/mercurial/revlogs/revlog.rb +1026 -0
  142. data/lib/amp/repository/mercurial/revlogs/revlog_support.rb +129 -0
  143. data/lib/amp/repository/mercurial/revlogs/versioned_file.rb +597 -0
  144. data/lib/amp/repository/repository.rb +11 -88
  145. data/lib/amp/server/extension/amp_extension.rb +3 -3
  146. data/lib/amp/server/fancy_http_server.rb +1 -1
  147. data/lib/amp/server/fancy_views/_browser.haml +1 -1
  148. data/lib/amp/server/fancy_views/_diff_file.haml +1 -8
  149. data/lib/amp/server/fancy_views/changeset.haml +2 -2
  150. data/lib/amp/server/fancy_views/file.haml +1 -1
  151. data/lib/amp/server/fancy_views/file_diff.haml +1 -1
  152. data/lib/amp/support/amp_ui.rb +13 -29
  153. data/lib/amp/support/generator.rb +1 -1
  154. data/lib/amp/support/loaders.rb +1 -2
  155. data/lib/amp/support/logger.rb +10 -16
  156. data/lib/amp/support/match.rb +18 -4
  157. data/lib/amp/support/mercurial/ignore.rb +151 -0
  158. data/lib/amp/support/openers.rb +8 -3
  159. data/lib/amp/support/support.rb +91 -46
  160. data/lib/amp/templates/{blank.commit.erb → mercurial/blank.commit.erb} +0 -0
  161. data/lib/amp/templates/{blank.log.erb → mercurial/blank.log.erb} +0 -0
  162. data/lib/amp/templates/{default.commit.erb → mercurial/default.commit.erb} +0 -0
  163. data/lib/amp/templates/{default.log.erb → mercurial/default.log.erb} +0 -0
  164. data/lib/amp/templates/template.rb +18 -18
  165. data/man/amp.1 +51 -0
  166. data/site/src/about/commands.haml +1 -1
  167. data/site/src/css/amp.css +1 -1
  168. data/site/src/index.haml +3 -3
  169. data/tasks/man.rake +39 -0
  170. data/tasks/stats.rake +1 -10
  171. data/tasks/yard.rake +1 -50
  172. data/test/dirstate_tests/test_dir_state.rb +10 -8
  173. data/test/functional_tests/annotate.out +31 -0
  174. data/test/functional_tests/test_functional.rb +155 -63
  175. data/test/localrepo_tests/ampfile.rb +12 -0
  176. data/test/localrepo_tests/test_local_repo.rb +56 -57
  177. data/test/manifest_tests/test_manifest.rb +3 -5
  178. data/test/merge_tests/test_merge.rb +3 -3
  179. data/test/revlog_tests/test_revlog.rb +14 -6
  180. data/test/store_tests/test_fncache_store.rb +19 -19
  181. data/test/test_19_compatibility.rb +46 -0
  182. data/test/test_base85.rb +2 -2
  183. data/test/test_bdiff.rb +2 -2
  184. data/test/test_changegroup.rb +59 -0
  185. data/test/test_commands.rb +2 -2
  186. data/test/test_difflib.rb +2 -2
  187. data/test/test_generator.rb +34 -0
  188. data/test/test_ignore.rb +203 -0
  189. data/test/test_journal.rb +18 -13
  190. data/test/test_match.rb +2 -2
  191. data/test/test_mdiff.rb +3 -3
  192. data/test/test_mpatch.rb +3 -3
  193. data/test/test_multi_io.rb +40 -0
  194. data/test/test_support.rb +18 -2
  195. data/test/test_templates.rb +38 -0
  196. data/test/test_ui.rb +79 -0
  197. data/test/testutilities.rb +56 -0
  198. metadata +168 -49
  199. data/ext/amp/bz2/mkmf.log +0 -38
  200. data/lib/amp/encoding/mercurial_diff.rb +0 -378
  201. data/lib/amp/encoding/mercurial_patch.rb +0 -1
  202. data/lib/amp/encoding/patch.rb +0 -292
  203. data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +0 -123
  204. data/lib/amp/merges/merge_state.rb +0 -164
  205. data/lib/amp/merges/merge_ui.rb +0 -322
  206. data/lib/amp/merges/simple_merge.rb +0 -450
  207. data/lib/amp/repository/branch_manager.rb +0 -234
  208. data/lib/amp/repository/dir_state.rb +0 -950
  209. data/lib/amp/repository/journal.rb +0 -203
  210. data/lib/amp/repository/lock.rb +0 -207
  211. data/lib/amp/repository/repositories/bundle_repository.rb +0 -214
  212. data/lib/amp/repository/repositories/http_repository.rb +0 -377
  213. data/lib/amp/repository/repositories/local_repository.rb +0 -2661
  214. data/lib/amp/repository/store.rb +0 -485
  215. data/lib/amp/repository/tag_manager.rb +0 -319
  216. data/lib/amp/repository/updatable.rb +0 -532
  217. data/lib/amp/repository/verification.rb +0 -431
  218. data/lib/amp/repository/versioned_file.rb +0 -475
  219. data/lib/amp/revlogs/bundle_revlogs.rb +0 -246
  220. data/lib/amp/revlogs/changegroup.rb +0 -217
  221. data/lib/amp/revlogs/changelog.rb +0 -338
  222. data/lib/amp/revlogs/changeset.rb +0 -521
  223. data/lib/amp/revlogs/file_log.rb +0 -165
  224. data/lib/amp/revlogs/index.rb +0 -493
  225. data/lib/amp/revlogs/manifest.rb +0 -195
  226. data/lib/amp/revlogs/node.rb +0 -18
  227. data/lib/amp/revlogs/revlog.rb +0 -1045
  228. data/lib/amp/revlogs/revlog_support.rb +0 -126
  229. data/lib/amp/support/ignore.rb +0 -144
  230. data/site/Rakefile +0 -38
  231. data/test/test_amp.rb +0 -9
  232. data/test/test_helper.rb +0 -15
@@ -0,0 +1,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