kintsugi 0.6.2 → 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 +210 -116
- 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 +686 -108
- 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,12 +105,24 @@ 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)
|
114
|
+
|
115
|
+
if containing_group.nil?
|
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)
|
125
|
+
end
|
105
126
|
|
106
127
|
next if !Settings.allow_duplicates &&
|
107
128
|
!find_group_in_group(containing_group, group_type, change).nil?
|
@@ -121,7 +142,7 @@ module Kintsugi
|
|
121
142
|
end
|
122
143
|
end
|
123
144
|
|
124
|
-
def apply_file_changes(project, additions, removals)
|
145
|
+
def apply_file_changes(project, additions, removals, force_create_containing_group: false)
|
125
146
|
def file_reference_key(change)
|
126
147
|
[change["name"], change["path"], change["sourceTree"]]
|
127
148
|
end
|
@@ -141,9 +162,21 @@ module Kintsugi
|
|
141
162
|
end.to_h
|
142
163
|
|
143
164
|
file_additions.each do |change, path|
|
144
|
-
containing_group =
|
165
|
+
containing_group = project.group_or_file_at_path(path)
|
145
166
|
change_key = file_reference_key(change)
|
146
167
|
|
168
|
+
if containing_group.nil?
|
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)
|
178
|
+
end
|
179
|
+
|
147
180
|
if (removal_keys_to_references[change_key] || []).empty?
|
148
181
|
apply_file_addition(containing_group, change, "rootObject/mainGroup/#{path}")
|
149
182
|
elsif addition_keys_to_paths[change_key].length == 1 &&
|
@@ -182,9 +215,17 @@ module Kintsugi
|
|
182
215
|
|
183
216
|
def apply_group_and_file_diffs(project, diffs)
|
184
217
|
diffs.each do |change, path|
|
185
|
-
component = project
|
218
|
+
component = project.group_or_file_at_path(path)
|
186
219
|
|
187
|
-
|
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
|
225
|
+
|
226
|
+
@created_components_group_paths.append(path)
|
227
|
+
component = create_nonexistent_groupable_component(project, path)
|
228
|
+
end
|
188
229
|
|
189
230
|
change.each do |subchange_name, subchange|
|
190
231
|
next if subchange_name == "children"
|
@@ -194,6 +235,25 @@ module Kintsugi
|
|
194
235
|
end
|
195
236
|
end
|
196
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
|
+
|
197
257
|
def apply_group_removals(project, removals)
|
198
258
|
removals.sort_by(&:last).reverse.each do |change, path|
|
199
259
|
next unless %w[PBXGroup PBXVariantGroup].include?(change["isa"])
|
@@ -224,7 +284,7 @@ module Kintsugi
|
|
224
284
|
component = replace_component_with_new_type(parent_component, attribute_name, change)
|
225
285
|
change = change_for_component_of_new_type(component, change)
|
226
286
|
else
|
227
|
-
component = child_component(parent_component,
|
287
|
+
component = child_component(parent_component, change_name)
|
228
288
|
end
|
229
289
|
|
230
290
|
if change[:removed].is_a?(Hash)
|
@@ -232,8 +292,7 @@ module Kintsugi
|
|
232
292
|
elsif change[:removed].is_a?(Array)
|
233
293
|
unless component.nil?
|
234
294
|
(change[:removed]).each do |removed_change|
|
235
|
-
child = child_component_of_object_list(component, removed_change
|
236
|
-
removed_change["displayName"])
|
295
|
+
child = child_component_of_object_list(component, removed_change["displayName"])
|
237
296
|
remove_component(child, removed_change)
|
238
297
|
end
|
239
298
|
end
|
@@ -243,6 +302,7 @@ module Kintsugi
|
|
243
302
|
|
244
303
|
if change[:added].is_a?(Hash)
|
245
304
|
add_child_to_component(parent_component, change[:added], change_path)
|
305
|
+
component = child_component(parent_component, change_name)
|
246
306
|
elsif change[:added].is_a?(Array)
|
247
307
|
(change[:added]).each do |added_change|
|
248
308
|
add_child_to_component(parent_component, added_change, change_path)
|
@@ -253,15 +313,58 @@ module Kintsugi
|
|
253
313
|
|
254
314
|
subchanges_of_change(change).each do |subchange_name, subchange|
|
255
315
|
if component.nil?
|
256
|
-
|
257
|
-
|
258
|
-
"conflict that should be resolved manually."
|
316
|
+
component = resolve_nonexistent_component(parent_component, change_path)
|
317
|
+
break if component.nil?
|
259
318
|
end
|
260
319
|
|
261
320
|
apply_change_to_component(component, subchange_name, subchange, change_path)
|
262
321
|
end
|
263
322
|
end
|
264
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
|
+
|
265
368
|
def subchanges_of_change(change)
|
266
369
|
if change.key?(:diff)
|
267
370
|
change[:diff]
|
@@ -329,21 +432,17 @@ module Kintsugi
|
|
329
432
|
end
|
330
433
|
end
|
331
434
|
|
332
|
-
def child_component(component,
|
435
|
+
def child_component(component, change_name)
|
333
436
|
if component.is_a?(Xcodeproj::Project::ObjectList)
|
334
|
-
child_component_of_object_list(component,
|
437
|
+
child_component_of_object_list(component, change_name)
|
335
438
|
else
|
336
439
|
attribute_name = attribute_name_from_change_name(change_name)
|
337
440
|
component.send(attribute_name)
|
338
441
|
end
|
339
442
|
end
|
340
443
|
|
341
|
-
def child_component_of_object_list(component,
|
342
|
-
|
343
|
-
find_reference_proxy_in_component(component, change["remoteRef"])
|
344
|
-
else
|
345
|
-
component.find { |child| child.display_name == change_name }
|
346
|
-
end
|
444
|
+
def child_component_of_object_list(component, change_name)
|
445
|
+
component.find { |child| child.display_name == change_name }
|
347
446
|
end
|
348
447
|
|
349
448
|
def simple_attribute?(component, attribute_name)
|
@@ -354,18 +453,20 @@ module Kintsugi
|
|
354
453
|
|
355
454
|
def apply_change_to_simple_attribute(component, attribute_name, change)
|
356
455
|
new_attribute_value =
|
357
|
-
simple_attribute_value_with_change(component.send(attribute_name), change)
|
456
|
+
simple_attribute_value_with_change(component.send(attribute_name), change, attribute_name)
|
358
457
|
component.send("#{attribute_name}=", new_attribute_value)
|
359
458
|
end
|
360
459
|
|
361
|
-
def simple_attribute_value_with_change(old_value, change)
|
460
|
+
def simple_attribute_value_with_change(old_value, change, attribute_name)
|
362
461
|
type = simple_attribute_type(old_value, change[:removed], change[:added])
|
363
|
-
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)
|
364
464
|
|
365
465
|
subchanges_of_change(change).each do |subchange_name, subchange_value|
|
366
466
|
new_value = new_value || old_value || {}
|
367
467
|
new_value[subchange_name] =
|
368
|
-
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)
|
369
470
|
end
|
370
471
|
|
371
472
|
new_value
|
@@ -377,71 +478,87 @@ module Kintsugi
|
|
377
478
|
if types.include?(Hash)
|
378
479
|
unless types.to_set.subset?([Hash, NilClass].to_set)
|
379
480
|
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
380
|
-
|
381
|
-
|
481
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added " \
|
482
|
+
"change: '#{added_change}'"
|
382
483
|
end
|
383
484
|
Hash
|
384
485
|
elsif types.include?(Array)
|
385
486
|
unless types.to_set.subset?([Array, String, NilClass].to_set)
|
386
487
|
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
387
|
-
|
388
|
-
|
488
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added " \
|
489
|
+
"change: '#{added_change}'"
|
389
490
|
end
|
390
491
|
Array
|
391
492
|
elsif types.include?(String)
|
392
493
|
unless types.to_set.subset?([String, NilClass].to_set)
|
393
494
|
raise MergeError, "Cannot apply changes because the types are not compatible. Existing " \
|
394
|
-
|
395
|
-
|
495
|
+
"value: '#{old_value}', removed change: '#{removed_change}', added " \
|
496
|
+
"change: '#{added_change}'"
|
396
497
|
end
|
397
498
|
String
|
398
499
|
else
|
399
500
|
raise MergeError, "Unsupported types of all of the values. Existing value: " \
|
400
|
-
|
501
|
+
"'#{old_value}', removed change: '#{removed_change}', added change: " \
|
502
|
+
"'#{added_change}'"
|
401
503
|
end
|
402
504
|
end
|
403
505
|
|
404
|
-
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)
|
405
507
|
if type == Hash
|
406
|
-
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)
|
407
509
|
elsif type == Array
|
408
|
-
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)
|
409
511
|
elsif type == String
|
410
|
-
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)
|
411
513
|
else
|
412
514
|
raise MergeError, "Unsupported types of all of the values. Existing value: " \
|
413
|
-
|
515
|
+
"'#{old_value}', removed change: '#{removed_change}', added change: " \
|
516
|
+
"'#{added_change}'"
|
414
517
|
end
|
415
518
|
end
|
416
519
|
|
417
|
-
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)
|
418
521
|
return added_change if ((old_value || {}).to_a - (removed_change || {}).to_a).empty?
|
419
522
|
|
420
523
|
# First apply the added change to see if there are any conflicts with it.
|
421
524
|
new_value = (old_value || {}).merge(added_change || {})
|
525
|
+
conflicting_added_hash_values = (old_value.to_a - new_value.to_a)
|
422
526
|
|
423
|
-
unless
|
424
|
-
|
425
|
-
|
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)
|
426
532
|
end
|
427
533
|
|
428
534
|
if removed_change.nil?
|
429
535
|
return new_value
|
430
536
|
end
|
431
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
|
+
|
432
551
|
new_value
|
433
552
|
.reject do |key, value|
|
434
|
-
if
|
435
|
-
|
436
|
-
"'#{key}' but it changed to #{value}. This is considered a conflict that should be " \
|
437
|
-
"resolved manually."
|
553
|
+
if conflicting_removal_hash_values.key?(key)
|
554
|
+
next should_remove_conflicting_values
|
438
555
|
end
|
439
556
|
|
440
557
|
removed_change.key?(key)
|
441
558
|
end
|
442
559
|
end
|
443
560
|
|
444
|
-
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)
|
445
562
|
if old_value.is_a?(String)
|
446
563
|
old_value = [old_value]
|
447
564
|
end
|
@@ -464,10 +581,12 @@ module Kintsugi
|
|
464
581
|
new_value + filtered_added_change
|
465
582
|
end
|
466
583
|
|
467
|
-
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)
|
468
585
|
if old_value != removed_change && !old_value.nil? && added_change != old_value
|
469
|
-
|
470
|
-
|
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
|
471
590
|
end
|
472
591
|
|
473
592
|
added_change
|
@@ -476,26 +595,25 @@ module Kintsugi
|
|
476
595
|
def remove_component(component, change)
|
477
596
|
return if component.nil?
|
478
597
|
|
479
|
-
if component.to_tree_hash != change
|
480
|
-
|
481
|
-
|
482
|
-
"'#{component.display_name}'. Existing component: #{component.to_tree_hash}. " \
|
483
|
-
"Change: #{change}"
|
598
|
+
if component.to_tree_hash != change &&
|
599
|
+
!ConflictResolver.remove_component_when_unexpected_hash?(component, change)
|
600
|
+
return
|
484
601
|
end
|
485
602
|
|
486
|
-
if change["isa"] == "PBXFileReference"
|
487
|
-
|
603
|
+
if change["isa"] == "PBXFileReference" || change["isa"] == "PBXReferenceProxy" ||
|
604
|
+
change["isa"] == "PBXGroup" || change["isa"] == "PBXVariantGroup"
|
605
|
+
remove_build_files_of_file_reference(component)
|
488
606
|
end
|
489
607
|
|
490
608
|
component.remove_from_project
|
491
609
|
end
|
492
610
|
|
493
|
-
def remove_build_files_of_file_reference(file_reference
|
611
|
+
def remove_build_files_of_file_reference(file_reference)
|
494
612
|
# Since the build file's display name depends on the file reference, removing the file
|
495
613
|
# reference before removing it will change the build file's display name which will not be
|
496
614
|
# detected when trying to remove the build file. Therefore, the build files that depend on
|
497
615
|
# the file reference are removed prior to removing the file reference.
|
498
|
-
file_reference.
|
616
|
+
file_reference.referrers.grep(Xcodeproj::Project::PBXBuildFile).each do |build_file|
|
499
617
|
build_file.referrers.each do |referrer|
|
500
618
|
referrer.remove_build_file(build_file)
|
501
619
|
end
|
@@ -553,7 +671,7 @@ module Kintsugi
|
|
553
671
|
add_remote_swift_package_reference(component, change, change_path)
|
554
672
|
else
|
555
673
|
raise MergeError, "Trying to add unsupported component type #{change["isa"]}. Full " \
|
556
|
-
|
674
|
+
"component change is: #{change}"
|
557
675
|
end
|
558
676
|
end
|
559
677
|
|
@@ -569,7 +687,7 @@ module Kintsugi
|
|
569
687
|
containing_component.package_references << remote_swift_package_reference
|
570
688
|
else
|
571
689
|
raise MergeError, "Trying to add remote swift package reference to an unsupported " \
|
572
|
-
|
690
|
+
"component type #{containing_component.isa}. Change is: #{change}"
|
573
691
|
end
|
574
692
|
end
|
575
693
|
|
@@ -585,7 +703,7 @@ module Kintsugi
|
|
585
703
|
containing_component.package_product_dependencies << swift_package_product_dependency
|
586
704
|
else
|
587
705
|
raise MergeError, "Trying to add swift package product dependency to an unsupported " \
|
588
|
-
|
706
|
+
"component type #{containing_component.isa}. Change is: #{change}"
|
589
707
|
end
|
590
708
|
end
|
591
709
|
|
@@ -602,10 +720,10 @@ module Kintsugi
|
|
602
720
|
end.nil?
|
603
721
|
end
|
604
722
|
file_reference =
|
605
|
-
find_reference_proxy(containing_component.project, change
|
723
|
+
find_reference_proxy(containing_component.project, change,
|
606
724
|
reference_filter: filter_references_without_build_files)
|
607
725
|
if file_reference.nil?
|
608
|
-
file_reference = find_reference_proxy(containing_component.project, change
|
726
|
+
file_reference = find_reference_proxy(containing_component.project, change)
|
609
727
|
end
|
610
728
|
containing_component.file_ref = file_reference
|
611
729
|
when Xcodeproj::Project::PBXGroup
|
@@ -618,7 +736,7 @@ module Kintsugi
|
|
618
736
|
add_attributes_to_component(reference_proxy, change, change_path)
|
619
737
|
else
|
620
738
|
raise MergeError, "Trying to add reference proxy to an unsupported component type " \
|
621
|
-
|
739
|
+
"#{containing_component.isa}. Change is: #{change}"
|
622
740
|
end
|
623
741
|
end
|
624
742
|
|
@@ -627,15 +745,11 @@ module Kintsugi
|
|
627
745
|
when Xcodeproj::Project::PBXBuildFile
|
628
746
|
containing_component.file_ref =
|
629
747
|
find_variant_group(containing_component.project, change["displayName"])
|
630
|
-
when Xcodeproj::Project::PBXGroup
|
631
|
-
|
632
|
-
change).nil?
|
633
|
-
raise "Group should have been added already, so this is most likely a bug in Kintsugi" \
|
634
|
-
"Change is: #{change}. Change path: #{change_path}"
|
635
|
-
end
|
748
|
+
when Xcodeproj::Project::PBXGroup
|
749
|
+
# Adding variant groups to groups is handled by another part of the code.
|
636
750
|
else
|
637
751
|
raise MergeError, "Trying to add variant group to an unsupported component type " \
|
638
|
-
|
752
|
+
"#{containing_component.isa}. Change is: #{change}"
|
639
753
|
end
|
640
754
|
end
|
641
755
|
|
@@ -687,7 +801,7 @@ module Kintsugi
|
|
687
801
|
def add_build_file(build_phase, change, change_path)
|
688
802
|
if change["fileRef"].nil?
|
689
803
|
puts "Warning: Trying to add a build file without any file reference to build phase " \
|
690
|
-
|
804
|
+
"'#{build_phase}'"
|
691
805
|
return
|
692
806
|
end
|
693
807
|
|
@@ -736,7 +850,7 @@ module Kintsugi
|
|
736
850
|
component.remote_ref = container_proxy
|
737
851
|
else
|
738
852
|
raise MergeError, "Trying to add container item proxy to an unsupported component type " \
|
739
|
-
|
853
|
+
"#{containing_component.isa}. Change is: #{change}"
|
740
854
|
end
|
741
855
|
add_attributes_to_component(container_proxy, change, change_path,
|
742
856
|
ignore_keys: ["containerPortal"])
|
@@ -760,10 +874,10 @@ module Kintsugi
|
|
760
874
|
|
761
875
|
if container_item_proxies.length > 1
|
762
876
|
puts "Debug: Found more than one potential dependency with name " \
|
763
|
-
|
877
|
+
"'#{container_item_proxy_change["remoteInfo"]}'. Using the first one."
|
764
878
|
elsif container_item_proxies.empty?
|
765
879
|
puts "Warning: No container portal was found for dependency with name " \
|
766
|
-
|
880
|
+
"'#{container_item_proxy_change["remoteInfo"]}'."
|
767
881
|
return
|
768
882
|
end
|
769
883
|
|
@@ -788,8 +902,8 @@ module Kintsugi
|
|
788
902
|
|
789
903
|
unless subproject_reference
|
790
904
|
raise MergeError, "No file reference was found for project reference with change " \
|
791
|
-
|
792
|
-
|
905
|
+
"#{project_reference_change}. This might mean that the file used to " \
|
906
|
+
"exist in the project the but was removed at some point"
|
793
907
|
end
|
794
908
|
|
795
909
|
attribute =
|
@@ -829,8 +943,6 @@ module Kintsugi
|
|
829
943
|
def add_file_reference(containing_component, change, change_path)
|
830
944
|
# base configuration reference and product reference always reference a file that exists
|
831
945
|
# inside a group, therefore in these cases the file is searched for.
|
832
|
-
# In the case of group and variant group, the file can't exist in another group, therefore a
|
833
|
-
# new file reference is always created.
|
834
946
|
case containing_component
|
835
947
|
when Xcodeproj::Project::XCBuildConfiguration
|
836
948
|
containing_component.base_configuration_reference =
|
@@ -840,15 +952,11 @@ module Kintsugi
|
|
840
952
|
find_file(containing_component.project, change["path"])
|
841
953
|
when Xcodeproj::Project::PBXBuildFile
|
842
954
|
containing_component.file_ref = find_file(containing_component.project, change["path"])
|
843
|
-
when Xcodeproj::Project::PBXGroup
|
844
|
-
|
845
|
-
change["path"]).nil?
|
846
|
-
raise "File should have been added already, so this is most likely a bug in Kintsugi" \
|
847
|
-
"Change is: #{change}. Change path: #{change_path}"
|
848
|
-
end
|
955
|
+
when Xcodeproj::Project::PBXGroup
|
956
|
+
# Adding files to groups is handled by another part of the code.
|
849
957
|
else
|
850
958
|
raise MergeError, "Trying to add file reference to an unsupported component type " \
|
851
|
-
|
959
|
+
"#{containing_component.isa}. Change is: #{change}"
|
852
960
|
end
|
853
961
|
end
|
854
962
|
|
@@ -859,11 +967,6 @@ module Kintsugi
|
|
859
967
|
.find { |file| file.path == filepath }
|
860
968
|
end
|
861
969
|
|
862
|
-
def adding_files_and_groups_allowed?(change_path)
|
863
|
-
change_path.start_with?("rootObject/mainGroup") ||
|
864
|
-
change_path.start_with?("rootObject/projectReferences")
|
865
|
-
end
|
866
|
-
|
867
970
|
def add_group(containing_component, change, change_path)
|
868
971
|
case containing_component
|
869
972
|
when Xcodeproj::Project::ObjectDictionary
|
@@ -872,13 +975,11 @@ module Kintsugi
|
|
872
975
|
containing_component[:product_group] = new_group
|
873
976
|
add_attributes_to_component(new_group, change, change_path)
|
874
977
|
when Xcodeproj::Project::PBXGroup
|
875
|
-
|
876
|
-
raise "Group should have been added already, so this is most likely a bug in Kintsugi" \
|
877
|
-
"Change is: #{change}. Change path: #{change_path}"
|
878
|
-
end
|
978
|
+
# Adding groups to groups is handled by another part of the code.
|
879
979
|
else
|
880
980
|
raise MergeError, "Trying to add group to an unsupported component type " \
|
881
|
-
|
981
|
+
"#{containing_component.isa}. Change is: #{change}. Change path: " \
|
982
|
+
"#{change_path}"
|
882
983
|
end
|
883
984
|
end
|
884
985
|
|
@@ -905,7 +1006,7 @@ module Kintsugi
|
|
905
1006
|
end
|
906
1007
|
else
|
907
1008
|
raise MergeError, "Trying to add attribute of unsupported type '#{change_value.class}' " \
|
908
|
-
|
1009
|
+
"to object #{component}. Attribute name is '#{change_name}'"
|
909
1010
|
end
|
910
1011
|
end
|
911
1012
|
end
|
@@ -923,44 +1024,37 @@ module Kintsugi
|
|
923
1024
|
if file_references.length > 1
|
924
1025
|
puts "Debug: Found more than one matching file with path '#{path}'. Using the first one."
|
925
1026
|
elsif file_references.empty?
|
926
|
-
puts "Debug: No file reference found for file with path '#{path}'."
|
927
1027
|
return
|
928
1028
|
end
|
929
1029
|
|
930
1030
|
file_references.first
|
931
1031
|
end
|
932
1032
|
|
933
|
-
def find_reference_proxy(project,
|
1033
|
+
def find_reference_proxy(project, change, reference_filter: ->(_) { true })
|
934
1034
|
reference_proxies = project.root_object.project_references.map do |project_ref_and_products|
|
935
|
-
|
936
|
-
|
937
|
-
|
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
|
938
1039
|
end.compact
|
939
1040
|
|
940
1041
|
if reference_proxies.length > 1
|
941
1042
|
puts "Debug: Found more than one matching reference proxy with name " \
|
942
|
-
|
1043
|
+
"'#{change["remoteInfo"]}'. Using the first one."
|
943
1044
|
elsif reference_proxies.empty?
|
944
|
-
puts "Warning: No reference proxy was found for name "
|
945
|
-
"'#{container_item_proxy_change["remoteInfo"]}'."
|
1045
|
+
puts "Warning: No reference proxy was found for name '#{change["remoteInfo"]}'."
|
946
1046
|
return
|
947
1047
|
end
|
948
1048
|
|
949
1049
|
reference_proxies.first
|
950
1050
|
end
|
951
1051
|
|
952
|
-
def find_reference_proxy_in_component(component, container_item_proxy_change,
|
953
|
-
reference_filter: ->(_) { true })
|
954
|
-
component.find do |product|
|
955
|
-
product.remote_ref.remote_global_id_string ==
|
956
|
-
container_item_proxy_change["remoteGlobalIDString"] &&
|
957
|
-
product.remote_ref.remote_info == container_item_proxy_change["remoteInfo"] &&
|
958
|
-
reference_filter.call(product)
|
959
|
-
end
|
960
|
-
end
|
961
|
-
|
962
1052
|
def join_path(left, right)
|
963
1053
|
left.empty? ? right : "#{left}/#{right}"
|
964
1054
|
end
|
1055
|
+
|
1056
|
+
def parent_group_path(group_path)
|
1057
|
+
group_path[/(.*)\//, 1] || ""
|
1058
|
+
end
|
965
1059
|
end
|
966
1060
|
end
|