kintsugi 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +1 -1
- data/.github/workflows/tests.yml +1 -1
- data/README.md +10 -0
- data/assets/interactive-conflict-resolution.png +0 -0
- data/kintsugi.gemspec +1 -0
- data/lib/kintsugi/apply_change_to_project.rb +204 -122
- data/lib/kintsugi/cli.rb +25 -8
- data/lib/kintsugi/conflict_resolver.rb +130 -0
- data/lib/kintsugi/merge.rb +7 -4
- data/lib/kintsugi/settings.rb +8 -0
- data/lib/kintsugi/version.rb +1 -1
- data/lib/kintsugi/xcodeproj_extensions.rb +49 -1
- data/spec/kintsugi_apply_change_to_project_spec.rb +605 -113
- data/spec/kintsugi_integration_spec.rb +2 -1
- metadata +19 -3
@@ -4,10 +4,11 @@
|
|
4
4
|
|
5
5
|
require "xcodeproj"
|
6
6
|
|
7
|
-
require_relative "
|
7
|
+
require_relative "conflict_resolver"
|
8
8
|
require_relative "error"
|
9
9
|
require_relative "settings"
|
10
10
|
require_relative "xcodeproj_extensions"
|
11
|
+
require_relative "utils"
|
11
12
|
|
12
13
|
class Array
|
13
14
|
# Converts an array of arrays of size 2 into a multimap, mapping the first element of each
|
@@ -33,10 +34,18 @@ module Kintsugi
|
|
33
34
|
# Xcodeproj::Differ#project_diff where its `key_1` and `key_2` parameters have values of
|
34
35
|
# `:added` and `:removed` respectively.
|
35
36
|
#
|
37
|
+
# @param [Xcodeproj::Project] change_source_project
|
38
|
+
# Project from which `change` to apply was created. Used for providing information about
|
39
|
+
# components that are no longer available in `project`.
|
40
|
+
|
36
41
|
# @return [void]
|
37
|
-
def apply_change_to_project(project, change)
|
42
|
+
def apply_change_to_project(project, change, change_source_project)
|
38
43
|
return unless change&.key?("rootObject")
|
39
44
|
|
45
|
+
@change_source_project = change_source_project
|
46
|
+
@ignored_components_group_paths = []
|
47
|
+
@created_components_group_paths = []
|
48
|
+
|
40
49
|
# We iterate over the main group and project references first because they might create file
|
41
50
|
# or project references that are referenced in other parts.
|
42
51
|
unless change["rootObject"]["mainGroup"].nil?
|
@@ -96,17 +105,23 @@ module Kintsugi
|
|
96
105
|
entries + group_entries
|
97
106
|
end
|
98
107
|
|
99
|
-
def apply_group_additions(project, additions)
|
108
|
+
def apply_group_additions(project, additions, force_create_containing_group: false)
|
100
109
|
additions.each do |change, path|
|
101
110
|
next unless %w[PBXGroup PBXVariantGroup].include?(change["isa"])
|
102
111
|
|
103
112
|
group_type = Module.const_get("Xcodeproj::Project::#{change["isa"]}")
|
104
|
-
containing_group =
|
113
|
+
containing_group = project.group_or_file_at_path(path)
|
105
114
|
|
106
115
|
if containing_group.nil?
|
107
|
-
|
108
|
-
|
109
|
-
|
116
|
+
display_name = change["displayName"]
|
117
|
+
if !force_create_containing_group &&
|
118
|
+
!ConflictResolver.create_nonexistent_group_when_adding_subgroup?(path, display_name)
|
119
|
+
@ignored_components_group_paths.append(join_path(path, display_name))
|
120
|
+
next
|
121
|
+
end
|
122
|
+
|
123
|
+
@created_components_group_paths.append(join_path(path, display_name))
|
124
|
+
containing_group = create_nonexistent_groupable_component(project, path)
|
110
125
|
end
|
111
126
|
|
112
127
|
next if !Settings.allow_duplicates &&
|
@@ -127,7 +142,7 @@ module Kintsugi
|
|
127
142
|
end
|
128
143
|
end
|
129
144
|
|
130
|
-
def apply_file_changes(project, additions, removals)
|
145
|
+
def apply_file_changes(project, additions, removals, force_create_containing_group: false)
|
131
146
|
def file_reference_key(change)
|
132
147
|
[change["name"], change["path"], change["sourceTree"]]
|
133
148
|
end
|
@@ -147,13 +162,19 @@ module Kintsugi
|
|
147
162
|
end.to_h
|
148
163
|
|
149
164
|
file_additions.each do |change, path|
|
150
|
-
containing_group =
|
165
|
+
containing_group = project.group_or_file_at_path(path)
|
151
166
|
change_key = file_reference_key(change)
|
152
167
|
|
153
168
|
if containing_group.nil?
|
154
|
-
|
155
|
-
|
156
|
-
|
169
|
+
if !force_create_containing_group &&
|
170
|
+
!ConflictResolver.create_nonexistent_group_when_adding_file?(path,
|
171
|
+
change["displayName"])
|
172
|
+
@ignored_components_group_paths.append(join_path(path, change["displayName"]))
|
173
|
+
next
|
174
|
+
end
|
175
|
+
|
176
|
+
@created_components_group_paths.append(join_path(path, change["displayName"]))
|
177
|
+
containing_group = create_nonexistent_groupable_component(project, path)
|
157
178
|
end
|
158
179
|
|
159
180
|
if (removal_keys_to_references[change_key] || []).empty?
|
@@ -194,9 +215,17 @@ module Kintsugi
|
|
194
215
|
|
195
216
|
def apply_group_and_file_diffs(project, diffs)
|
196
217
|
diffs.each do |change, path|
|
197
|
-
component = project
|
218
|
+
component = project.group_or_file_at_path(path)
|
219
|
+
|
220
|
+
if component.nil? && change&.keys != ["children"]
|
221
|
+
unless ConflictResolver.create_nonexistent_component_when_changing_it?(path)
|
222
|
+
@ignored_components_group_paths.append(path)
|
223
|
+
next
|
224
|
+
end
|
198
225
|
|
199
|
-
|
226
|
+
@created_components_group_paths.append(path)
|
227
|
+
component = create_nonexistent_groupable_component(project, path)
|
228
|
+
end
|
200
229
|
|
201
230
|
change.each do |subchange_name, subchange|
|
202
231
|
next if subchange_name == "children"
|
@@ -206,6 +235,25 @@ module Kintsugi
|
|
206
235
|
end
|
207
236
|
end
|
208
237
|
|
238
|
+
def create_nonexistent_groupable_component(project, path)
|
239
|
+
source_project_component = @change_source_project.group_or_file_at_path(path)
|
240
|
+
component_change = source_project_component.to_tree_hash
|
241
|
+
containing_group_path = parent_group_path(path)
|
242
|
+
|
243
|
+
case source_project_component
|
244
|
+
when Xcodeproj::Project::PBXFileReference
|
245
|
+
apply_file_changes(project, [[component_change, containing_group_path]], [],
|
246
|
+
force_create_containing_group: true)
|
247
|
+
when Xcodeproj::Project::PBXGroup
|
248
|
+
apply_group_additions(project, [[component_change, containing_group_path]],
|
249
|
+
force_create_containing_group: true)
|
250
|
+
else
|
251
|
+
raise MergeError, "Component should either be a group or a file reference. " \
|
252
|
+
"Instead got: #{source_project_component}"
|
253
|
+
end
|
254
|
+
project.group_or_file_at_path(path)
|
255
|
+
end
|
256
|
+
|
209
257
|
def apply_group_removals(project, removals)
|
210
258
|
removals.sort_by(&:last).reverse.each do |change, path|
|
211
259
|
next unless %w[PBXGroup PBXVariantGroup].include?(change["isa"])
|
@@ -236,7 +284,7 @@ module Kintsugi
|
|
236
284
|
component = replace_component_with_new_type(parent_component, attribute_name, change)
|
237
285
|
change = change_for_component_of_new_type(component, change)
|
238
286
|
else
|
239
|
-
component = child_component(parent_component,
|
287
|
+
component = child_component(parent_component, change_name)
|
240
288
|
end
|
241
289
|
|
242
290
|
if change[:removed].is_a?(Hash)
|
@@ -244,8 +292,7 @@ module Kintsugi
|
|
244
292
|
elsif change[:removed].is_a?(Array)
|
245
293
|
unless component.nil?
|
246
294
|
(change[:removed]).each do |removed_change|
|
247
|
-
child = child_component_of_object_list(component, removed_change
|
248
|
-
removed_change["displayName"])
|
295
|
+
child = child_component_of_object_list(component, removed_change["displayName"])
|
249
296
|
remove_component(child, removed_change)
|
250
297
|
end
|
251
298
|
end
|
@@ -255,6 +302,7 @@ module Kintsugi
|
|
255
302
|
|
256
303
|
if change[:added].is_a?(Hash)
|
257
304
|
add_child_to_component(parent_component, change[:added], change_path)
|
305
|
+
component = child_component(parent_component, change_name)
|
258
306
|
elsif change[:added].is_a?(Array)
|
259
307
|
(change[:added]).each do |added_change|
|
260
308
|
add_child_to_component(parent_component, added_change, change_path)
|
@@ -265,15 +313,58 @@ module Kintsugi
|
|
265
313
|
|
266
314
|
subchanges_of_change(change).each do |subchange_name, subchange|
|
267
315
|
if component.nil?
|
268
|
-
|
269
|
-
|
270
|
-
"conflict that should be resolved manually."
|
316
|
+
component = resolve_nonexistent_component(parent_component, change_path)
|
317
|
+
break if component.nil?
|
271
318
|
end
|
272
319
|
|
273
320
|
apply_change_to_component(component, subchange_name, subchange, change_path)
|
274
321
|
end
|
275
322
|
end
|
276
323
|
|
324
|
+
def resolve_nonexistent_component(parent_component, change_path)
|
325
|
+
source_project_component = component_at_path(@change_source_project, change_path)
|
326
|
+
group_path = group_path_of_group_based_component(source_project_component)
|
327
|
+
|
328
|
+
if group_path
|
329
|
+
should_create_component = @created_components_group_paths.include?(group_path) ||
|
330
|
+
(!@ignored_components_group_paths.include?(group_path) &&
|
331
|
+
!ConflictResolver.create_nonexistent_component_when_changing_it?(change_path))
|
332
|
+
return unless should_create_component
|
333
|
+
elsif !ConflictResolver.create_nonexistent_component_when_changing_it?(change_path)
|
334
|
+
return
|
335
|
+
end
|
336
|
+
|
337
|
+
non_object_list_parent =
|
338
|
+
if parent_component.is_a?(Xcodeproj::Project::ObjectList)
|
339
|
+
parent_component.owner
|
340
|
+
else
|
341
|
+
parent_component
|
342
|
+
end
|
343
|
+
add_child_to_component(non_object_list_parent, source_project_component.to_tree_hash,
|
344
|
+
change_path)
|
345
|
+
component_at_path(non_object_list_parent.project, change_path)
|
346
|
+
end
|
347
|
+
|
348
|
+
def group_path_of_group_based_component(component)
|
349
|
+
if component.is_a?(Xcodeproj::Project::PBXBuildFile) && !component.file_ref.nil?
|
350
|
+
component.file_ref.hierarchy_path.delete_prefix("/")
|
351
|
+
elsif component.is_a?(Xcodeproj::Project::PBXFileReference) ||
|
352
|
+
component.is_a?(Xcodeproj::Project::PBXGroup)
|
353
|
+
component.hierarchy_path.delete_prefix("/")
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def component_at_path(project, path)
|
358
|
+
current_component = project
|
359
|
+
until path["/"].nil?
|
360
|
+
change_name = path.split("/")[0]
|
361
|
+
current_component = child_component(current_component, change_name)
|
362
|
+
path = path.delete_prefix("#{change_name}/")
|
363
|
+
end
|
364
|
+
|
365
|
+
child_component(current_component, path)
|
366
|
+
end
|
367
|
+
|
277
368
|
def subchanges_of_change(change)
|
278
369
|
if change.key?(:diff)
|
279
370
|
change[:diff]
|
@@ -341,21 +432,17 @@ module Kintsugi
|
|
341
432
|
end
|
342
433
|
end
|
343
434
|
|
344
|
-
def child_component(component,
|
435
|
+
def child_component(component, change_name)
|
345
436
|
if component.is_a?(Xcodeproj::Project::ObjectList)
|
346
|
-
child_component_of_object_list(component,
|
437
|
+
child_component_of_object_list(component, change_name)
|
347
438
|
else
|
348
439
|
attribute_name = attribute_name_from_change_name(change_name)
|
349
440
|
component.send(attribute_name)
|
350
441
|
end
|
351
442
|
end
|
352
443
|
|
353
|
-
def child_component_of_object_list(component,
|
354
|
-
|
355
|
-
find_reference_proxy_in_component(component, change["remoteRef"])
|
356
|
-
else
|
357
|
-
component.find { |child| child.display_name == change_name }
|
358
|
-
end
|
444
|
+
def child_component_of_object_list(component, change_name)
|
445
|
+
component.find { |child| child.display_name == change_name }
|
359
446
|
end
|
360
447
|
|
361
448
|
def simple_attribute?(component, attribute_name)
|
@@ -366,18 +453,20 @@ module Kintsugi
|
|
366
453
|
|
367
454
|
def apply_change_to_simple_attribute(component, attribute_name, change)
|
368
455
|
new_attribute_value =
|
369
|
-
simple_attribute_value_with_change(component.send(attribute_name), change)
|
456
|
+
simple_attribute_value_with_change(component.send(attribute_name), change, attribute_name)
|
370
457
|
component.send("#{attribute_name}=", new_attribute_value)
|
371
458
|
end
|
372
459
|
|
373
|
-
def simple_attribute_value_with_change(old_value, change)
|
460
|
+
def simple_attribute_value_with_change(old_value, change, attribute_name)
|
374
461
|
type = simple_attribute_type(old_value, change[:removed], change[:added])
|
375
|
-
new_value = new_simple_attribute_value(type, old_value, change[:removed], change[:added]
|
462
|
+
new_value = new_simple_attribute_value(type, old_value, change[:removed], change[:added],
|
463
|
+
attribute_name)
|
376
464
|
|
377
465
|
subchanges_of_change(change).each do |subchange_name, subchange_value|
|
378
466
|
new_value = new_value || old_value || {}
|
379
467
|
new_value[subchange_name] =
|
380
|
-
simple_attribute_value_with_change(old_value[subchange_name], subchange_value
|
468
|
+
simple_attribute_value_with_change(old_value[subchange_name], subchange_value,
|
469
|
+
subchange_name)
|
381
470
|
end
|
382
471
|
|
383
472
|
new_value
|
@@ -389,71 +478,87 @@ module Kintsugi
|
|
389
478
|
if types.include?(Hash)
|
390
479
|
unless types.to_set.subset?([Hash, NilClass].to_set)
|
391
480
|
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
392
|
-
|
393
|
-
|
481
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added " \
|
482
|
+
"change: '#{added_change}'"
|
394
483
|
end
|
395
484
|
Hash
|
396
485
|
elsif types.include?(Array)
|
397
486
|
unless types.to_set.subset?([Array, String, NilClass].to_set)
|
398
487
|
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
399
|
-
|
400
|
-
|
488
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added " \
|
489
|
+
"change: '#{added_change}'"
|
401
490
|
end
|
402
491
|
Array
|
403
492
|
elsif types.include?(String)
|
404
493
|
unless types.to_set.subset?([String, NilClass].to_set)
|
405
494
|
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
406
|
-
|
407
|
-
|
495
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added " \
|
496
|
+
"change: '#{added_change}'"
|
408
497
|
end
|
409
498
|
String
|
410
499
|
else
|
411
500
|
raise MergeError, "Unsupported types of all of the values. Existing value: " \
|
412
|
-
|
501
|
+
"'#{old_value}', removed change: '#{removed_change}', added change: " \
|
502
|
+
"'#{added_change}'"
|
413
503
|
end
|
414
504
|
end
|
415
505
|
|
416
|
-
def new_simple_attribute_value(type, old_value, removed_change, added_change)
|
506
|
+
def new_simple_attribute_value(type, old_value, removed_change, added_change, attribute_name)
|
417
507
|
if type == Hash
|
418
|
-
new_hash_simple_attribute_value(old_value, removed_change, added_change)
|
508
|
+
new_hash_simple_attribute_value(old_value, removed_change, added_change, attribute_name)
|
419
509
|
elsif type == Array
|
420
|
-
new_array_simple_attribute_value(old_value, removed_change, added_change)
|
510
|
+
new_array_simple_attribute_value(old_value, removed_change, added_change, attribute_name)
|
421
511
|
elsif type == String
|
422
|
-
new_string_simple_attribute_value(old_value, removed_change, added_change)
|
512
|
+
new_string_simple_attribute_value(old_value, removed_change, added_change, attribute_name)
|
423
513
|
else
|
424
514
|
raise MergeError, "Unsupported types of all of the values. Existing value: " \
|
425
|
-
|
515
|
+
"'#{old_value}', removed change: '#{removed_change}', added change: " \
|
516
|
+
"'#{added_change}'"
|
426
517
|
end
|
427
518
|
end
|
428
519
|
|
429
|
-
def new_hash_simple_attribute_value(old_value, removed_change, added_change)
|
520
|
+
def new_hash_simple_attribute_value(old_value, removed_change, added_change, attribute_name)
|
430
521
|
return added_change if ((old_value || {}).to_a - (removed_change || {}).to_a).empty?
|
431
522
|
|
432
523
|
# First apply the added change to see if there are any conflicts with it.
|
433
524
|
new_value = (old_value || {}).merge(added_change || {})
|
525
|
+
conflicting_added_hash_values = (old_value.to_a - new_value.to_a)
|
434
526
|
|
435
|
-
unless
|
436
|
-
|
437
|
-
|
527
|
+
unless conflicting_added_hash_values.empty?
|
528
|
+
override_values = ConflictResolver.override_values_when_keys_already_exist_in_hash?(
|
529
|
+
attribute_name, old_value, added_change
|
530
|
+
)
|
531
|
+
new_value = override_values ? old_value.merge(added_change) : added_change.merge(old_value)
|
438
532
|
end
|
439
533
|
|
440
534
|
if removed_change.nil?
|
441
535
|
return new_value
|
442
536
|
end
|
443
537
|
|
538
|
+
conflicting_removal_hash_values = new_value.select do |key, value|
|
539
|
+
value != removed_change[key] && value != (added_change || {})[key]
|
540
|
+
end
|
541
|
+
|
542
|
+
unless conflicting_removal_hash_values.empty?
|
543
|
+
expected_values =
|
544
|
+
removed_change.select { |key, value| conflicting_removal_hash_values.key?(key) }
|
545
|
+
should_remove_conflicting_values =
|
546
|
+
ConflictResolver.remove_entries_when_unexpected_values_in_hash?(
|
547
|
+
attribute_name, expected_values, conflicting_removal_hash_values
|
548
|
+
)
|
549
|
+
end
|
550
|
+
|
444
551
|
new_value
|
445
552
|
.reject do |key, value|
|
446
|
-
if
|
447
|
-
|
448
|
-
"'#{key}' but it changed to #{value}. This is considered a conflict that should be " \
|
449
|
-
"resolved manually."
|
553
|
+
if conflicting_removal_hash_values.key?(key)
|
554
|
+
next should_remove_conflicting_values
|
450
555
|
end
|
451
556
|
|
452
557
|
removed_change.key?(key)
|
453
558
|
end
|
454
559
|
end
|
455
560
|
|
456
|
-
def new_array_simple_attribute_value(old_value, removed_change, added_change)
|
561
|
+
def new_array_simple_attribute_value(old_value, removed_change, added_change, attribute_name)
|
457
562
|
if old_value.is_a?(String)
|
458
563
|
old_value = [old_value]
|
459
564
|
end
|
@@ -476,10 +581,12 @@ module Kintsugi
|
|
476
581
|
new_value + filtered_added_change
|
477
582
|
end
|
478
583
|
|
479
|
-
def new_string_simple_attribute_value(old_value, removed_change, added_change)
|
584
|
+
def new_string_simple_attribute_value(old_value, removed_change, added_change, attribute_name)
|
480
585
|
if old_value != removed_change && !old_value.nil? && added_change != old_value
|
481
|
-
|
482
|
-
|
586
|
+
use_added_change = ConflictResolver.set_value_to_string_when_unxpected_value?(
|
587
|
+
attribute_name, added_change, removed_change, old_value
|
588
|
+
)
|
589
|
+
return use_added_change ? added_change : old_value
|
483
590
|
end
|
484
591
|
|
485
592
|
added_change
|
@@ -488,26 +595,25 @@ module Kintsugi
|
|
488
595
|
def remove_component(component, change)
|
489
596
|
return if component.nil?
|
490
597
|
|
491
|
-
if component.to_tree_hash != change
|
492
|
-
|
493
|
-
|
494
|
-
"'#{component.display_name}'. Existing component: #{component.to_tree_hash}. " \
|
495
|
-
"Change: #{change}"
|
598
|
+
if component.to_tree_hash != change &&
|
599
|
+
!ConflictResolver.remove_component_when_unexpected_hash?(component, change)
|
600
|
+
return
|
496
601
|
end
|
497
602
|
|
498
|
-
if change["isa"] == "PBXFileReference"
|
499
|
-
|
603
|
+
if change["isa"] == "PBXFileReference" || change["isa"] == "PBXReferenceProxy" ||
|
604
|
+
change["isa"] == "PBXGroup" || change["isa"] == "PBXVariantGroup"
|
605
|
+
remove_build_files_of_file_reference(component)
|
500
606
|
end
|
501
607
|
|
502
608
|
component.remove_from_project
|
503
609
|
end
|
504
610
|
|
505
|
-
def remove_build_files_of_file_reference(file_reference
|
611
|
+
def remove_build_files_of_file_reference(file_reference)
|
506
612
|
# Since the build file's display name depends on the file reference, removing the file
|
507
613
|
# reference before removing it will change the build file's display name which will not be
|
508
614
|
# detected when trying to remove the build file. Therefore, the build files that depend on
|
509
615
|
# the file reference are removed prior to removing the file reference.
|
510
|
-
file_reference.
|
616
|
+
file_reference.referrers.grep(Xcodeproj::Project::PBXBuildFile).each do |build_file|
|
511
617
|
build_file.referrers.each do |referrer|
|
512
618
|
referrer.remove_build_file(build_file)
|
513
619
|
end
|
@@ -565,7 +671,7 @@ module Kintsugi
|
|
565
671
|
add_remote_swift_package_reference(component, change, change_path)
|
566
672
|
else
|
567
673
|
raise MergeError, "Trying to add unsupported component type #{change["isa"]}. Full " \
|
568
|
-
|
674
|
+
"component change is: #{change}"
|
569
675
|
end
|
570
676
|
end
|
571
677
|
|
@@ -581,7 +687,7 @@ module Kintsugi
|
|
581
687
|
containing_component.package_references << remote_swift_package_reference
|
582
688
|
else
|
583
689
|
raise MergeError, "Trying to add remote swift package reference to an unsupported " \
|
584
|
-
|
690
|
+
"component type #{containing_component.isa}. Change is: #{change}"
|
585
691
|
end
|
586
692
|
end
|
587
693
|
|
@@ -597,7 +703,7 @@ module Kintsugi
|
|
597
703
|
containing_component.package_product_dependencies << swift_package_product_dependency
|
598
704
|
else
|
599
705
|
raise MergeError, "Trying to add swift package product dependency to an unsupported " \
|
600
|
-
|
706
|
+
"component type #{containing_component.isa}. Change is: #{change}"
|
601
707
|
end
|
602
708
|
end
|
603
709
|
|
@@ -614,10 +720,10 @@ module Kintsugi
|
|
614
720
|
end.nil?
|
615
721
|
end
|
616
722
|
file_reference =
|
617
|
-
find_reference_proxy(containing_component.project, change
|
723
|
+
find_reference_proxy(containing_component.project, change,
|
618
724
|
reference_filter: filter_references_without_build_files)
|
619
725
|
if file_reference.nil?
|
620
|
-
file_reference = find_reference_proxy(containing_component.project, change
|
726
|
+
file_reference = find_reference_proxy(containing_component.project, change)
|
621
727
|
end
|
622
728
|
containing_component.file_ref = file_reference
|
623
729
|
when Xcodeproj::Project::PBXGroup
|
@@ -630,7 +736,7 @@ module Kintsugi
|
|
630
736
|
add_attributes_to_component(reference_proxy, change, change_path)
|
631
737
|
else
|
632
738
|
raise MergeError, "Trying to add reference proxy to an unsupported component type " \
|
633
|
-
|
739
|
+
"#{containing_component.isa}. Change is: #{change}"
|
634
740
|
end
|
635
741
|
end
|
636
742
|
|
@@ -639,15 +745,11 @@ module Kintsugi
|
|
639
745
|
when Xcodeproj::Project::PBXBuildFile
|
640
746
|
containing_component.file_ref =
|
641
747
|
find_variant_group(containing_component.project, change["displayName"])
|
642
|
-
when Xcodeproj::Project::PBXGroup
|
643
|
-
|
644
|
-
change).nil?
|
645
|
-
raise "Group should have been added already, so this is most likely a bug in Kintsugi" \
|
646
|
-
"Change is: #{change}. Change path: #{change_path}"
|
647
|
-
end
|
748
|
+
when Xcodeproj::Project::PBXGroup
|
749
|
+
# Adding variant groups to groups is handled by another part of the code.
|
648
750
|
else
|
649
751
|
raise MergeError, "Trying to add variant group to an unsupported component type " \
|
650
|
-
|
752
|
+
"#{containing_component.isa}. Change is: #{change}"
|
651
753
|
end
|
652
754
|
end
|
653
755
|
|
@@ -699,7 +801,7 @@ module Kintsugi
|
|
699
801
|
def add_build_file(build_phase, change, change_path)
|
700
802
|
if change["fileRef"].nil?
|
701
803
|
puts "Warning: Trying to add a build file without any file reference to build phase " \
|
702
|
-
|
804
|
+
"'#{build_phase}'"
|
703
805
|
return
|
704
806
|
end
|
705
807
|
|
@@ -748,7 +850,7 @@ module Kintsugi
|
|
748
850
|
component.remote_ref = container_proxy
|
749
851
|
else
|
750
852
|
raise MergeError, "Trying to add container item proxy to an unsupported component type " \
|
751
|
-
|
853
|
+
"#{containing_component.isa}. Change is: #{change}"
|
752
854
|
end
|
753
855
|
add_attributes_to_component(container_proxy, change, change_path,
|
754
856
|
ignore_keys: ["containerPortal"])
|
@@ -772,10 +874,10 @@ module Kintsugi
|
|
772
874
|
|
773
875
|
if container_item_proxies.length > 1
|
774
876
|
puts "Debug: Found more than one potential dependency with name " \
|
775
|
-
|
877
|
+
"'#{container_item_proxy_change["remoteInfo"]}'. Using the first one."
|
776
878
|
elsif container_item_proxies.empty?
|
777
879
|
puts "Warning: No container portal was found for dependency with name " \
|
778
|
-
|
880
|
+
"'#{container_item_proxy_change["remoteInfo"]}'."
|
779
881
|
return
|
780
882
|
end
|
781
883
|
|
@@ -800,8 +902,8 @@ module Kintsugi
|
|
800
902
|
|
801
903
|
unless subproject_reference
|
802
904
|
raise MergeError, "No file reference was found for project reference with change " \
|
803
|
-
|
804
|
-
|
905
|
+
"#{project_reference_change}. This might mean that the file used to " \
|
906
|
+
"exist in the project the but was removed at some point"
|
805
907
|
end
|
806
908
|
|
807
909
|
attribute =
|
@@ -841,8 +943,6 @@ module Kintsugi
|
|
841
943
|
def add_file_reference(containing_component, change, change_path)
|
842
944
|
# base configuration reference and product reference always reference a file that exists
|
843
945
|
# inside a group, therefore in these cases the file is searched for.
|
844
|
-
# In the case of group and variant group, the file can't exist in another group, therefore a
|
845
|
-
# new file reference is always created.
|
846
946
|
case containing_component
|
847
947
|
when Xcodeproj::Project::XCBuildConfiguration
|
848
948
|
containing_component.base_configuration_reference =
|
@@ -852,15 +952,11 @@ module Kintsugi
|
|
852
952
|
find_file(containing_component.project, change["path"])
|
853
953
|
when Xcodeproj::Project::PBXBuildFile
|
854
954
|
containing_component.file_ref = find_file(containing_component.project, change["path"])
|
855
|
-
when Xcodeproj::Project::PBXGroup
|
856
|
-
|
857
|
-
change["path"]).nil?
|
858
|
-
raise "File should have been added already, so this is most likely a bug in Kintsugi" \
|
859
|
-
"Change is: #{change}. Change path: #{change_path}"
|
860
|
-
end
|
955
|
+
when Xcodeproj::Project::PBXGroup
|
956
|
+
# Adding files to groups is handled by another part of the code.
|
861
957
|
else
|
862
958
|
raise MergeError, "Trying to add file reference to an unsupported component type " \
|
863
|
-
|
959
|
+
"#{containing_component.isa}. Change is: #{change}"
|
864
960
|
end
|
865
961
|
end
|
866
962
|
|
@@ -871,11 +967,6 @@ module Kintsugi
|
|
871
967
|
.find { |file| file.path == filepath }
|
872
968
|
end
|
873
969
|
|
874
|
-
def adding_files_and_groups_allowed?(change_path)
|
875
|
-
change_path.start_with?("rootObject/mainGroup") ||
|
876
|
-
change_path.start_with?("rootObject/projectReferences")
|
877
|
-
end
|
878
|
-
|
879
970
|
def add_group(containing_component, change, change_path)
|
880
971
|
case containing_component
|
881
972
|
when Xcodeproj::Project::ObjectDictionary
|
@@ -884,13 +975,11 @@ module Kintsugi
|
|
884
975
|
containing_component[:product_group] = new_group
|
885
976
|
add_attributes_to_component(new_group, change, change_path)
|
886
977
|
when Xcodeproj::Project::PBXGroup
|
887
|
-
|
888
|
-
raise "Group should have been added already, so this is most likely a bug in Kintsugi" \
|
889
|
-
"Change is: #{change}. Change path: #{change_path}"
|
890
|
-
end
|
978
|
+
# Adding groups to groups is handled by another part of the code.
|
891
979
|
else
|
892
980
|
raise MergeError, "Trying to add group to an unsupported component type " \
|
893
|
-
|
981
|
+
"#{containing_component.isa}. Change is: #{change}. Change path: " \
|
982
|
+
"#{change_path}"
|
894
983
|
end
|
895
984
|
end
|
896
985
|
|
@@ -917,7 +1006,7 @@ module Kintsugi
|
|
917
1006
|
end
|
918
1007
|
else
|
919
1008
|
raise MergeError, "Trying to add attribute of unsupported type '#{change_value.class}' " \
|
920
|
-
|
1009
|
+
"to object #{component}. Attribute name is '#{change_name}'"
|
921
1010
|
end
|
922
1011
|
end
|
923
1012
|
end
|
@@ -935,44 +1024,37 @@ module Kintsugi
|
|
935
1024
|
if file_references.length > 1
|
936
1025
|
puts "Debug: Found more than one matching file with path '#{path}'. Using the first one."
|
937
1026
|
elsif file_references.empty?
|
938
|
-
puts "Debug: No file reference found for file with path '#{path}'."
|
939
1027
|
return
|
940
1028
|
end
|
941
1029
|
|
942
1030
|
file_references.first
|
943
1031
|
end
|
944
1032
|
|
945
|
-
def find_reference_proxy(project,
|
1033
|
+
def find_reference_proxy(project, change, reference_filter: ->(_) { true })
|
946
1034
|
reference_proxies = project.root_object.project_references.map do |project_ref_and_products|
|
947
|
-
|
948
|
-
|
949
|
-
|
1035
|
+
project_ref_and_products[:product_group].children.find do |reference_proxy|
|
1036
|
+
reference_proxy.display_name == change["displayName"] &&
|
1037
|
+
reference_filter.call(reference_proxy)
|
1038
|
+
end
|
950
1039
|
end.compact
|
951
1040
|
|
952
1041
|
if reference_proxies.length > 1
|
953
1042
|
puts "Debug: Found more than one matching reference proxy with name " \
|
954
|
-
|
1043
|
+
"'#{change["remoteInfo"]}'. Using the first one."
|
955
1044
|
elsif reference_proxies.empty?
|
956
|
-
puts "Warning: No reference proxy was found for name "
|
957
|
-
"'#{container_item_proxy_change["remoteInfo"]}'."
|
1045
|
+
puts "Warning: No reference proxy was found for name '#{change["remoteInfo"]}'."
|
958
1046
|
return
|
959
1047
|
end
|
960
1048
|
|
961
1049
|
reference_proxies.first
|
962
1050
|
end
|
963
1051
|
|
964
|
-
def find_reference_proxy_in_component(component, container_item_proxy_change,
|
965
|
-
reference_filter: ->(_) { true })
|
966
|
-
component.find do |product|
|
967
|
-
product.remote_ref.remote_global_id_string ==
|
968
|
-
container_item_proxy_change["remoteGlobalIDString"] &&
|
969
|
-
product.remote_ref.remote_info == container_item_proxy_change["remoteInfo"] &&
|
970
|
-
reference_filter.call(product)
|
971
|
-
end
|
972
|
-
end
|
973
|
-
|
974
1052
|
def join_path(left, right)
|
975
1053
|
left.empty? ? right : "#{left}/#{right}"
|
976
1054
|
end
|
1055
|
+
|
1056
|
+
def parent_group_path(group_path)
|
1057
|
+
group_path[/(.*)\//, 1] || ""
|
1058
|
+
end
|
977
1059
|
end
|
978
1060
|
end
|