Module: Amp::Diffs::MercurialDiff
- Extended by:
- MercurialDiff
- Included in:
- MercurialDiff
- Defined in:
- lib/amp/encoding/mercurial_diff.rb
Overview
MercurialDiff
Mercurial has it’s own implementation of the unified diff, because windows boxes don’t have diff -u. Plus code is faster than the shell. Lame. That’s ok, it’s pretty easy to do. And we can also add flags and change default settings.
Mainly, you’re only going to use MercurialDiff.unified_diff(). It’s usage is described below.
Constant Summary collapse
- DEFAULT_OPTIONS =
These are the default options you can modify. Grab them, clone them, change them. Notice: You have to clone this when you use it, or you will be changing the default options!
{:context => 3, :text => false, :show_func => false, :git => false, :no_dates => false, :ignore_ws => false, :ignore_ws_amount => false, :ignore_blank_lines => false, :pretty => false}
Instance Method Summary collapse
-
#add_line(input, options) ⇒ String
Given a line, returns a string that represents “adding that line” in a diff, based on the options.
-
#bunidiff(t1, t2, l1, l2, header1, header2, opts = DEFAULT_OPTIONS) ⇒ Object
Helper method for creating unified diffs.
-
#context_end(l, len, options) ⇒ Object
Starts a block ending context for a change - part of the unified diff format.
-
#context_start(l, options) ⇒ Object
Starts a block starting context for a change - part of the unified diff format.
-
#date_tag(date, fn1, addtab = true, options = DEFAULT_OPTIONS) ⇒ Object
Creates a date tag appropriate for diffs.
-
#diff_line(revisions, a, b, options = DEFAULT_OPTIONS) ⇒ Object
Creates a header or something? Not sure what this is used for, no code references it.
-
#get_matching_blocks(a, b) ⇒ [Hash]
Gets the matching blocks between the two texts.
-
#patch(a, bin) ⇒ Object
Applies the patch bin to the text a.
-
#patch_text(binary) ⇒ Object
Unpacks a binary-compressed patch.
-
#remove_line(input, options) ⇒ String
Given a line, returns a string that represents “removing that line” in a diff, based on the options.
-
#text_diff(a, b) ⇒ Object
Returns a text diff between a and b.
-
#trivial_diff_header(length) ⇒ String
Returns the obvious header for when we create a new file.
-
#unified_diff(a, ad, b, bd, fn1, fn2, r = nil, options = DEFAULT_OPTIONS) ⇒ Object
Returns a unified diff based on the 2 blocks of text, their modification times, their filenames, and the options.
-
#whitespace_clean(text, options = DEFAULT_OPTIONS) ⇒ Object
Clear up whitespace in the text if we have any options relating to getting rid of whitespace.
-
#yield_hunk(hunk, header, l1, delta, options) ⇒ Object
Given a hunk of changes, yield each line we need to write to the diff.
Instance Method Details
#add_line(input, options) ⇒ String
Given a line, returns a string that represents “adding that line” in a diff, based on the options.
53 54 55 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 53 def add_line(input, ) [:pretty] ? "+#{input.chomp}".green+"\n" : "+#{input}" end |
#bunidiff(t1, t2, l1, l2, header1, header2, opts = DEFAULT_OPTIONS) ⇒ Object
Helper method for creating unified diffs.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 263 def bunidiff(t1,t2, l1, l2, header1, header2, opts=DEFAULT_OPTIONS) header = [ "--- #{header1}\t\n", "+++ #{header2}\t\n" ] diff = BinaryDiff.blocks(t1,t2) hunk = nil return_hunks = [] saved_delta = [] delta = [] diff.size.times do |i| s = (i > 0) ? diff[i-1] : {:start_a => 0, :end_a => 0, :start_b => 0, :end_b => 0} saved_delta += delta unless delta.empty? delta = [] s1 = diff[i] a1 = s[:end_a] a2 = s1[:start_a] b1 = s[:end_b] b2 = s1[:start_b] old = (a2 == 0) ? [] : l1[a1..(a2-1)] newb = (b2 == 0) ? [] : l2[b1..(b2-1)] #stands for new "b" next if old.empty? && newb.empty? if opts[:ignore_ws] || opts[:ignore_blank_lines] || opts[:ignore_ws_amount] next if whitespace_clean(old.join,opts) == whitespace_clean(newb.join,opts) end astart = context_start(a1,opts) bstart = context_start(b1,opts) prev = nil if hunk if astart < hunk[:end_a] + opts[:context] + 1 prev = hunk astart = hunk[:end_a] bstart = hunk[:end_b] else yield_hunk(hunk, header, l1, delta, opts) {|x| return_hunks << x} header = nil end end # move this inside previous nested if statements if prev hunk[:end_a] = a2 hunk[:end_b] = b2 delta = hunk[:delta] else hunk = {:start_a => astart, :end_a => a2, :start_b => bstart, :end_b => b2, :delta => delta} end hunk[:delta] += l1[astart..(a1-1)].map {|x| ' ' + x } if a1 > 0 hunk[:delta] += old.map {|x| remove_line(x, opts) } hunk[:delta] += newb.map {|x| add_line(x, opts) } end saved_delta += delta yield_hunk(hunk, header, l1, saved_delta, opts) {|x| return_hunks << x} if hunk return_hunks end |
#context_end(l, len, options) ⇒ Object
Starts a block ending context for a change - part of the unified diff format.
195 196 197 198 199 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 195 def context_end(l, len, ) ret = l + [:context] ret = len if ret > len ret end |
#context_start(l, options) ⇒ Object
Starts a block starting context for a change - part of the unified diff format.
204 205 206 207 208 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 204 def context_start(l, ) ret = l - [:context] return 0 if ret < 0 ret end |
#date_tag(date, fn1, addtab = true, options = DEFAULT_OPTIONS) ⇒ Object
Creates a date tag appropriate for diffs. Not all diff types use dates though (namely git, apparently), so the options matter.
98 99 100 101 102 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 98 def date_tag(date, fn1, addtab = true, = DEFAULT_OPTIONS) return "\t#{date.to_diff}\n" if !([:git]) && !([:nodates]) return "\t\n" if addtab && fn1 =~ / / return "\n" end |
#diff_line(revisions, a, b, options = DEFAULT_OPTIONS) ⇒ Object
Creates a header or something? Not sure what this is used for, no code references it. I think it’s for git or something. eh.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 70 def diff_line(revisions, a, b, =DEFAULT_OPTIONS) = DEFAULT_OPTIONS.merge parts = ['diff'] parts << '--git' if [:git] parts << revisions.map {|r| "-r #{r}"}.join(' ') if revisions && ![:git] if [:git] parts << "a/#{a}" parts << "b/#{b}" else parts << a end parts.join(' ') + "\n" end |
#get_matching_blocks(a, b) ⇒ [Hash]
Gets the matching blocks between the two texts.
354 355 356 357 358 359 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 354 def get_matching_blocks(a, b) an = a.split_lines_better bn = b.split_lines_better SequenceMatcher.new(an, bn).get_matching_blocks end |
#patch(a, bin) ⇒ Object
Applies the patch bin to the text a.
344 345 346 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 344 def patch(a, bin) MercurialPatch.apply_patches(a, [bin]) end |
#patch_text(binary) ⇒ Object
Unpacks a binary-compressed patch.
327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 327 def patch_text(binary) pos = 0 t = [] while pos < binary.size p1, p2, l = binary[pos..(pos+11)].unpack("NNN") pos += 12 t << binary[pos..(pos + l - 1)] pos += l end t.join end |
#remove_line(input, options) ⇒ String
Given a line, returns a string that represents “removing that line” in a diff, based on the options.
63 64 65 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 63 def remove_line(input, ) [:pretty] ? "-#{input.chomp}".red+"\n" : "-#{input}" end |
#text_diff(a, b) ⇒ Object
Returns a text diff between a and b. This returns the packed, binary kind of diff.
373 374 375 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 373 def text_diff a,b BinaryDiff.bdiff a,b end |
#trivial_diff_header(length) ⇒ String
Returns the obvious header for when we create a new file
366 367 368 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 366 def trivial_diff_header(length) [0, 0, length].pack("NNN") end |
#unified_diff(a, ad, b, bd, fn1, fn2, r = nil, options = DEFAULT_OPTIONS) ⇒ Object
Returns a unified diff based on the 2 blocks of text, their modification times, their filenames, and the options.
This is a self-contained replacement for diffs.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 119 def unified_diff(a, ad, b, bd, fn1, fn2, r=nil, =DEFAULT_OPTIONS) return "" if (a.nil? || a.empty?) && (b.nil? || b.empty?) epoch = Time.at(0) if ![:text] && (!a.nil? && a.binary? || !b.nil? && b.binary?) return "" if a.any? && b.any? && a.size == b.size && a == b #DERR l = ["Binary file #{fn1} has changed\n"] elsif a.nil? || a.empty? b = b.split_lines_better header = [] if [:pretty] l1 = a.nil? ? "Added file " : "Changed file " l1 += "#{fn2} at #{date_tag(bd,fn1,true,)}" l1 = l1.cyan header << l1 else if a.nil? header << "--- /dev/null#{date_tag(epoch, fn1, false, )}" else header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,)}" end header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,)}" header << "@@ -0,0 +1,#{b.size} @@\n" end l = header + (b.map {|line| add_line(line, )}) elsif b.nil? || b.empty? a = b.split_lines_better header = [] if [:pretty] l1 = b.nil? ? "Removed file " : "Changed file " l1 += "#{fn2} at #{date_tag(bd,fn1,true,)}" l1 = l1.cyan header << l1 else header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,)}" if b.nil? header << "+++ /dev/null#{date_tag(epoch, fn1, false, )}" else header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,)}" end header << "@@ -1,#{a.size} +0,0 @@\n" end l = header + (a.map {|line| remove_line(line, )}) else al = a.split_lines_better bl = b.split_lines_better l = bunidiff(a, b, al, bl, "a/"+fn1, "b/"+fn2, ) return "" if l.nil? || l.empty? if [:pretty] l.shift if fn1 == fn2 l[0] = "Changed file #{fn1.cyan} at #{date_tag(bd,fn1,true,).lstrip}" else l[0] = "Moved file from #{fn1.cyan} to #{fn2.cyan}" end else l[0] = "#{l[0][0 .. -3]}#{date_tag(ad,fn1,true,)}" l[1] = "#{l[1][0 .. -3]}#{date_tag(bd,fn1,true,)}" end end l.size.times do |ln| if l[ln][-1,1] != "\n" l[ln] << "\n\\ No newline at end of file\n" end end if r l.unshift diff_line(r, fn1, fn2, ) end l.join end |
#whitespace_clean(text, options = DEFAULT_OPTIONS) ⇒ Object
Clear up whitespace in the text if we have any options relating to getting rid of whitespace.
36 37 38 39 40 41 42 43 44 45 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 36 def whitespace_clean(text, =DEFAULT_OPTIONS) if [:ignore_ws] text.gsub!(/[ \t]+/, "") #warnings made me use parens elsif [:ignore_ws_amount] text.gsub!(/[ \t]+/, ' ') text.gsub!(/[ \t]+\n/, "\n") end text.gsub!(/\n+/, '') if [:ignore_blank_lines] text end |
#yield_hunk(hunk, header, l1, delta, options) ⇒ Object
Given a hunk of changes, yield each line we need to write to the diff.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/amp/encoding/mercurial_diff.rb', line 219 def yield_hunk(hunk, header, l1, delta, ) header.each {|x| yield x} if header && header.any? delta = hunk[:delta] astart, a2, bstart, b2 = hunk[:start_a], hunk[:end_a], hunk[:start_b], hunk[:end_b] aend = context_end(a2,l1.size,) alen = aend - astart blen = b2 - bstart + aend - a2 # i seriously don't know what this does. func = "" if [:show_func] (astart - 1).downto(0) do |x| t = l1[x].rstrip if t =~ /\w/ func = ' ' + t[0 .. 39] break end end end # yield the header if [:pretty] yield "From original lines #{astart + 1}-#{alen+astart+1}".yellow + "\n" else yield "@@ -%d,%d +%d,%d @@%s\n" % [astart + 1, alen, bstart + 1, blen, func] end # then yield each line of changes delta.each {|x| yield x} # then yield some context or something? a2.upto(aend-1) {|x| yield ' ' + l1[x] } end |