kdiff3 0.9.1

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.
@@ -0,0 +1,52 @@
1
+ KDiff3
2
+ ========
3
+
4
+ [![Gem Version](http://img.shields.io/gem/v/KDiff3.svg?style=flat-square)](http://badge.fury.io/rb/KDiff3)
5
+ [![Build Status](http://img.shields.io/travis/NullVoxPopuli/kdiff3-rb.svg?style=flat-square)](https://travis-ci.org/NullVoxPopuli/kdiff3-rb)
6
+ [![Code Climate](http://img.shields.io/codeclimate/github/NullVoxPopuli/kdiff3-rb.svg?style=flat-square)](https://codeclimate.com/github/NullVoxPopuli/kdiff3-rb)
7
+ [![Test Coverage](http://img.shields.io/codeclimate/coverage/github/NullVoxPopuli/kdiff3-rb.svg?style=flat-square)](https://codeclimate.com/github/NullVoxPopuli/kdiff3-rb)
8
+ [![Dependency Status](http://img.shields.io/gemnasium/NullVoxPopuli/kdiff3-rb.svg?style=flat-square)](https://gemnasium.com/NullVoxPopuli/kdiff3-rb)
9
+ [![security](https://hakiri.io/github/NullVoxPopuli/kdiff3-rb/master.svg)](https://hakiri.io/github/NullVoxPopuli/kdiff3-rb/master)
10
+
11
+ Provides a simple way to utilize the power of [kdiff3 by Joachim Eibl](http://kdiff3.sourceforge.net/) in ruby.
12
+
13
+ ## Installation
14
+
15
+ gem install KDiff3
16
+
17
+ or
18
+
19
+ gem 'KDiff3' # in your Gemfile
20
+
21
+ ## Examples
22
+
23
+ Merging inline HTML
24
+
25
+ result = KDiff3.merge(
26
+ base: "<p>1,2,<p>1,<p>2,</p></p>3</p>",
27
+ yours: "<p>1,2,<p>1,</p>3</p>",
28
+ theirs: "<p>1,2,<p><p>2,</p></p>3</p>",
29
+ html: true
30
+ )
31
+
32
+ # => "<p>1,2,<p></p>3</p>"
33
+
34
+ Merging files
35
+
36
+ result = KDiff3.merge(
37
+ base: "path/to/base",
38
+ yours: "path/to/yours",
39
+ theirs: "path/to/theirs"
40
+ )
41
+
42
+ # => (whatever merged output is from the files)
43
+
44
+
45
+ ## Development
46
+
47
+ KDiff3 exists in ext/kdiff3 using a git subtree
48
+ - KDiff3 repo here: https://github.com/NullVoxPopuli/kdiff3
49
+ - More info on subtrees here: https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844
50
+
51
+ ## Contributing
52
+ - Fork, Test, Pull Request :-)
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
Binary file
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "kdiff3/version"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "kdiff3"
9
+ s.version = KDiff3::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.license = "GNU GPL v2"
12
+ s.authors = ["L. Preston Sego III"]
13
+ s.email = "LPSego3+dev@gmail.com"
14
+ s.homepage = "https://github.com/NullVoxPopuli/kdiff3-rb"
15
+ s.summary = "kdiff3-#{KDiff3::VERSION}"
16
+ s.description = "Ruby wrapper for the kdiff3 mergetool"
17
+
18
+
19
+ s.files = `git ls-files`.split($/)
20
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
22
+ s.require_paths = ["lib"]
23
+
24
+ s.extensions = ['ext/kdiff3/extconf.rb']
25
+
26
+ s.add_dependency 'activesupport', '>= 3.2'
27
+
28
+ s.add_development_dependency "bundler", '>= 1.6.0'
29
+ s.add_development_dependency "awesome_print", '>= 1.2'
30
+ s.add_development_dependency "rspec", '>= 3.1.0'
31
+ s.add_development_dependency "pry-byebug", '>= 2.0.0'
32
+ s.add_development_dependency "codeclimate-test-reporter", '>= 0.4.3'
33
+
34
+ end
@@ -0,0 +1,173 @@
1
+ # string.present?
2
+ require 'active_support/core_ext/string'
3
+
4
+ module KDiff3
5
+
6
+ # list of created Tempfiles
7
+ TEMPFILES = []
8
+ # so we don't accidentally mess up formatting when we remove these later,
9
+ # we need to have a weird, non-stand sequence of characters to search
10
+ # and replace
11
+ NEWLINE = "\n\-"
12
+
13
+
14
+ # performs a 3 way merge between base, a, and b
15
+ # a 2 way merge will be performed if either yours
16
+ # or theirs are left out
17
+ #
18
+ # @param [String] base string or file path for common ancestor
19
+ # @param [String] yours string or file path for your changes
20
+ # @param [String] theirs string or file path for their changes
21
+ # @param [Boolean] html whether or not use an HTML diffing technique
22
+ # @return [String] result of merge
23
+ def self.merge(base: nil, yours: nil, theirs: nil, html: false)
24
+ raise ArgumentError.new('base is required') unless base.present?
25
+ raise ArgumentError.new('yours and/or theirs required') unless yours || theirs
26
+
27
+ # since HTML is often compressed to conserve transfer space, and therefore
28
+ # has few lines, we need to split up the HTML in to a multi-lined document
29
+ if html
30
+ base = add_new_lines(base)
31
+ yours = add_new_lines(yours)
32
+ theirs = add_new_lines(theirs)
33
+ end
34
+
35
+ base_path = tempfile(text: base, name: 'base')
36
+ your_path = tempfile(text: yours, name: 'yours')
37
+ their_path = tempfile(text: theirs, name: 'theirs')
38
+ output_path = tempfile(name: 'output')
39
+
40
+ # we don't need these open for anything
41
+ close_tempfiles
42
+
43
+ # the heavy lifting, courtesy of kdiff3
44
+ exit_code = run("#{base_path} #{your_path} #{their_path} -m --auto --fail -o #{output_path}")
45
+ conflicts_exist = exit_code == 1
46
+
47
+ result = IO.read(output_path) unless conflicts_exist
48
+
49
+ # clean up
50
+ delete_tempfiles
51
+
52
+ raise RuntimeError.new("Conflicts exist and could not be resolved") if conflicts_exist
53
+
54
+ if html
55
+ # remove the NEWLINES
56
+ result.gsub!(NEWLINE, "")
57
+ end
58
+
59
+ result
60
+ end
61
+
62
+ private
63
+
64
+ # @param [String] name of the file
65
+ # @param [String] text content of file or path
66
+ # @return [String] path of the Tempfile or Pre-existing file
67
+ def self.tempfile(text: nil, name: nil)
68
+ result = ""
69
+
70
+ # if the file already exists,
71
+ # don't add it to the tempfile list,
72
+ # as we don't want it to be deleted
73
+ if text && is_file_path?(text)
74
+ result = text
75
+ else
76
+ t = Tempfile.new(name)
77
+ t << text if text
78
+ TEMPFILES << t
79
+ result = t.path
80
+ end
81
+
82
+ result
83
+ end
84
+
85
+ def self.close_tempfiles
86
+ TEMPFILES.map(&:close)
87
+ end
88
+
89
+ def self.delete_tempfiles
90
+ TEMPFILES.map(&:delete)
91
+ end
92
+
93
+ # @param [String] path can be a file path or arbitrary string
94
+ # @return [Boolean] if the given string is a path to a file
95
+ def self.is_file_path?(path)
96
+ File.exist?(path)
97
+ end
98
+
99
+ def self.run(args)
100
+ %x(
101
+ #{kdiff3_path} #{args}
102
+ )
103
+ $?.exitstatus
104
+ end
105
+
106
+ # ensures the local copy of kdiff3 is present, if not, download and compile it
107
+ def self.kdiff3_path
108
+ current_folder = File.dirname(__FILE__)
109
+ path = "#{current_folder}/../ext/kdiff3/releaseQt/kdiff3"
110
+
111
+ unless File.exist?(path)
112
+ build_kdiff3
113
+ end
114
+
115
+ path
116
+ end
117
+
118
+ def self.build_kdiff3
119
+ current_folder = File.dirname(__FILE__)
120
+ kdiff3_repo_path = "#{current_folder}/../ext/kdiff3"
121
+
122
+ if File.exist?(kdiff3_repo_path)
123
+ %x(
124
+ git pull # origin optionally-fail-on-conflict
125
+ )
126
+ else
127
+ %x(
128
+ git clone git@github.com:NullVoxPopuli/kdiff3.git #{kdiff3_repo_path}
129
+ cd #{kdiff3_repo_path} && git checkout optionally-fail-on-conflict
130
+ )
131
+ end
132
+
133
+ # build
134
+ %x(
135
+ cd #{kdiff3_repo_path} && ./configure qt4
136
+ )
137
+ end
138
+
139
+ # add newlines after every tag, and every character
140
+ def self.add_new_lines(text)
141
+ text = self.add_new_lines_to_non_tags(text)
142
+ text = self.add_new_lines_after_tags(text)
143
+
144
+ # trim accidental blank lines
145
+ text.gsub!("#{NEWLINE}#{NEWLINE}", NEWLINE)
146
+
147
+ text
148
+ end
149
+
150
+ # http://www.rubular.com/r/N2AHZgpPum
151
+ # http://stackoverflow.com/questions/7540489/javascript-regex-match-text-not-part-of-a-html-tag
152
+ def self.add_new_lines_after_tags(text)
153
+ tag_selection_regex = /<[^>]*>/
154
+ text.gsub(tag_selection_regex) do |match|
155
+ match << NEWLINE
156
+ end
157
+ end
158
+
159
+ # http://www.rubular.com/r/mpX6Ee2r0k
160
+ # http://stackoverflow.com/questions/18621568/regex-replace-text-outside-html-tags
161
+ def self.add_new_lines_to_non_tags(text)
162
+ non_tags = /(?<=^|>)[^><]+?(?=<|$)/
163
+ # non_tags = /([^<>]+)(?![^<]*>|[^<>]*<\/)/
164
+ text.gsub(non_tags) do |match|
165
+ # consecutive words
166
+ match.gsub!(" ", " #{NEWLINE}")
167
+ # end with NEWLINE
168
+ match << NEWLINE
169
+ end
170
+ end
171
+
172
+
173
+ end
@@ -0,0 +1,3 @@
1
+ module KDiff3
2
+ VERSION = "0.9.1"
3
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ describe KDiff3 do
4
+ before(:each) do
5
+ if ENV['USE_BIN_KDIFF3']
6
+ current_folder = File.dirname(__FILE__)
7
+ # may only work on Ubuntu 14.04?
8
+ kdiff3_bin_path = "#{current_folder}/../bin/kdiff3"
9
+ allow(KDiff3).to receive(:kdiff3_path).and_return(kdiff3_bin_path)
10
+ end
11
+ end
12
+
13
+
14
+ describe 'merge' do
15
+ it { expect{KDiff3.merge}.to raise_error(ArgumentError) }
16
+ it { expect{KDiff3.merge(base: "base")}.to raise_error(ArgumentError) }
17
+ it { expect{KDiff3.merge(base: "base", yours: "base")}.to_not raise_error }
18
+ it { expect{KDiff3.merge(base: "base", yours: "base", theirs: "base")}.to_not raise_error }
19
+
20
+ describe 'by line' do
21
+ before(:all) do
22
+ @base = "#{File.dirname(__FILE__)}/support/base"
23
+ @a = "#{File.dirname(__FILE__)}/support/a"
24
+ @b = "#{File.dirname(__FILE__)}/support/b"
25
+ @expected = "#{File.dirname(__FILE__)}/support/expected"
26
+ end
27
+
28
+ it 'completes a 3-way merge' do
29
+ result = KDiff3.merge(
30
+ base: @base,
31
+ yours: @a,
32
+ theirs: @b
33
+ )
34
+ expected = IO.read(@expected)
35
+
36
+ expect(result).to eq expected
37
+ end
38
+ end
39
+
40
+ describe 'by word (the html option)' do
41
+ before(:all) do
42
+ @base = "<p>1,2,<p>1,<p>2,</p></p>3</p>"
43
+ @a = "<p>1,2,<p>1,</p>3</p>"
44
+ @b = "<p>1,2,<p><p>2,</p></p>3</p>"
45
+ @expected = "<p>1,2,<p></p>3</p>"
46
+ end
47
+
48
+ it 'completes a 3-way merge' do
49
+ result = KDiff3.merge(
50
+ base: @base,
51
+ yours: @a,
52
+ theirs: @b,
53
+ html: true
54
+ )
55
+ expect(result).to eq @expected
56
+ end
57
+ end
58
+
59
+ describe "raises an error if there are conflicts" do
60
+ it { expect{ KDiff3.merge(base: 'a b', yours: '1 b', theirs: '2 b', html: true) }.to raise_error }
61
+ end
62
+ end
63
+
64
+ describe 'private methods' do
65
+
66
+ describe 'is_file?' do
67
+ it { expect(KDiff3.send(:is_file_path?, __FILE__)).to eq true }
68
+ it { expect(KDiff3.send(:is_file_path?, "#{File.dirname(__FILE__)}/spec_helper.rb")).to eq true }
69
+ it { expect(KDiff3.send(:is_file_path?, "not a file")).to eq false }
70
+ it { expect(KDiff3.send(:is_file_path?, "not/a/file")).to eq false }
71
+ end
72
+
73
+ describe 'tempfile' do
74
+ it 'returns path when path is a file' do
75
+ expect(KDiff3.send(:tempfile, text: __FILE__, name: 'rspec')).to eq __FILE__
76
+ end
77
+
78
+ it 'creates a tempfile' do
79
+ expect{
80
+ KDiff3.send(:tempfile, name: 'base')
81
+ }.to change(KDiff3::TEMPFILES, :size).by 1
82
+ end
83
+
84
+ it 'creates a tempfile with content' do
85
+ path = KDiff3.send(:tempfile, name: 'base', text: 'content')
86
+ # file must be closed before reading
87
+ KDiff3.send(:close_tempfiles)
88
+ expect(IO.read(path)).to eq 'content'
89
+ end
90
+ end
91
+
92
+ describe 'kdiff3_path' do
93
+ it "hasn't compiled kdiff3 yet"
94
+ it "has compiled kdiff3"
95
+ it "updates the kdiff3 repo"
96
+ end
97
+
98
+ describe 'add_new_lines' do
99
+ after(:each) do
100
+ result = KDiff3.send(:add_new_lines, @text)
101
+ # remove special character
102
+ # - this determines our line breaks from pre-existing ones
103
+ result.gsub!(KDiff3::NEWLINE, "\n")
104
+ expect(result).to eq @expected
105
+ end
106
+
107
+ it 'adds new lines to text' do
108
+ @text = "something like this"
109
+ @expected = "something \nlike \nthis\n"
110
+ end
111
+
112
+ it 'adds new lines to html' do
113
+ @text = "<p>something maybe<p><span>like <a>this</a></span></p></p>"
114
+ @expected = "<p>\nsomething \nmaybe\n<p>\n<span>\nlike \n<a>\nthis\n</a>\n</span>\n</p>\n</p>\n"
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,31 @@
1
+
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+
5
+ require "pry-byebug" # binding.pry # to debug!
6
+ require 'awesome_print' # ap object # to pretty print
7
+
8
+ # Coverage
9
+ require "codeclimate-test-reporter"
10
+ # ENV['CODECLIMATE_REPO_TOKEN'] = ""
11
+ CodeClimate::TestReporter.start
12
+
13
+ # This Gem
14
+ require "kdiff3"
15
+
16
+ # This file was generated by the `rspec --init` command. Conventionally, all
17
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
18
+ # Require this file using `require "spec_helper"` to ensure that it is only
19
+ # loaded once.
20
+ #
21
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
22
+ RSpec.configure do |config|
23
+ config.run_all_when_everything_filtered = true
24
+ config.filter_run :focus
25
+
26
+ # Run specs in random order to surface order dependencies. If you find an
27
+ # order dependency and want to debug it, you can fix the order by providing
28
+ # the seed, which is printed after each run.
29
+ # --seed 1234
30
+ config.order = 'random'
31
+ end
@@ -0,0 +1,7 @@
1
+ <p>
2
+ 1,2,
3
+ <p>
4
+ 1,
5
+ </p>
6
+ 3
7
+ </p>
@@ -0,0 +1,9 @@
1
+ <p>
2
+ 1,2,
3
+ <p>
4
+ <p>
5
+ 2,
6
+ </p>
7
+ </p>
8
+ 3
9
+ </p>
@@ -0,0 +1,10 @@
1
+ <p>
2
+ 1,2,
3
+ <p>
4
+ 1,
5
+ <p>
6
+ 2,
7
+ </p>
8
+ </p>
9
+ 3
10
+ </p>