kdiff3 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>