Applying diff-patch to a line / file

For a standalone smartphone app, I create one-way text synchronization for Xml files. I would like my server to send delta / difference (e.g. GN-diff-patch) to the target device.

This is the plan:

Time = 0
Server: has version_1 of Xml file (~800 kiB)
Client: has version_1 of Xml file (~800 kiB)

Time = 1
Server: has version_1 and version_2 of Xml file (each ~800 kiB)
        computes delta of these versions (=patch) (~10 kiB) 
        sends patch to Client (~10 kiB transferred)

Client: computes version_2 from version_1 and patch  <= this is the problem =>

Is there a Ruby library that can take this last step to apply a text patch to files / lines? The patch can be formatted according to the requirements of the library.

Thank you for your help!

(I use the Rhodes Cross-Platform, which uses Ruby as a programming language.)

+5
source share
2 answers

- . (IMHO) : ed (1) script. /usr/bin/diff -e old.xml new.xml ; diff (1) , . ed :

36a
    <tr><td class="eg" style="background: #182349;">&nbsp;</td><td><tt>#182349</tt></td></tr>
.
34c
    <tr><td class="eg" style="background: #66ccff;">&nbsp;</td><td><tt>#xxxxxx</tt></td></tr>
.
20,23d

- , . :

  • a: .
  • c: . d, a.
  • d: .

, , , . , , , (.. /^\.$/ patch_line == '.' ). , :

[line-number-range][command]
[optional-argument-lines...]
[dot-terminator-if-there-are-arguments]

, ed, ( ), , Array # insert, Array # delete_at, , Ruby , .

XML, :

<tag>
blah blah
</tag>
<other-tag x="y">
mumble mumble
</other>

:

<tag>blah blah</tag><other-tag x="y">mumble mumble</other>

- ; EOL , .

Ruby (google "ruby algorithm:: diff" ). diff XML , , , . , ed ( , ), .

+6

, , . , Ruby, , , . , .

, Linux, diff Cygwin. Diffy gem ed script:

patch_text = Diffy::Diff.new(old_text, new_text, :diff => "-e").to_s

. , , , , . 200_success answer, , .

require 'stringio'
def self.apply_patch(old_text, patch)
  text = old_text.split("\n")
  patch = StringIO.new(patch)
  current_line = 1

  while patch_line = patch.gets
    # Grab the command
    m = %r{\A(?:(\d+))?(?:,(\d+))?([acd]|s/\.//)\Z}.match(patch_line)
    raise ArgumentError.new("Invalid ed command: #{patch_line.chomp}") if m.nil?
    first_line = (m[1] || current_line).to_i
    last_line = (m[2] || first_line).to_i
    command = m[3]

    case command
    when "s/.//"
      (first_line..last_line).each { |i| text[i - 1].sub!(/./, '') }
    else
      if ['d', 'c'].include?(command)
        text[first_line - 1 .. last_line - 1] = []
      end
      if ['a', 'c'].include?(command)
        current_line = first_line - (command=='a' ? 0 : 1) # Adds are 0-indexed, but Changes and Deletes are 1-indexed
        while (patch_line = patch.gets) && (patch_line.chomp! != '.') && (patch_line != '.')
          text.insert(current_line, patch_line)
          current_line += 1
        end
      end
    end
  end
  text.join("\n")
end
+2

All Articles