kintsugi 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|