kintsugi 0.2.0 → 0.4.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile +0 -2
- data/README.md +16 -5
- data/bin/kintsugi +9 -1
- data/kintsugi.gemspec +1 -1
- data/lib/kintsugi/apply_change_to_project.rb +77 -40
- data/lib/kintsugi/cli.rb +109 -8
- data/lib/kintsugi/error.rb +9 -0
- data/lib/kintsugi/version.rb +1 -1
- data/lib/kintsugi.rb +10 -8
- data/spec/kintsugi_apply_change_to_project_spec.rb +95 -4
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7efc466f1583ea53deda2b4e77f030a1072ac4e93f8d8687f5eca22e2ea1d60c
|
4
|
+
data.tar.gz: 0c9dd29ad25b68fbb8c7d2b3b7ce4113e95a341234edaa3d3e11277dd691fc04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee93b9b5f72d5ab35fb37a6fcc5464815e9ea13a9a985296d9132bd04be02edc9f542564b6cf5f495bc1a680b9b06a317bed0c709347ca641f5b31bb3145796e
|
7
|
+
data.tar.gz: c89520181be68ea61548b989a957e3d7fea7cef637b5a5cfa962ca3634b1db8d0e85906f947ea8868fa3a7ec5feed558d0ea156a77887f59d1123e4865bc6062
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -40,15 +40,22 @@ And see the magic happen! :sparkles:
|
|
40
40
|
|
41
41
|
### Git merge driver
|
42
42
|
|
43
|
-
|
43
|
+
You can setup Kintsugi to automatically resolve conflicts that occur in `pbxproj` files when such conflicts occur.
|
44
44
|
|
45
|
-
|
45
|
+
#### Automatic install
|
46
|
+
|
47
|
+
Run `kintsugi install-driver`. This will install Kintsugi as a merge driver globally. Note that Kintsugi needs to be in your `PATH`.
|
48
|
+
|
49
|
+
❗ Do not install with bundler because the installation might succeed even if Kintsugi is not in `PATH`.
|
50
|
+
|
51
|
+
#### Manual install
|
52
|
+
|
53
|
+
- Add Kintsugi as driver to Git config file by running the following:
|
46
54
|
```sh
|
47
55
|
git config merge.kintsugi.name "Kintsugi driver" # Or any other name you prefer
|
48
|
-
git config merge.kintsugi.driver "
|
56
|
+
git config merge.kintsugi.driver "<path_to_kintsugi> driver %O %A %B %P"
|
49
57
|
```
|
50
58
|
|
51
|
-
`kintsugi` should be in your `PATH`.
|
52
59
|
Run `git config` with `--global` to add this to the global config file.
|
53
60
|
|
54
61
|
- Add the following line to the `.gitattributes` file at the root of the repository:
|
@@ -65,7 +72,11 @@ See our [Contribution guidelines](./CONTRIBUTING.md).
|
|
65
72
|
|
66
73
|
## Alternatives
|
67
74
|
|
68
|
-
|
75
|
+
All of the alternatives below allow you to generate your Xcode projects based on a spec or manifest. You commit these files to git, and can even remove the `.xcodeproj` files from git.
|
76
|
+
|
77
|
+
- [XcodeGen](https://github.com/yonaskolb/XcodeGen)
|
78
|
+
- [Tuist](https://github.com/tuist)
|
79
|
+
- [Xcake](https://github.com/igor-makarov/xcake)
|
69
80
|
|
70
81
|
## Copyright
|
71
82
|
|
data/bin/kintsugi
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
|
7
7
|
require "kintsugi"
|
8
8
|
require_relative "../lib/kintsugi/cli"
|
9
|
+
require_relative "../lib/kintsugi/error"
|
9
10
|
|
10
11
|
def parse_options!(command, argv)
|
11
12
|
options = {}
|
@@ -28,4 +29,11 @@ command =
|
|
28
29
|
end
|
29
30
|
|
30
31
|
options = parse_options!(command, ARGV)
|
31
|
-
|
32
|
+
|
33
|
+
begin
|
34
|
+
command.action.call(options, ARGV)
|
35
|
+
rescue ArgumentError => e
|
36
|
+
puts "#{e.class}: #{e}"
|
37
|
+
rescue Kintsugi::MergeError => e
|
38
|
+
puts e
|
39
|
+
end
|
data/kintsugi.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.add_dependency "xcodeproj", "1.19.0"
|
25
|
+
spec.add_dependency "xcodeproj", ">= 1.19.0", "<= 1.21.0"
|
26
26
|
|
27
27
|
spec.add_development_dependency "rake", "~> 13.0"
|
28
28
|
spec.add_development_dependency "rspec", "~> 3.9"
|
@@ -167,7 +167,7 @@ module Kintsugi
|
|
167
167
|
new_value = nil
|
168
168
|
|
169
169
|
if change.key?(:removed)
|
170
|
-
new_value = apply_removal_to_simple_attribute(old_value, change[:removed])
|
170
|
+
new_value = apply_removal_to_simple_attribute(old_value, change[:removed], change[:added])
|
171
171
|
end
|
172
172
|
|
173
173
|
if change.key?(:added)
|
@@ -183,30 +183,31 @@ module Kintsugi
|
|
183
183
|
new_value
|
184
184
|
end
|
185
185
|
|
186
|
-
def apply_removal_to_simple_attribute(old_value,
|
187
|
-
case
|
186
|
+
def apply_removal_to_simple_attribute(old_value, removed_change, added_change)
|
187
|
+
case removed_change
|
188
188
|
when Array
|
189
|
-
(old_value || []) -
|
189
|
+
(old_value || []) - removed_change
|
190
190
|
when Hash
|
191
191
|
(old_value || {}).reject do |key, value|
|
192
|
-
if value !=
|
193
|
-
raise "Trying to remove value #{
|
194
|
-
"to #{value}. This is considered a conflict that should be
|
192
|
+
if value != removed_change[key] && added_change[key] != value
|
193
|
+
raise MergeError, "Trying to remove value '#{removed_change[key]}' of hash with key " \
|
194
|
+
"'#{key}' but it changed to #{value}. This is considered a conflict that should be " \
|
195
|
+
"resolved manually."
|
195
196
|
end
|
196
197
|
|
197
|
-
|
198
|
+
removed_change.key?(key)
|
198
199
|
end
|
199
200
|
when String
|
200
|
-
if old_value !=
|
201
|
-
raise "Trying to remove value #{
|
202
|
-
"is considered a conflict that should be resolved manually."
|
201
|
+
if old_value != removed_change && !old_value.nil? && added_change != old_value
|
202
|
+
raise MergeError, "Trying to remove value '#{removed_change}', but the existing value " \
|
203
|
+
"is '#{old_value}'. This is considered a conflict that should be resolved manually."
|
203
204
|
end
|
204
205
|
|
205
206
|
nil
|
206
207
|
when nil
|
207
208
|
nil
|
208
209
|
else
|
209
|
-
raise "Unsupported change #{
|
210
|
+
raise MergeError, "Unsupported change #{removed_change} of type #{removed_change.class}"
|
210
211
|
end
|
211
212
|
end
|
212
213
|
|
@@ -219,7 +220,8 @@ module Kintsugi
|
|
219
220
|
new_value = old_value.merge(change)
|
220
221
|
|
221
222
|
unless (old_value.to_a - new_value.to_a).empty?
|
222
|
-
raise "New hash #{change} contains values that conflict with old hash
|
223
|
+
raise MergeError, "New hash #{change} contains values that conflict with old hash " \
|
224
|
+
"#{old_value}"
|
223
225
|
end
|
224
226
|
|
225
227
|
new_value
|
@@ -228,14 +230,15 @@ module Kintsugi
|
|
228
230
|
when nil
|
229
231
|
nil
|
230
232
|
else
|
231
|
-
raise "Unsupported change #{change} of type #{change.class}"
|
233
|
+
raise MergeError, "Unsupported change #{change} of type #{change.class}"
|
232
234
|
end
|
233
235
|
end
|
234
236
|
|
235
237
|
def remove_component(component, change)
|
236
238
|
if component.to_tree_hash != change
|
237
|
-
raise "Trying to remove an object that changed since then. This is
|
238
|
-
"that should be resolved manually. Name of the object is:
|
239
|
+
raise MergeError, "Trying to remove an object that changed since then. This is " \
|
240
|
+
"considered a conflict that should be resolved manually. Name of the object is: " \
|
241
|
+
"'#{component.display_name}'"
|
239
242
|
end
|
240
243
|
|
241
244
|
if change["isa"] == "PBXFileReference"
|
@@ -266,6 +269,8 @@ module Kintsugi
|
|
266
269
|
case change["isa"]
|
267
270
|
when "PBXNativeTarget"
|
268
271
|
add_target(component, change)
|
272
|
+
when "PBXAggregateTarget"
|
273
|
+
add_aggregate_target(component, change)
|
269
274
|
when "PBXFileReference"
|
270
275
|
add_file_reference(component, change)
|
271
276
|
when "PBXGroup"
|
@@ -299,21 +304,36 @@ module Kintsugi
|
|
299
304
|
when "PBXReferenceProxy"
|
300
305
|
add_reference_proxy(component, change)
|
301
306
|
else
|
302
|
-
raise "Trying to add unsupported component type #{change["isa"]}. Full
|
303
|
-
"is: #{change}"
|
307
|
+
raise MergeError, "Trying to add unsupported component type #{change["isa"]}. Full " \
|
308
|
+
"component change is: #{change}"
|
304
309
|
end
|
305
310
|
end
|
306
311
|
|
307
312
|
def add_reference_proxy(containing_component, change)
|
308
313
|
case containing_component
|
309
314
|
when Xcodeproj::Project::PBXBuildFile
|
310
|
-
|
315
|
+
# If there are two file references that refer to the same file, one with a build file and
|
316
|
+
# the other one without, this method will prefer to take the one without the build file.
|
317
|
+
# This assumes that it's preferred to have a file reference with build file than a file
|
318
|
+
# reference without/with two build files.
|
319
|
+
filter_references_without_build_files = lambda do |reference|
|
320
|
+
reference.referrers.find do |referrer|
|
321
|
+
referrer.is_a?(Xcodeproj::Project::PBXBuildFile)
|
322
|
+
end.nil?
|
323
|
+
end
|
324
|
+
file_reference =
|
325
|
+
find_reference_proxy(containing_component.project, change["remoteRef"],
|
326
|
+
reference_filter: filter_references_without_build_files)
|
327
|
+
if file_reference.nil?
|
328
|
+
file_reference = find_reference_proxy(containing_component.project, change["remoteRef"])
|
329
|
+
end
|
330
|
+
containing_component.file_ref = file_reference
|
311
331
|
when Xcodeproj::Project::PBXGroup
|
312
332
|
reference_proxy = containing_component.project.new(Xcodeproj::Project::PBXReferenceProxy)
|
313
333
|
containing_component << reference_proxy
|
314
334
|
add_attributes_to_component(reference_proxy, change)
|
315
335
|
else
|
316
|
-
raise "Trying to add reference proxy to an unsupported component type " \
|
336
|
+
raise MergeError, "Trying to add reference proxy to an unsupported component type " \
|
317
337
|
"#{containing_component.isa}. Change is: #{change}"
|
318
338
|
end
|
319
339
|
end
|
@@ -328,7 +348,7 @@ module Kintsugi
|
|
328
348
|
containing_component.children << variant_group
|
329
349
|
add_attributes_to_component(variant_group, change)
|
330
350
|
else
|
331
|
-
raise "Trying to add variant group to an unsupported component type " \
|
351
|
+
raise MergeError, "Trying to add variant group to an unsupported component type " \
|
332
352
|
"#{containing_component.isa}. Change is: #{change}"
|
333
353
|
end
|
334
354
|
end
|
@@ -424,7 +444,7 @@ module Kintsugi
|
|
424
444
|
when "PBXReferenceProxy"
|
425
445
|
component.remote_ref = container_proxy
|
426
446
|
else
|
427
|
-
raise "Trying to add container item proxy to an unsupported component type " \
|
447
|
+
raise MergeError, "Trying to add container item proxy to an unsupported component type " \
|
428
448
|
"#{containing_component.isa}. Change is: #{change}"
|
429
449
|
end
|
430
450
|
add_attributes_to_component(container_proxy, change, ignore_keys: ["containerPortal"])
|
@@ -459,7 +479,14 @@ module Kintsugi
|
|
459
479
|
end
|
460
480
|
|
461
481
|
def add_subproject_reference(root_object, project_reference_change)
|
462
|
-
|
482
|
+
filter_subproject_without_project_references = lambda do |file_reference|
|
483
|
+
root_object.project_references.find do |project_reference|
|
484
|
+
project_reference.project_ref.uuid == file_reference.uuid
|
485
|
+
end.nil?
|
486
|
+
end
|
487
|
+
subproject_reference =
|
488
|
+
find_file(root_object.project, project_reference_change["ProjectRef"],
|
489
|
+
file_filter: filter_subproject_without_project_references)
|
463
490
|
|
464
491
|
attribute =
|
465
492
|
Xcodeproj::Project::PBXProject.references_by_keys_attributes
|
@@ -489,6 +516,12 @@ module Kintsugi
|
|
489
516
|
add_attributes_to_component(target, change)
|
490
517
|
end
|
491
518
|
|
519
|
+
def add_aggregate_target(root_object, change)
|
520
|
+
target = root_object.project.new(Xcodeproj::Project::PBXAggregateTarget)
|
521
|
+
root_object.project.targets << target
|
522
|
+
add_attributes_to_component(target, change)
|
523
|
+
end
|
524
|
+
|
492
525
|
def add_file_reference(containing_component, change)
|
493
526
|
# base configuration reference and product reference always reference a file that exists
|
494
527
|
# inside a group, therefore in these cases the file is searched for.
|
@@ -510,7 +543,7 @@ module Kintsugi
|
|
510
543
|
file_reference.include_in_index = nil
|
511
544
|
add_attributes_to_component(file_reference, change)
|
512
545
|
else
|
513
|
-
raise "Trying to add file reference to an unsupported component type " \
|
546
|
+
raise MergeError, "Trying to add file reference to an unsupported component type " \
|
514
547
|
"#{containing_component.isa}. Change is: #{change}"
|
515
548
|
end
|
516
549
|
end
|
@@ -525,8 +558,8 @@ module Kintsugi
|
|
525
558
|
new_group = containing_component.project.new(Xcodeproj::Project::PBXGroup)
|
526
559
|
containing_component.children << new_group
|
527
560
|
else
|
528
|
-
raise "Trying to add group to an unsupported component type
|
529
|
-
"Change is: #{change}"
|
561
|
+
raise MergeError, "Trying to add group to an unsupported component type " \
|
562
|
+
"#{containing_component.isa}. Change is: #{change}"
|
530
563
|
end
|
531
564
|
|
532
565
|
add_attributes_to_component(new_group, change)
|
@@ -550,31 +583,35 @@ module Kintsugi
|
|
550
583
|
add_child_to_component(component, added_attribute_element)
|
551
584
|
end
|
552
585
|
else
|
553
|
-
raise "Trying to add attribute of unsupported type '#{change_value.class}'
|
554
|
-
"object #{component}. Attribute name is '#{change_name}'"
|
586
|
+
raise MergeError, "Trying to add attribute of unsupported type '#{change_value.class}' " \
|
587
|
+
"to object #{component}. Attribute name is '#{change_name}'"
|
555
588
|
end
|
556
589
|
end
|
557
590
|
end
|
558
591
|
|
559
|
-
def find_file(project, file_reference_change)
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
592
|
+
def find_file(project, file_reference_change, file_filter: ->(_) { true })
|
593
|
+
file_references = project.files.select do |file_reference|
|
594
|
+
file_reference.path == file_reference_change["path"] && file_filter.call(file_reference)
|
595
|
+
end
|
596
|
+
if file_references.length > 1
|
597
|
+
puts "Debug: Found more than one matching file with path " \
|
598
|
+
"'#{file_reference_change["path"]}'. Using the first one."
|
599
|
+
elsif file_references.empty?
|
600
|
+
puts "Debug: No file reference found for file with path " \
|
601
|
+
"'#{file_reference_change["path"]}'."
|
602
|
+
return
|
569
603
|
end
|
604
|
+
|
605
|
+
file_references.first
|
570
606
|
end
|
571
607
|
|
572
|
-
def find_reference_proxy(project, container_item_proxy_change)
|
608
|
+
def find_reference_proxy(project, container_item_proxy_change, reference_filter: ->(_) { true })
|
573
609
|
reference_proxies = project.root_object.project_references.map do |project_ref_and_products|
|
574
610
|
project_ref_and_products[:product_group].children.find do |product|
|
575
611
|
product.remote_ref.remote_global_id_string ==
|
576
612
|
container_item_proxy_change["remoteGlobalIDString"] &&
|
577
|
-
product.remote_ref.remote_info == container_item_proxy_change["remoteInfo"]
|
613
|
+
product.remote_ref.remote_info == container_item_proxy_change["remoteInfo"] &&
|
614
|
+
reference_filter.call(product)
|
578
615
|
end
|
579
616
|
end.compact
|
580
617
|
|
data/lib/kintsugi/cli.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# Created by Ben Yohay.
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
+
require "fileutils"
|
5
6
|
require "optparse"
|
6
7
|
|
7
8
|
require_relative "version"
|
@@ -12,12 +13,14 @@ module Kintsugi
|
|
12
13
|
# Subcommands of Kintsugi CLI.
|
13
14
|
attr_reader :subcommands
|
14
15
|
|
15
|
-
# Root command Kintsugi CLI.
|
16
|
+
# Root command of Kintsugi CLI.
|
16
17
|
attr_reader :root_command
|
17
18
|
|
18
19
|
def initialize
|
19
20
|
@subcommands = {
|
20
|
-
"driver" => create_driver_subcommand
|
21
|
+
"driver" => create_driver_subcommand,
|
22
|
+
"install-driver" => create_install_driver_subcommand,
|
23
|
+
"uninstall-driver" => create_uninstall_driver_subcommand
|
21
24
|
}.freeze
|
22
25
|
@root_command = create_root_command
|
23
26
|
end
|
@@ -27,7 +30,7 @@ module Kintsugi
|
|
27
30
|
Command = Struct.new(:option_parser, :action, :description, keyword_init: true)
|
28
31
|
|
29
32
|
def create_driver_subcommand
|
30
|
-
|
33
|
+
option_parser =
|
31
34
|
OptionParser.new do |opts|
|
32
35
|
opts.banner = "Usage: kintsugi driver BASE OURS THEIRS ORIGINAL_FILE_PATH\n" \
|
33
36
|
"Uses Kintsugi as a Git merge driver. Parameters " \
|
@@ -40,7 +43,7 @@ module Kintsugi
|
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
43
|
-
driver_action = lambda { |_, arguments
|
46
|
+
driver_action = lambda { |_, arguments|
|
44
47
|
if arguments.count != 4
|
45
48
|
puts "Incorrect number of arguments to 'driver' subcommand\n\n"
|
46
49
|
puts option_parser
|
@@ -50,14 +53,112 @@ module Kintsugi
|
|
50
53
|
}
|
51
54
|
|
52
55
|
Command.new(
|
53
|
-
option_parser:
|
56
|
+
option_parser: option_parser,
|
54
57
|
action: driver_action,
|
55
58
|
description: "3-way merge compatible with Git merge driver"
|
56
59
|
)
|
57
60
|
end
|
58
61
|
|
62
|
+
def create_install_driver_subcommand
|
63
|
+
option_parser =
|
64
|
+
OptionParser.new do |opts|
|
65
|
+
opts.banner = "Usage: kintsugi install-driver\n" \
|
66
|
+
"Installs Kintsugi as a Git merge driver globally. "
|
67
|
+
|
68
|
+
opts.on("-h", "--help", "Prints this help") do
|
69
|
+
puts opts
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
action = lambda { |_, arguments|
|
75
|
+
if arguments.count != 0
|
76
|
+
puts "Incorrect number of arguments to 'install-driver' subcommand\n\n"
|
77
|
+
puts option_parser
|
78
|
+
exit(1)
|
79
|
+
end
|
80
|
+
|
81
|
+
if `which kintsugi`.chomp.empty?
|
82
|
+
puts "Can only install Kintsugi globally if Kintsugi is in your PATH"
|
83
|
+
exit(1)
|
84
|
+
end
|
85
|
+
|
86
|
+
install_kintsugi_driver_globally
|
87
|
+
puts "Done! 🪄"
|
88
|
+
}
|
89
|
+
|
90
|
+
Command.new(
|
91
|
+
option_parser: option_parser,
|
92
|
+
action: action,
|
93
|
+
description: "Installs Kintsugi as a Git merge driver globally"
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def install_kintsugi_driver_globally
|
98
|
+
`git config --global merge.kintsugi.name "Kintsugi driver"`
|
99
|
+
`git config --global merge.kintsugi.driver "kintsugi driver %O %A %B %P"`
|
100
|
+
|
101
|
+
attributes_file_path = global_attributes_file_path
|
102
|
+
FileUtils.mkdir_p(File.dirname(attributes_file_path))
|
103
|
+
|
104
|
+
merge_using_kintsugi_line = "'*.pbxproj merge=kintsugi'"
|
105
|
+
`grep -sqxF #{merge_using_kintsugi_line} "#{attributes_file_path}" \
|
106
|
+
|| echo #{merge_using_kintsugi_line} >> "#{attributes_file_path}"`
|
107
|
+
end
|
108
|
+
|
109
|
+
def global_attributes_file_path
|
110
|
+
# The logic to decide the path to the global attributes file is described at:
|
111
|
+
# https://git-scm.com/docs/gitattributes.
|
112
|
+
config_attributes_file_path = `git config --global core.attributesfile`
|
113
|
+
return config_attributes_file_path unless config_attributes_file_path.empty?
|
114
|
+
|
115
|
+
if ENV["XDG_CONFIG_HOME"].nil? || ENV["XDG_CONFIG_HOME"].empty?
|
116
|
+
File.join(ENV["HOME"], ".config/git/attributes")
|
117
|
+
else
|
118
|
+
File.join(ENV["XDG_CONFIG_HOME"], "git/attributes")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def create_uninstall_driver_subcommand
|
123
|
+
option_parser =
|
124
|
+
OptionParser.new do |opts|
|
125
|
+
opts.banner = "Usage: kintsugi uninstall-driver\n" \
|
126
|
+
"Uninstalls Kintsugi as a Git merge driver that was previously installed globally."
|
127
|
+
|
128
|
+
opts.on("-h", "--help", "Prints this help") do
|
129
|
+
puts opts
|
130
|
+
exit
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
action = lambda { |_, arguments|
|
135
|
+
if arguments.count != 0
|
136
|
+
puts "Incorrect number of arguments to 'uninstall-driver' subcommand\n\n"
|
137
|
+
puts option_parser
|
138
|
+
exit(1)
|
139
|
+
end
|
140
|
+
|
141
|
+
uninstall_kintsugi_driver_globally
|
142
|
+
puts "Done!"
|
143
|
+
}
|
144
|
+
|
145
|
+
Command.new(
|
146
|
+
option_parser: option_parser,
|
147
|
+
action: action,
|
148
|
+
description: "Uninstalls Kintsugi as a Git merge driver that was previously installed " \
|
149
|
+
"globally."
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
def uninstall_kintsugi_driver_globally
|
154
|
+
`git config --global --unset merge.kintsugi.name`
|
155
|
+
`git config --global --unset merge.kintsugi.driver`
|
156
|
+
|
157
|
+
`sed -i '' '/\*.pbxproj\ merge=kintsugi/d' "#{global_attributes_file_path}"`
|
158
|
+
end
|
159
|
+
|
59
160
|
def create_root_command
|
60
|
-
|
161
|
+
option_parser = OptionParser.new do |opts|
|
61
162
|
opts.banner = "Kintsugi, version #{Version::STRING}\n" \
|
62
163
|
"Copyright (c) 2021 Lightricks\n\n" \
|
63
164
|
"Usage: kintsugi <pbxproj_filepath> [options]\n" \
|
@@ -83,7 +184,7 @@ module Kintsugi
|
|
83
184
|
opts.on_tail("\nSUBCOMMANDS\n#{subcommands_descriptions}")
|
84
185
|
end
|
85
186
|
|
86
|
-
root_action = lambda { |options, arguments
|
187
|
+
root_action = lambda { |options, arguments|
|
87
188
|
if arguments.count != 1
|
88
189
|
puts "Incorrect number of arguments\n\n"
|
89
190
|
puts option_parser
|
@@ -96,7 +197,7 @@ module Kintsugi
|
|
96
197
|
}
|
97
198
|
|
98
199
|
Command.new(
|
99
|
-
option_parser:
|
200
|
+
option_parser: option_parser,
|
100
201
|
action: root_action,
|
101
202
|
description: nil
|
102
203
|
)
|
data/lib/kintsugi/version.rb
CHANGED
data/lib/kintsugi.rb
CHANGED
@@ -2,12 +2,14 @@
|
|
2
2
|
# Created by Ben Yohay.
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
+
require "json"
|
5
6
|
require "tmpdir"
|
6
7
|
require "tempfile"
|
7
8
|
require "xcodeproj"
|
8
9
|
|
9
10
|
require_relative "kintsugi/xcodeproj_extensions"
|
10
11
|
require_relative "kintsugi/apply_change_to_project"
|
12
|
+
require_relative "kintsugi/error"
|
11
13
|
|
12
14
|
module Kintsugi
|
13
15
|
class << self
|
@@ -16,15 +18,15 @@ module Kintsugi
|
|
16
18
|
# @param [String] project_file_path
|
17
19
|
# Project to which to apply the changes.
|
18
20
|
#
|
19
|
-
# @param [String]
|
21
|
+
# @param [String] changes_output_path
|
20
22
|
# Path to where the changes to apply to the project are written in JSON format.
|
21
23
|
#
|
22
24
|
# @raise [ArgumentError]
|
23
|
-
# If the file extension is not `pbxproj
|
25
|
+
# If the file extension is not `pbxproj`, or the file doesn't exist, or if no rebase,
|
26
|
+
# cherry-pick, or merge is in progress
|
24
27
|
#
|
25
|
-
# @raise [
|
26
|
-
# If
|
27
|
-
# opened, or there was an error applying the change to the project.
|
28
|
+
# @raise [MergeError]
|
29
|
+
# If there was an error applying the change to the project.
|
28
30
|
#
|
29
31
|
# @return [void]
|
30
32
|
def resolve_conflicts(project_file_path, changes_output_path)
|
@@ -58,7 +60,7 @@ module Kintsugi
|
|
58
60
|
# @param [String] original_project_path
|
59
61
|
# Path to the original path of the file.
|
60
62
|
#
|
61
|
-
# @raise [
|
63
|
+
# @raise [MergeError]
|
62
64
|
# If there was an error applying the change to the project.
|
63
65
|
#
|
64
66
|
# @return [void]
|
@@ -103,8 +105,8 @@ module Kintsugi
|
|
103
105
|
|
104
106
|
Dir.chdir(File.dirname(project_file_path)) do
|
105
107
|
unless file_has_base_ours_and_theirs_versions?(project_file_path)
|
106
|
-
raise ArgumentError, "File '#{project_file_path}' doesn't have conflicts,
|
107
|
-
"merge is not possible."
|
108
|
+
raise ArgumentError, "File '#{project_file_path}' doesn't have conflicts, " \
|
109
|
+
"or a 3-way merge is not possible."
|
108
110
|
end
|
109
111
|
end
|
110
112
|
end
|
@@ -38,6 +38,18 @@ describe Kintsugi, :apply_change_to_project do
|
|
38
38
|
expect(base_project).to be_equivalent_to_project(theirs_project)
|
39
39
|
end
|
40
40
|
|
41
|
+
it "adds new aggregate target" do
|
42
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
43
|
+
theirs_project.new_aggregate_target("foo")
|
44
|
+
|
45
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
46
|
+
|
47
|
+
described_class.apply_change_to_project(base_project, changes_to_apply)
|
48
|
+
base_project.save
|
49
|
+
|
50
|
+
expect(base_project).to be_equivalent_to_project(theirs_project)
|
51
|
+
end
|
52
|
+
|
41
53
|
it "adds new subproject" do
|
42
54
|
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
43
55
|
add_new_subproject_to_project(theirs_project, "foo", "foo")
|
@@ -50,6 +62,26 @@ describe Kintsugi, :apply_change_to_project do
|
|
50
62
|
expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
|
51
63
|
end
|
52
64
|
|
65
|
+
it "adds subproject that already exists" do
|
66
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
67
|
+
|
68
|
+
subproject = add_new_subproject_to_project(theirs_project, "foo", "foo")
|
69
|
+
theirs_project.save
|
70
|
+
|
71
|
+
ours_project = create_copy_of_project(base_project.path, "ours")
|
72
|
+
add_existing_subproject_to_project(ours_project, subproject, "foo")
|
73
|
+
|
74
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
75
|
+
|
76
|
+
described_class.apply_change_to_project(ours_project, changes_to_apply)
|
77
|
+
ours_project.save
|
78
|
+
|
79
|
+
expect(ours_project.root_object.project_references[0][:project_ref].uuid)
|
80
|
+
.not_to equal(ours_project.root_object.project_references[1][:project_ref].uuid)
|
81
|
+
expect(ours_project.root_object.project_references[0][:project_ref].proxy_containers).not_to be_empty
|
82
|
+
expect(ours_project.root_object.project_references[1][:project_ref].proxy_containers).not_to be_empty
|
83
|
+
end
|
84
|
+
|
53
85
|
# Checks that the order the changes are applied in is correct.
|
54
86
|
it "adds new subproject and reference to its framework" do
|
55
87
|
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
@@ -144,6 +176,23 @@ describe Kintsugi, :apply_change_to_project do
|
|
144
176
|
expect(base_project).to be_equivalent_to_project(theirs_project)
|
145
177
|
end
|
146
178
|
|
179
|
+
it "changes simple attribute of a file that has a build file" do
|
180
|
+
target = base_project.new_target("com.apple.product-type.library.static", "bar", :ios)
|
181
|
+
file_reference = base_project.main_group.find_file_by_path(filepath)
|
182
|
+
target.frameworks_build_phase.add_file_reference(file_reference)
|
183
|
+
base_project.save
|
184
|
+
|
185
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
186
|
+
file_reference = theirs_project.main_group.find_file_by_path(filepath)
|
187
|
+
file_reference.include_in_index = "4"
|
188
|
+
|
189
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
190
|
+
|
191
|
+
described_class.apply_change_to_project(base_project, changes_to_apply)
|
192
|
+
|
193
|
+
expect(base_project).to be_equivalent_to_project(theirs_project)
|
194
|
+
end
|
195
|
+
|
147
196
|
it "removes build files of a removed file" do
|
148
197
|
target = base_project.new_target("com.apple.product-type.library.static", "foo", :ios)
|
149
198
|
target.source_build_phase.add_file_reference(
|
@@ -417,6 +466,30 @@ describe Kintsugi, :apply_change_to_project do
|
|
417
466
|
expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
|
418
467
|
end
|
419
468
|
|
469
|
+
it "adds build file to a file reference that already exist" do
|
470
|
+
file_reference = base_project.main_group.new_reference("bar")
|
471
|
+
base_project.targets[0].frameworks_build_phase.add_file_reference(file_reference)
|
472
|
+
|
473
|
+
base_project.main_group.new_reference("bar")
|
474
|
+
|
475
|
+
base_project.save
|
476
|
+
|
477
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
478
|
+
|
479
|
+
theirs_file_reference = theirs_project.main_group.files.find do |file|
|
480
|
+
!file.referrers.find { |referrer| referrer.is_a?(Xcodeproj::Project::PBXBuildFile) } &&
|
481
|
+
file.display_name == "bar"
|
482
|
+
end
|
483
|
+
theirs_project.targets[0].frameworks_build_phase.add_file_reference(theirs_file_reference)
|
484
|
+
|
485
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
486
|
+
|
487
|
+
described_class.apply_change_to_project(base_project, changes_to_apply)
|
488
|
+
base_project.save
|
489
|
+
|
490
|
+
expect(base_project).to be_equivalent_to_project(theirs_project)
|
491
|
+
end
|
492
|
+
|
420
493
|
it "adds file reference to build file" do
|
421
494
|
file_reference = base_project.main_group.new_reference("bar")
|
422
495
|
|
@@ -789,7 +862,25 @@ describe Kintsugi, :apply_change_to_project do
|
|
789
862
|
expect(ours_project).to be_equivalent_to_project(theirs_project)
|
790
863
|
end
|
791
864
|
|
792
|
-
it "
|
865
|
+
it "doesn't throw if existing attribute target change is same as added change" do
|
866
|
+
base_project.root_object.attributes["TargetAttributes"] = {"foo" => "1140"}
|
867
|
+
base_project.save
|
868
|
+
|
869
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
870
|
+
theirs_project.root_object.attributes["TargetAttributes"]["foo"] = "1111"
|
871
|
+
|
872
|
+
ours_project = create_copy_of_project(base_project.path, "ours")
|
873
|
+
ours_project.root_object.attributes["TargetAttributes"]["foo"] = "1111"
|
874
|
+
|
875
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
876
|
+
|
877
|
+
described_class.apply_change_to_project(ours_project, changes_to_apply)
|
878
|
+
ours_project.save
|
879
|
+
|
880
|
+
expect(ours_project).to be_equivalent_to_project(theirs_project)
|
881
|
+
end
|
882
|
+
|
883
|
+
it "identifies subproject added at separate times when adding a product to the subproject" do
|
793
884
|
framework_filename = "baz"
|
794
885
|
|
795
886
|
subproject = new_subproject("subproj", framework_filename)
|
@@ -870,10 +961,10 @@ describe Kintsugi, :apply_change_to_project do
|
|
870
961
|
file_reference.path == subproject_product_name
|
871
962
|
end.remove_from_project
|
872
963
|
|
873
|
-
project.root_object.project_references[
|
964
|
+
project.root_object.project_references[-1][:product_group] =
|
874
965
|
project.new(Xcodeproj::Project::PBXGroup)
|
875
|
-
project.root_object.project_references[
|
876
|
-
project.root_object.project_references[
|
966
|
+
project.root_object.project_references[-1][:product_group].name = "Products"
|
967
|
+
project.root_object.project_references[-1][:product_group] <<
|
877
968
|
create_reference_proxy_from_product_reference(project, subproject_reference,
|
878
969
|
subproject.products_group.files[0])
|
879
970
|
end
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kintsugi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Yohay
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: xcodeproj
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.19.0
|
20
|
+
- - "<="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.21.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: 1.19.0
|
30
|
+
- - "<="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.21.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rake
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,6 +123,7 @@ files:
|
|
117
123
|
- lib/kintsugi.rb
|
118
124
|
- lib/kintsugi/apply_change_to_project.rb
|
119
125
|
- lib/kintsugi/cli.rb
|
126
|
+
- lib/kintsugi/error.rb
|
120
127
|
- lib/kintsugi/utils.rb
|
121
128
|
- lib/kintsugi/version.rb
|
122
129
|
- lib/kintsugi/xcodeproj_extensions.rb
|
@@ -143,7 +150,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
150
|
- !ruby/object:Gem::Version
|
144
151
|
version: '0'
|
145
152
|
requirements: []
|
146
|
-
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.7.6.3
|
147
155
|
signing_key:
|
148
156
|
specification_version: 4
|
149
157
|
summary: pbxproj files git conflicts solver
|