fix_tsv_conflict 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +1 -1
- data/lib/fix_tsv_conflict/conflict.rb +30 -0
- data/lib/fix_tsv_conflict/repairman.rb +38 -139
- data/lib/fix_tsv_conflict/resolver.rb +111 -0
- data/lib/fix_tsv_conflict/version.rb +1 -1
- metadata +4 -3
- data/lib/fix_tsv_conflict/diff_printer.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 073b4ff002e1a394af88ca8d2415786d8ee40c69c77af3741bbedc1585f5cd52
|
4
|
+
data.tar.gz: ab701259a3360cef72e8d37c0f9bed15c0bc6abcb1ff178afa1392d9f965f443
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ab5861f20e17388137f1c3a83c03b6c13e05bc0865e80894557849c701379f9ac9e9c56ed73b96102447b431758d01f8da0366f67531f9b9ac9b618f26ed0e1
|
7
|
+
data.tar.gz: bf957e7a2d6614b233cc7e0769a1130f8c6b0f32523761546947e2d7b3f399e2461633d90fbddf2698fafc7c998ae540db71ecf2f1c25b3c7181307190f56be4
|
data/Guardfile
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
module FixTSVConflict
|
2
|
+
class Conflict
|
3
|
+
attr_reader :left, :lbranch, :right, :rbranch
|
4
|
+
|
5
|
+
def initialize(left, lbranch, right, rbranch)
|
6
|
+
@left = left
|
7
|
+
@lbranch = lbranch
|
8
|
+
@right = right
|
9
|
+
@rbranch = rbranch
|
10
|
+
end
|
11
|
+
|
12
|
+
ID_REGEXP = /\A[0-9]+\t/
|
13
|
+
NL_REGEXP = /\A\n/
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
left.all? { |line| ID_REGEXP =~ line || NL_REGEXP =~ line} &&
|
17
|
+
right.all? { |line| ID_REGEXP =~ line || NL_REGEXP =~ line }
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_a
|
21
|
+
result = []
|
22
|
+
result << "#{LEFT} #{lbranch}\n"
|
23
|
+
result += left
|
24
|
+
result << "#{SEP}\n"
|
25
|
+
result += right
|
26
|
+
result << "#{RIGHT} #{rbranch}\n"
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,48 +1,46 @@
|
|
1
|
-
require "fix_tsv_conflict/
|
1
|
+
require "fix_tsv_conflict/conflict"
|
2
2
|
require "fix_tsv_conflict/logging"
|
3
|
+
require "fix_tsv_conflict/resolver"
|
3
4
|
|
4
5
|
module FixTSVConflict
|
5
6
|
class Repairman
|
6
7
|
include Logging
|
7
8
|
|
8
|
-
|
9
|
-
using Module.new {
|
10
|
-
refine String do
|
11
|
-
def blank?
|
12
|
-
BLANK_RE === self
|
13
|
-
end
|
14
|
-
end
|
15
|
-
}
|
16
|
-
|
17
|
-
attr_reader :stderr
|
9
|
+
attr_reader :stdin, :stderr
|
18
10
|
|
19
11
|
def initialize(stdin: $stdin, stderr: $stderr)
|
20
12
|
@stdin = stdin
|
21
13
|
@stderr = stderr
|
22
14
|
end
|
23
15
|
|
16
|
+
def resolver
|
17
|
+
@resolver ||= Resolver.new(stdin: stdin, stderr: stderr)
|
18
|
+
end
|
19
|
+
|
24
20
|
def repair(source)
|
25
21
|
result = []
|
26
|
-
branch
|
22
|
+
branch = nil
|
23
|
+
left, lbranch = [], nil
|
24
|
+
right, rbranch = [], nil
|
27
25
|
|
28
26
|
source.each_line.with_index do |line, i|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
27
|
+
if i.zero?
|
28
|
+
load_tabs_count(line)
|
29
|
+
result << line
|
30
|
+
elsif line.start_with?(LEFT)
|
31
|
+
lbranch = line.chomp.split(" ").last
|
32
|
+
branch = left
|
33
|
+
elsif line.start_with?(SEP)
|
34
|
+
branch = right
|
35
|
+
elsif line.start_with?(RIGHT)
|
36
|
+
rbranch = line.chomp.split(" ").last
|
37
|
+
result += handle(left, lbranch, right, rbranch)
|
38
|
+
branch = nil
|
39
|
+
left.clear
|
40
|
+
right.clear
|
42
41
|
else
|
43
|
-
if
|
44
|
-
|
45
|
-
branch = left
|
42
|
+
if branch
|
43
|
+
branch << line
|
46
44
|
else
|
47
45
|
result << line
|
48
46
|
end
|
@@ -51,130 +49,31 @@ module FixTSVConflict
|
|
51
49
|
result.join
|
52
50
|
end
|
53
51
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
52
|
+
def load_tabs_count(header)
|
53
|
+
resolver.tabs = header.count(TAB)
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle(left, lbranch, right, rbranch)
|
57
|
+
conflict = Conflict.new(left, lbranch, right, rbranch)
|
58
|
+
print_conflict(conflict)
|
59
|
+
result = resolver.resolve(conflict)
|
57
60
|
print_result(result)
|
58
61
|
result
|
59
62
|
end
|
60
63
|
|
61
|
-
def
|
62
|
-
|
64
|
+
def print_conflict(conflict)
|
65
|
+
info "Found a conflict:"
|
63
66
|
blank
|
64
|
-
dump
|
65
|
-
dump left
|
66
|
-
dump SEP
|
67
|
-
dump right
|
68
|
-
dump "#{RIGHT} #{@rbranch}"
|
67
|
+
dump conflict.to_a
|
69
68
|
blank
|
70
69
|
end
|
71
70
|
|
72
71
|
def print_result(result)
|
73
|
-
notice "
|
72
|
+
notice "Resolved to:"
|
74
73
|
blank
|
75
74
|
dump result
|
76
75
|
blank
|
77
76
|
blank
|
78
77
|
end
|
79
|
-
|
80
|
-
def resolve_conflicts(left, right)
|
81
|
-
left = index_by_id(left.reject(&:blank?))
|
82
|
-
right = index_by_id(right.reject(&:blank?))
|
83
|
-
(left.keys + right.keys).uniq.sort.map do |id|
|
84
|
-
l = left[id]
|
85
|
-
r = right[id]
|
86
|
-
if l && r
|
87
|
-
select(l, r)
|
88
|
-
else
|
89
|
-
l || r
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def select(l, r)
|
95
|
-
selected = if l.rstrip == r.rstrip
|
96
|
-
pick_by_trailing_tabs(l, r)
|
97
|
-
else
|
98
|
-
prompt(l, r)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def prompt(l, r)
|
103
|
-
print_diff(l, r)
|
104
|
-
prompt_select(l, r)
|
105
|
-
end
|
106
|
-
|
107
|
-
def print_diff(l, r)
|
108
|
-
log "Diff by columns:"
|
109
|
-
blank
|
110
|
-
printer = DiffPrinter.new(stderr: @stderr)
|
111
|
-
printer.print(@cols, l, @lbranch, r, @rbranch)
|
112
|
-
end
|
113
|
-
|
114
|
-
def prompt_select(l, r)
|
115
|
-
text = <<-TEXT
|
116
|
-
|
117
|
-
Which do you want keep?
|
118
|
-
|
119
|
-
1) #{@lbranch}
|
120
|
-
2) #{@rbranch}
|
121
|
-
k) keep as is
|
122
|
-
|
123
|
-
Please enter 1, 2, or k:
|
124
|
-
TEXT
|
125
|
-
|
126
|
-
info text.chomp, no_newline: true
|
127
|
-
|
128
|
-
loop do
|
129
|
-
case selected = @stdin.gets.strip
|
130
|
-
when "1"
|
131
|
-
break l
|
132
|
-
when "2"
|
133
|
-
break r
|
134
|
-
when "k"
|
135
|
-
break "#{LEFT} #{@lbranch}\n#{l}#{SEP}\n#{r}#{RIGHT} #{@rbranch}\n"
|
136
|
-
else
|
137
|
-
text = <<-TEXT
|
138
|
-
Invalid input: #{selected}
|
139
|
-
Please enter 1, 2, or k:
|
140
|
-
TEXT
|
141
|
-
@stderr.print text.chomp
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def index_by_id(lines)
|
147
|
-
result = {}
|
148
|
-
lines.each do |line|
|
149
|
-
id = line.split(TAB, 2).first
|
150
|
-
result[id] = line
|
151
|
-
end
|
152
|
-
result
|
153
|
-
end
|
154
|
-
|
155
|
-
def parse_header(line)
|
156
|
-
@cols = {}
|
157
|
-
line.chomp.split(TAB).each.with_index do |col, i|
|
158
|
-
@cols[col] = i
|
159
|
-
end
|
160
|
-
@tabs = @cols.length - 1
|
161
|
-
end
|
162
|
-
|
163
|
-
def pick_by_trailing_tabs(l, r)
|
164
|
-
info "Trailing tab conflicts were fixed automatically."
|
165
|
-
|
166
|
-
ltabs = l.count(TAB)
|
167
|
-
rtabs = r.count(TAB)
|
168
|
-
|
169
|
-
if ltabs == @tabs
|
170
|
-
l
|
171
|
-
elsif rtabs == @tabs
|
172
|
-
r
|
173
|
-
else
|
174
|
-
# both are wrong.
|
175
|
-
# so this is a determistic picking.
|
176
|
-
ltabs < rtabs ? l : r
|
177
|
-
end
|
178
|
-
end
|
179
78
|
end
|
180
79
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "fix_tsv_conflict/logging"
|
2
|
+
|
3
|
+
module FixTSVConflict
|
4
|
+
class Resolver
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
BLANK_RE = /\A[[:space:]]*\z/
|
8
|
+
using Module.new {
|
9
|
+
refine String do
|
10
|
+
def blank?
|
11
|
+
BLANK_RE === self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
}
|
15
|
+
|
16
|
+
attr_reader :stdin, :stderr
|
17
|
+
attr_accessor :tabs
|
18
|
+
|
19
|
+
def initialize(stdin: $stdin, stderr: $stderr)
|
20
|
+
@stdin = stdin
|
21
|
+
@stderr = stderr
|
22
|
+
|
23
|
+
@tabs = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def resolve(conflict)
|
27
|
+
unless conflict.valid?
|
28
|
+
return conflict.to_a
|
29
|
+
end
|
30
|
+
|
31
|
+
result = try(conflict)
|
32
|
+
if result
|
33
|
+
return result
|
34
|
+
end
|
35
|
+
|
36
|
+
warn "Failed to resolve it automatically."
|
37
|
+
select(conflict)
|
38
|
+
end
|
39
|
+
|
40
|
+
def try(conflict)
|
41
|
+
result = []
|
42
|
+
left = index_by_id(conflict.left.reject(&:blank?))
|
43
|
+
right = index_by_id(conflict.right.reject(&:blank?))
|
44
|
+
(left.keys + right.keys).uniq.sort.each do |id|
|
45
|
+
l = left[id]
|
46
|
+
r = right[id]
|
47
|
+
if l && r
|
48
|
+
if l.rstrip == r.rstrip
|
49
|
+
result << pick_by_tabs(l, r)
|
50
|
+
else
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
else
|
54
|
+
result << (l || r)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def select(conflict)
|
61
|
+
text = <<-TEXT
|
62
|
+
Which do you want keep?
|
63
|
+
|
64
|
+
1) #{conflict.lbranch}
|
65
|
+
2) #{conflict.rbranch}
|
66
|
+
k) keep as is
|
67
|
+
|
68
|
+
TEXT
|
69
|
+
|
70
|
+
info text
|
71
|
+
|
72
|
+
loop do
|
73
|
+
info "Please enter 1, 2, or k: ", no_newline: true
|
74
|
+
case selected = stdin.gets.strip
|
75
|
+
when "1"
|
76
|
+
return conflict.left
|
77
|
+
when "2"
|
78
|
+
return conflict.right
|
79
|
+
when "k"
|
80
|
+
return conflict.to_a
|
81
|
+
else
|
82
|
+
info "Invalid input: #{selected}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def index_by_id(lines)
|
88
|
+
result = {}
|
89
|
+
lines.each do |line|
|
90
|
+
id = line.split(TAB, 2).first
|
91
|
+
result[id] = line
|
92
|
+
end
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
def pick_by_tabs(l, r)
|
97
|
+
ltabs = l.count(TAB)
|
98
|
+
rtabs = r.count(TAB)
|
99
|
+
|
100
|
+
if ltabs == tabs
|
101
|
+
l
|
102
|
+
elsif rtabs == tabs
|
103
|
+
r
|
104
|
+
else
|
105
|
+
# both are wrong.
|
106
|
+
# so this is a determistic picking.
|
107
|
+
ltabs < rtabs ? l : r
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fix_tsv_conflict
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masato Ikeda
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-02-
|
11
|
+
date: 2018-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,9 +100,10 @@ files:
|
|
100
100
|
- fix_tsv_conflict.gemspec
|
101
101
|
- lib/fix_tsv_conflict.rb
|
102
102
|
- lib/fix_tsv_conflict/cli.rb
|
103
|
-
- lib/fix_tsv_conflict/
|
103
|
+
- lib/fix_tsv_conflict/conflict.rb
|
104
104
|
- lib/fix_tsv_conflict/logging.rb
|
105
105
|
- lib/fix_tsv_conflict/repairman.rb
|
106
|
+
- lib/fix_tsv_conflict/resolver.rb
|
106
107
|
- lib/fix_tsv_conflict/version.rb
|
107
108
|
homepage: https://github.com/a2ikm/fix_tsv_conflict
|
108
109
|
licenses: []
|
@@ -1,53 +0,0 @@
|
|
1
|
-
require "fix_tsv_conflict/logging"
|
2
|
-
|
3
|
-
module FixTSVConflict
|
4
|
-
class DiffPrinter
|
5
|
-
include Logging
|
6
|
-
|
7
|
-
attr_reader :stderr
|
8
|
-
|
9
|
-
def initialize(stderr: $stderr)
|
10
|
-
@stderr = stderr
|
11
|
-
@left, @right = {}, {}
|
12
|
-
end
|
13
|
-
|
14
|
-
def print(cols, left, lbranch, right, rbranch)
|
15
|
-
@lbranch, @rbranch = lbranch, rbranch
|
16
|
-
|
17
|
-
left = left.chomp.split(TAB)
|
18
|
-
right = right.chomp.split(TAB)
|
19
|
-
|
20
|
-
cols.each do |col, i|
|
21
|
-
l, r = left[i], right[i]
|
22
|
-
if l == r
|
23
|
-
flush_conflicts if in_conflict?
|
24
|
-
dump [col, l].join(TAB)
|
25
|
-
else
|
26
|
-
@left[col] = l
|
27
|
-
@right[col] = r
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
flush_conflicts
|
32
|
-
end
|
33
|
-
|
34
|
-
def flush_conflicts
|
35
|
-
dump "#{LEFT} #{@lbranch}"
|
36
|
-
dump @left.map { |c, v| [c, v].join(TAB) }
|
37
|
-
dump SEP
|
38
|
-
dump @right.map { |c, v| [c, v].join(TAB) }
|
39
|
-
dump "#{RIGHT} #{@rbranch}"
|
40
|
-
|
41
|
-
@left.clear
|
42
|
-
@right.clear
|
43
|
-
end
|
44
|
-
|
45
|
-
def print_col_and_value(col, value)
|
46
|
-
@stderr.puts [col, value].join(TAB)
|
47
|
-
end
|
48
|
-
|
49
|
-
def in_conflict?
|
50
|
-
@left.length > 0 || @right.length > 0
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|