has_versions 0.4.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +7 -24
- data/has_versions.gemspec +5 -6
- data/lib/has_versions/attributes.rb +3 -3
- data/lib/has_versions/configuration.rb +0 -1
- data/lib/has_versions/reset.rb +10 -4
- data/lib/has_versions/version_methods/diff.rb +40 -0
- data/lib/has_versions/version_methods/log.rb +45 -0
- data/lib/has_versions/versioned.rb +9 -12
- data/lib/has_versions.rb +8 -9
- data/test/has_versions/attributes_test.rb +15 -10
- data/test/has_versions/configuration_test.rb +12 -0
- data/test/has_versions/reset_test.rb +29 -0
- data/test/has_versions/version_methods/diff_test.rb +68 -0
- data/test/has_versions/version_methods/log_test.rb +33 -0
- data/test/has_versions/versioned_test.rb +27 -0
- data/test/helper.rb +6 -0
- data/test/support/test_model.rb +21 -0
- data/test/support/test_version.rb +15 -0
- metadata +24 -96
- data/lib/has_versions/apply/simple.rb +0 -34
- data/lib/has_versions/apply.rb +0 -9
- data/lib/has_versions/diff/simple.rb +0 -27
- data/lib/has_versions/diff.rb +0 -7
- data/lib/has_versions/merge/always_conflicted.rb +0 -9
- data/lib/has_versions/merge/base.rb +0 -22
- data/lib/has_versions/merge/choose_first.rb +0 -9
- data/lib/has_versions/merge/diff3.rb +0 -66
- data/lib/has_versions/merge/fast_forward.rb +0 -34
- data/lib/has_versions/merge/merge_base.rb +0 -75
- data/lib/has_versions/merge/octopus.rb +0 -42
- data/lib/has_versions/merge/three_way.rb +0 -53
- data/lib/has_versions/merge.rb +0 -32
- data/lib/has_versions/reset/simple.rb +0 -19
- data/spec/has_versions/apply_spec.rb +0 -33
- data/spec/has_versions/configuration_spec.rb +0 -33
- data/spec/has_versions/diff_spec.rb +0 -40
- data/spec/has_versions/merge/diff3_spec.rb +0 -29
- data/spec/has_versions/merge/merge_base_spec.rb +0 -61
- data/spec/has_versions/merge/octopus_spec.rb +0 -74
- data/spec/has_versions/merge/three_way_spec.rb +0 -57
- data/spec/has_versions/reset_spec.rb +0 -40
- data/spec/has_versions/stage_spec.rb +0 -17
- data/spec/has_versions/versioned_spec.rb +0 -60
- data/spec/spec_helper.rb +0 -17
- data/spec/support/matchers/be_a_uuid.rb +0 -7
- data/spec/support/version.rb +0 -17
- data/test/test_helper.rb +0 -3
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_versions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: i18n
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
14
|
- !ruby/object:Gem::Dependency
|
31
15
|
name: activesupport
|
32
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -34,7 +18,7 @@ dependencies:
|
|
34
18
|
requirements:
|
35
19
|
- - ~>
|
36
20
|
- !ruby/object:Gem::Version
|
37
|
-
version: '3.
|
21
|
+
version: '3.2'
|
38
22
|
type: :runtime
|
39
23
|
prerelease: false
|
40
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -42,16 +26,16 @@ dependencies:
|
|
42
26
|
requirements:
|
43
27
|
- - ~>
|
44
28
|
- !ruby/object:Gem::Version
|
45
|
-
version: '3.
|
29
|
+
version: '3.2'
|
46
30
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
31
|
+
name: rake
|
48
32
|
requirement: !ruby/object:Gem::Requirement
|
49
33
|
none: false
|
50
34
|
requirements:
|
51
35
|
- - ! '>='
|
52
36
|
- !ruby/object:Gem::Version
|
53
37
|
version: '0'
|
54
|
-
type: :
|
38
|
+
type: :development
|
55
39
|
prerelease: false
|
56
40
|
version_requirements: !ruby/object:Gem::Requirement
|
57
41
|
none: false
|
@@ -59,38 +43,6 @@ dependencies:
|
|
59
43
|
- - ! '>='
|
60
44
|
- !ruby/object:Gem::Version
|
61
45
|
version: '0'
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: rspec
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ~>
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '2.0'
|
70
|
-
type: :development
|
71
|
-
prerelease: false
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ~>
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '2.0'
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: yard
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - ~>
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: 0.6.0
|
86
|
-
type: :development
|
87
|
-
prerelease: false
|
88
|
-
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ~>
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: 0.6.0
|
94
46
|
description: ActiveModel versioning
|
95
47
|
email:
|
96
48
|
- grantr@gmail.com
|
@@ -113,40 +65,22 @@ files:
|
|
113
65
|
- features/support/env.rb
|
114
66
|
- has_versions.gemspec
|
115
67
|
- lib/has_versions.rb
|
116
|
-
- lib/has_versions/apply.rb
|
117
|
-
- lib/has_versions/apply/simple.rb
|
118
68
|
- lib/has_versions/attributes.rb
|
119
69
|
- lib/has_versions/configuration.rb
|
120
|
-
- lib/has_versions/diff.rb
|
121
|
-
- lib/has_versions/diff/simple.rb
|
122
|
-
- lib/has_versions/merge.rb
|
123
|
-
- lib/has_versions/merge/always_conflicted.rb
|
124
|
-
- lib/has_versions/merge/base.rb
|
125
|
-
- lib/has_versions/merge/choose_first.rb
|
126
|
-
- lib/has_versions/merge/diff3.rb
|
127
|
-
- lib/has_versions/merge/fast_forward.rb
|
128
|
-
- lib/has_versions/merge/merge_base.rb
|
129
|
-
- lib/has_versions/merge/octopus.rb
|
130
|
-
- lib/has_versions/merge/three_way.rb
|
131
70
|
- lib/has_versions/orm/cassandra_object.rb
|
132
71
|
- lib/has_versions/reset.rb
|
133
|
-
- lib/has_versions/
|
72
|
+
- lib/has_versions/version_methods/diff.rb
|
73
|
+
- lib/has_versions/version_methods/log.rb
|
134
74
|
- lib/has_versions/versioned.rb
|
135
|
-
- spec/has_versions/apply_spec.rb
|
136
|
-
- spec/has_versions/configuration_spec.rb
|
137
|
-
- spec/has_versions/diff_spec.rb
|
138
|
-
- spec/has_versions/merge/diff3_spec.rb
|
139
|
-
- spec/has_versions/merge/merge_base_spec.rb
|
140
|
-
- spec/has_versions/merge/octopus_spec.rb
|
141
|
-
- spec/has_versions/merge/three_way_spec.rb
|
142
|
-
- spec/has_versions/reset_spec.rb
|
143
|
-
- spec/has_versions/stage_spec.rb
|
144
|
-
- spec/has_versions/versioned_spec.rb
|
145
|
-
- spec/spec_helper.rb
|
146
|
-
- spec/support/matchers/be_a_uuid.rb
|
147
|
-
- spec/support/version.rb
|
148
75
|
- test/has_versions/attributes_test.rb
|
149
|
-
- test/
|
76
|
+
- test/has_versions/configuration_test.rb
|
77
|
+
- test/has_versions/reset_test.rb
|
78
|
+
- test/has_versions/version_methods/diff_test.rb
|
79
|
+
- test/has_versions/version_methods/log_test.rb
|
80
|
+
- test/has_versions/versioned_test.rb
|
81
|
+
- test/helper.rb
|
82
|
+
- test/support/test_model.rb
|
83
|
+
- test/support/test_version.rb
|
150
84
|
homepage: ''
|
151
85
|
licenses:
|
152
86
|
- MIT
|
@@ -176,18 +110,12 @@ test_files:
|
|
176
110
|
- features/has_versions.feature
|
177
111
|
- features/step_definitions/has_versions_steps.rb
|
178
112
|
- features/support/env.rb
|
179
|
-
- spec/has_versions/apply_spec.rb
|
180
|
-
- spec/has_versions/configuration_spec.rb
|
181
|
-
- spec/has_versions/diff_spec.rb
|
182
|
-
- spec/has_versions/merge/diff3_spec.rb
|
183
|
-
- spec/has_versions/merge/merge_base_spec.rb
|
184
|
-
- spec/has_versions/merge/octopus_spec.rb
|
185
|
-
- spec/has_versions/merge/three_way_spec.rb
|
186
|
-
- spec/has_versions/reset_spec.rb
|
187
|
-
- spec/has_versions/stage_spec.rb
|
188
|
-
- spec/has_versions/versioned_spec.rb
|
189
|
-
- spec/spec_helper.rb
|
190
|
-
- spec/support/matchers/be_a_uuid.rb
|
191
|
-
- spec/support/version.rb
|
192
113
|
- test/has_versions/attributes_test.rb
|
193
|
-
- test/
|
114
|
+
- test/has_versions/configuration_test.rb
|
115
|
+
- test/has_versions/reset_test.rb
|
116
|
+
- test/has_versions/version_methods/diff_test.rb
|
117
|
+
- test/has_versions/version_methods/log_test.rb
|
118
|
+
- test/has_versions/versioned_test.rb
|
119
|
+
- test/helper.rb
|
120
|
+
- test/support/test_model.rb
|
121
|
+
- test/support/test_version.rb
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Apply
|
3
|
-
|
4
|
-
# takes a list of diff (patch) hashes and returns a copy of self with the patches applied in order.
|
5
|
-
# raises ApplyFailed if the patch does not apply
|
6
|
-
# diff format is:
|
7
|
-
# {
|
8
|
-
# attribute_name: [a, b]
|
9
|
-
# }
|
10
|
-
#
|
11
|
-
#TODO should this be allowed to change the schema?
|
12
|
-
# it will if the hash returns nil for unset keys
|
13
|
-
|
14
|
-
module Simple
|
15
|
-
def apply(*patches)
|
16
|
-
self.class.new.tap do |version| #TODO is new the right thing here? it doe scall initialize (allocate does not)
|
17
|
-
ours = snapshot.dup #TEST is it necessary to deep dup the snapshot?
|
18
|
-
|
19
|
-
patches.each do |patch|
|
20
|
-
patch.each do |key, (a, b)|
|
21
|
-
if ours[key] == a
|
22
|
-
ours[key] = b #TEST is it necessary to dup the value?
|
23
|
-
else
|
24
|
-
raise ApplyFailed, "Expected #{key} to be #{a.inspect}, was #{ours[key].inspect}" #TODO should this contain more information about what failed?
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
version.snapshot = ours
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/has_versions/apply.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Diff
|
3
|
-
# takes a version and returns a diff (patch) from self
|
4
|
-
# version must respond to #snapshot with an attributes hash (or something that responds to [] and keys)
|
5
|
-
# diff format is:
|
6
|
-
# {
|
7
|
-
# attribute_name: [a, b]
|
8
|
-
# }
|
9
|
-
|
10
|
-
module Simple
|
11
|
-
def diff(other)
|
12
|
-
ours = snapshot
|
13
|
-
theirs = other.snapshot
|
14
|
-
all_keys = (ours.keys + theirs.keys).uniq
|
15
|
-
|
16
|
-
output = {}.with_indifferent_access
|
17
|
-
all_keys.each do |key|
|
18
|
-
next unless target.versioning_codable?(key)
|
19
|
-
our_value = target.versioning_decode_value(key, ours[key])
|
20
|
-
their_value = target.versioning_decode_value(key, theirs[key])
|
21
|
-
output[key] = [our_value, their_value] if our_value != their_value
|
22
|
-
end
|
23
|
-
output
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/has_versions/diff.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Merge
|
3
|
-
class Base
|
4
|
-
attr_accessor :versions, :options
|
5
|
-
attr_accessor :resolution, :conflicts
|
6
|
-
|
7
|
-
def initialize(versions, options={})
|
8
|
-
@versions = versions
|
9
|
-
@options = options
|
10
|
-
@conflicts = {}.with_indifferent_access
|
11
|
-
end
|
12
|
-
|
13
|
-
def merge
|
14
|
-
# implemented by subclasses
|
15
|
-
end
|
16
|
-
|
17
|
-
def conflicted?
|
18
|
-
!self.conflicts.empty?
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Merge
|
3
|
-
module Diff3
|
4
|
-
|
5
|
-
# 3-way merge, extended to support more than one other
|
6
|
-
# base ours *theirs.uniq take
|
7
|
-
# A A [A] A (ours)
|
8
|
-
# A B [A] B (ours)
|
9
|
-
# A A [B] B (theirs)
|
10
|
-
# B A [A] A (ours)
|
11
|
-
# B A [A,B] conflict
|
12
|
-
# B A [C] conflict
|
13
|
-
#
|
14
|
-
# cases where values are missing:
|
15
|
-
# m A A A
|
16
|
-
# m B A conflict
|
17
|
-
# A m A A
|
18
|
-
# A m B B
|
19
|
-
# A A m A
|
20
|
-
# A B m B
|
21
|
-
# m m A A
|
22
|
-
# m A m A
|
23
|
-
#
|
24
|
-
# returns an array [clean, result]
|
25
|
-
# where clean is a boolean
|
26
|
-
# and result is an array in the conflicts case, or a single value in the clean case
|
27
|
-
|
28
|
-
def diff3(base, ours, *theirs)
|
29
|
-
theirs = theirs.uniq
|
30
|
-
|
31
|
-
# if there is more than one possible value, it is already a conflict
|
32
|
-
if theirs.size > 1
|
33
|
-
# add ours to conflicts if it is different from base
|
34
|
-
if ours != base && !theirs.include?(ours)
|
35
|
-
theirs << ours
|
36
|
-
end
|
37
|
-
return [false, theirs]
|
38
|
-
end
|
39
|
-
|
40
|
-
theirs = theirs.first
|
41
|
-
|
42
|
-
if ours == base && base == theirs
|
43
|
-
[true, ours]
|
44
|
-
elsif base == theirs
|
45
|
-
[true, ours]
|
46
|
-
elsif ours == base
|
47
|
-
[true, theirs]
|
48
|
-
elsif ours == theirs
|
49
|
-
[true, ours]
|
50
|
-
elsif ours == :_missing
|
51
|
-
[true, theirs]
|
52
|
-
elsif theirs == :_missing
|
53
|
-
[true, ours]
|
54
|
-
else
|
55
|
-
[false, [ours, theirs]]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def value_or_missing(snapshot, key)
|
62
|
-
snapshot.keys.include?(key) ? snapshot[key] : :_missing
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Merge
|
3
|
-
class FastForward < Base
|
4
|
-
# Takes exactly two versions and resolves the merge using ancestry.
|
5
|
-
# One of the two versions must be an ancestor of the other.
|
6
|
-
#
|
7
|
-
# expects versions to define contains? which returns true if the given version
|
8
|
-
# is an ancestor of the receiver
|
9
|
-
# eg if v1 is an ancestor of v2, then
|
10
|
-
# v2.contains?(v1) #=> true
|
11
|
-
#
|
12
|
-
# raises MergeFailed if anything but two versions are given, or if neither version
|
13
|
-
# is an ancestor of the other.
|
14
|
-
|
15
|
-
def initialize(versions, options={})
|
16
|
-
super
|
17
|
-
if versions.size != 2
|
18
|
-
raise HasVersions::MergeFailed, "Cannot fast forward merge with #{versions.size} versions"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def merge
|
23
|
-
v1, v2 = versions
|
24
|
-
if v1.contains?(v2)
|
25
|
-
self.resolution = v1
|
26
|
-
elsif v2.contains?(v1)
|
27
|
-
self.resolution = v2
|
28
|
-
else
|
29
|
-
raise HasVersions::MergeFailed, "cannot fast forward if neither version is an ancestor"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Merge
|
3
|
-
module MergeBase
|
4
|
-
# Finds optimal merge bases for any number of versions
|
5
|
-
# Essentially a copy of git's algorithm
|
6
|
-
# Expects versions to respond to parents with an array of parent versions
|
7
|
-
def merge_bases(versions)
|
8
|
-
versions = versions.dup
|
9
|
-
one = versions.shift
|
10
|
-
twos = versions
|
11
|
-
|
12
|
-
bases = determine_merge_bases(one, twos)
|
13
|
-
if twos.include?(one)
|
14
|
-
return bases
|
15
|
-
end
|
16
|
-
|
17
|
-
if bases.size == 1
|
18
|
-
return bases
|
19
|
-
end
|
20
|
-
|
21
|
-
bases.each_with_index do |base1, i|
|
22
|
-
next if base1 == bases.last
|
23
|
-
|
24
|
-
bases.each_with_index do |base2, j|
|
25
|
-
next if base1.nil? || base2.nil?
|
26
|
-
sub_bases = determine_merge_bases(base1, [base2])
|
27
|
-
sub_bases.each do |sub_base|
|
28
|
-
bases[i] = nil if sub_base == base1
|
29
|
-
bases[j] = nil if sub_base == base2
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
bases.compact
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def determine_merge_bases(one, twos)
|
40
|
-
stale = Set.new
|
41
|
-
parent1 = Set.new
|
42
|
-
parent2 = Set.new
|
43
|
-
result = Set.new
|
44
|
-
|
45
|
-
interesting = []
|
46
|
-
|
47
|
-
if twos.include?(one)
|
48
|
-
return [one]
|
49
|
-
end
|
50
|
-
|
51
|
-
parent1 << one
|
52
|
-
parent2 += twos
|
53
|
-
|
54
|
-
interesting << one
|
55
|
-
interesting += twos
|
56
|
-
|
57
|
-
while !(interesting - stale.to_a).empty?
|
58
|
-
candidate = interesting.shift
|
59
|
-
if parent1.include?(candidate) && parent2.include?(candidate) && !stale.include?(candidate)
|
60
|
-
result << candidate
|
61
|
-
end
|
62
|
-
|
63
|
-
candidate.parents.each do |parent|
|
64
|
-
stale << parent if stale.include?(candidate)
|
65
|
-
parent1 << parent if parent1.include?(candidate)
|
66
|
-
parent2 << parent if parent2.include?(candidate)
|
67
|
-
interesting << parent
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
result.to_a.reject { |r| stale.include?(r) }
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Merge
|
3
|
-
# Merge >2 versions
|
4
|
-
# Works by doing a 3-way merge for first 2 versions, then merging the result with
|
5
|
-
# the next version for each version given.
|
6
|
-
# eg for 4 versions there are three merges: v1+v2, then (v1+v2)+v3, then ((v1+v2)+v3)+v4
|
7
|
-
# conflicts will be an accumulation of all conflicting values from each merge
|
8
|
-
class Octopus < Base
|
9
|
-
def initialize(versions, options={})
|
10
|
-
super
|
11
|
-
if versions.size <= 2
|
12
|
-
raise HasVersions::MergeFailed, "Cannot octopus merge with #{versions.size} versions, expect >2"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def merge
|
17
|
-
self.resolution = versions.inject(resolution) do |res, version|
|
18
|
-
if res
|
19
|
-
merge = ThreeWay.new([res, version], options)
|
20
|
-
merge.merge
|
21
|
-
add_conflicts(merge.conflicts)
|
22
|
-
res = merge.resolution
|
23
|
-
else
|
24
|
-
res = version
|
25
|
-
end
|
26
|
-
res
|
27
|
-
end
|
28
|
-
resolution.parents = versions
|
29
|
-
resolution
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
def add_conflicts(conflicts)
|
34
|
-
conflicts.each do |attribute, values|
|
35
|
-
self.conflicts[attribute] ||= []
|
36
|
-
self.conflicts[attribute] += values
|
37
|
-
self.conflicts[attribute].uniq!
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Merge
|
3
|
-
# Merges two versions using ancestry and 3-way diff
|
4
|
-
# expects versions to respond to parents= to set parents
|
5
|
-
# does not check ancestry when setting parents - use FastForward if one version
|
6
|
-
# is an ancestor of another or they are identical
|
7
|
-
class ThreeWay < Base
|
8
|
-
include MergeBase
|
9
|
-
include Diff3
|
10
|
-
|
11
|
-
def initialize(versions, options={})
|
12
|
-
super
|
13
|
-
if versions.size != 2
|
14
|
-
raise HasVersions::MergeFailed, "Cannot 3-way merge with #{versions.size} versions, expect 2"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# assume the first version is ours
|
19
|
-
def merge
|
20
|
-
version_class = options[:version_class] || versions.first.class
|
21
|
-
# find merge base or bases
|
22
|
-
bases = merge_bases(versions)
|
23
|
-
# raise if there is more than one base
|
24
|
-
# TODO could potentially just choose one (that's what git does)
|
25
|
-
raise MergeFailed, "More than one merge base" if bases.size > 1
|
26
|
-
|
27
|
-
base = bases.first.try(:snapshot) || {}.with_indifferent_access
|
28
|
-
ours = versions.first.snapshot
|
29
|
-
theirs = versions.last.snapshot
|
30
|
-
|
31
|
-
self.resolution = version_class.new
|
32
|
-
self.resolution.parents = versions
|
33
|
-
|
34
|
-
# get the full list of attributes in each version and the merge base
|
35
|
-
attributes = (versions.collect { |v| v.snapshot.keys }.flatten + base.keys).uniq
|
36
|
-
|
37
|
-
# do 3-way merge on each attribute
|
38
|
-
attributes.each do |key|
|
39
|
-
clean, result = diff3(value_or_missing(base, key), value_or_missing(ours, key), value_or_missing(theirs, key))
|
40
|
-
if clean
|
41
|
-
resolution.snapshot[key] = result
|
42
|
-
else
|
43
|
-
resolution.snapshot[key] = ours[key]
|
44
|
-
conflicts[key] = result
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
resolution
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
data/lib/has_versions/merge.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
|
3
|
-
class MergeFailed < VersioningError; end
|
4
|
-
|
5
|
-
class MergeConflict < MergeFailed
|
6
|
-
attr_accessor :conflicts
|
7
|
-
attr_accessor :result
|
8
|
-
|
9
|
-
def initialize(conflicts=nil, result=nil)
|
10
|
-
@conflicts = conflicts
|
11
|
-
@result = result
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
module Merge
|
16
|
-
extend ActiveSupport::Autoload
|
17
|
-
|
18
|
-
autoload :Base
|
19
|
-
|
20
|
-
autoload :FastForward
|
21
|
-
autoload :ThreeWay
|
22
|
-
autoload :Octopus
|
23
|
-
|
24
|
-
# strategies for testing
|
25
|
-
autoload :ChooseFirst
|
26
|
-
autoload :AlwaysConflicted
|
27
|
-
|
28
|
-
autoload :Diff3
|
29
|
-
autoload :MergeBase
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module HasVersions
|
2
|
-
module Reset
|
3
|
-
# reset should:
|
4
|
-
# take a version
|
5
|
-
# update self with the attributes from the version
|
6
|
-
# return updated self
|
7
|
-
|
8
|
-
module Simple
|
9
|
-
def reset!(version)
|
10
|
-
self.class.versioned_fields.each do |name|
|
11
|
-
value = versioning_decode_value(name, version.snapshot[name])
|
12
|
-
__send__("#{name}=", value)
|
13
|
-
end
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
class ApplyingVersion < Version; end
|
4
|
-
|
5
|
-
describe ApplyingVersion do
|
6
|
-
class ApplyingVersion
|
7
|
-
include HasVersions::Apply::Simple
|
8
|
-
end
|
9
|
-
|
10
|
-
let (:foo1) { described_class.new(name: 'foo1') }
|
11
|
-
let (:foo2) { described_class.new(name: 'foo2') }
|
12
|
-
|
13
|
-
let (:patch1) { {name: ['foo1', 'foo2']}.with_indifferent_access }
|
14
|
-
let (:patch2) { {name: ['foo2', 'foo1'], stripey: [nil, 'socks']}.with_indifferent_access }
|
15
|
-
let (:patch3) { {name: ['foo2', 'foo3']}.with_indifferent_access }
|
16
|
-
|
17
|
-
it 'should patch the snapshot' do
|
18
|
-
foo1.apply(patch1).snapshot.should include(name: 'foo2')
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should raise on non-applying patch' do
|
22
|
-
expect { foo1.apply(patch2) }.to raise_error(HasVersions::ApplyFailed)
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'should apply patches adding attributes', :focus => true do
|
26
|
-
foo2.apply(patch2).snapshot.should include(name: 'foo1')
|
27
|
-
foo2.apply(patch2).snapshot.should include(stripey: 'socks')
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'should apply multiple patches' do
|
31
|
-
foo1.apply(patch1, patch3).snapshot.should include(name: 'foo3')
|
32
|
-
end
|
33
|
-
end
|