has_versions 0.4.9 → 0.5.0
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.
- 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
|