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.
@@ -4,10 +4,11 @@
4
4
 
5
5
  require "xcodeproj"
6
6
 
7
- require_relative "utils"
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 = path.empty? ? project.main_group : project[path]
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 = path.empty? ? project.main_group : project[path]
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[path]
218
+ component = project.group_or_file_at_path(path)
186
219
 
187
- next if component.nil?
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, change, change_name)
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
- raise MergeError, "Trying to apply changes to a component that doesn't exist at path " \
257
- "#{change_path}. It was probably removed in a previous commit. This is considered a " \
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, change, change_name)
435
+ def child_component(component, change_name)
333
436
  if component.is_a?(Xcodeproj::Project::ObjectList)
334
- child_component_of_object_list(component, change, change_name)
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, change, change_name)
342
- if change["isa"] == "PBXReferenceProxy"
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
- "value: '#{old_value}', removed change: '#{removed_change}', added change: " \
381
- "'#{added_change}'"
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
- "value: '#{old_value}', removed change: '#{removed_change}', added change: " \
388
- "'#{added_change}'"
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
- "value: '#{old_value}', removed change: '#{removed_change}', added change: " \
395
- "'#{added_change}'"
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
- "'#{old_value}', removed change: '#{removed_change}', added change: '#{added_change}'"
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
- "'#{old_value}', removed change: '#{removed_change}', added change: '#{added_change}'"
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 (old_value.to_a - new_value.to_a).empty?
424
- raise MergeError, "New hash #{change} contains values that conflict with old hash " \
425
- "#{old_value}"
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 value != removed_change[key] && value != (added_change || {})[key]
435
- raise MergeError, "Trying to remove value '#{removed_change[key]}' of hash with key " \
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
- raise MergeError, "Trying to remove value '#{removed_change || "nil"}', but the existing " \
470
- "value is '#{old_value}'. This is considered a conflict that should be resolved manually."
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
- raise MergeError, "Trying to remove an object that changed since then. This is " \
481
- "considered a conflict that should be resolved manually. Name of the object is: " \
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
- remove_build_files_of_file_reference(component, change)
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, change)
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.build_files.each do |build_file|
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
- "component change is: #{change}"
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
- "component type #{containing_component.isa}. Change is: #{change}"
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
- "component type #{containing_component.isa}. Change is: #{change}"
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["remoteRef"],
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["remoteRef"])
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
- "#{containing_component.isa}. Change is: #{change}"
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, Xcodeproj::Project::PBXVariantGroup
631
- if find_group_in_group(containing_component, Xcodeproj::Project::PBXVariantGroup,
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
- "#{containing_component.isa}. Change is: #{change}"
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
- "'#{build_phase}'"
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
- "#{containing_component.isa}. Change is: #{change}"
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
- "'#{container_item_proxy_change["remoteInfo"]}'. Using the first one."
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
- "'#{container_item_proxy_change["remoteInfo"]}'."
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
- "#{project_reference_change}. This might mean that the file used to exist in the " \
792
- "project the but was removed at some point"
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, Xcodeproj::Project::PBXVariantGroup
844
- if find_file_in_group(containing_component, Xcodeproj::Project::PBXFileReference,
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
- "#{containing_component.isa}. Change is: #{change}"
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
- if find_group_in_group(containing_component, Xcodeproj::Project::PBXGroup, change).nil?
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
- "#{containing_component.isa}. Change is: #{change}. Change path: #{change_path}"
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
- "to object #{component}. Attribute name is '#{change_name}'"
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, container_item_proxy_change, reference_filter: ->(_) { true })
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
- find_reference_proxy_in_component(project_ref_and_products[:product_group].children,
936
- container_item_proxy_change,
937
- reference_filter: reference_filter)
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
- "'#{container_item_proxy_change["remoteInfo"]}'. Using the first one."
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