kintsugi 0.6.3 → 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,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 = path.empty? ? project.main_group : project[path]
113
+ containing_group = project.group_or_file_at_path(path)
105
114
 
106
115
  if containing_group.nil?
107
- raise MergeError, "Trying to add or move a group with change #{change} to a group that " \
108
- "no longer exists with path '#{path}'. This is considered a conflict that should be " \
109
- "resolved manually."
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 = path.empty? ? project.main_group : project[path]
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
- raise MergeError, "Trying to add or move a file with change #{change} to a group that " \
155
- "no longer exists with path '#{path}'. This is considered a conflict that should be " \
156
- "resolved manually."
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[path]
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
- next if component.nil?
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, change, change_name)
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
- raise MergeError, "Trying to apply changes to a component that doesn't exist at path " \
269
- "#{change_path}. It was probably removed in a previous commit. This is considered a " \
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, change, change_name)
435
+ def child_component(component, change_name)
345
436
  if component.is_a?(Xcodeproj::Project::ObjectList)
346
- child_component_of_object_list(component, change, change_name)
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, change, change_name)
354
- if change["isa"] == "PBXReferenceProxy"
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
- "value: '#{old_value}', removed change: '#{removed_change}', added change: " \
393
- "'#{added_change}'"
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
- "value: '#{old_value}', removed change: '#{removed_change}', added change: " \
400
- "'#{added_change}'"
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
- "value: '#{old_value}', removed change: '#{removed_change}', added change: " \
407
- "'#{added_change}'"
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
- "'#{old_value}', removed change: '#{removed_change}', added change: '#{added_change}'"
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
- "'#{old_value}', removed change: '#{removed_change}', added change: '#{added_change}'"
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 (old_value.to_a - new_value.to_a).empty?
436
- raise MergeError, "New hash #{change} contains values that conflict with old hash " \
437
- "#{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)
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 value != removed_change[key] && value != (added_change || {})[key]
447
- raise MergeError, "Trying to remove value '#{removed_change[key]}' of hash with key " \
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
- raise MergeError, "Trying to remove value '#{removed_change || "nil"}', but the existing " \
482
- "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
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
- raise MergeError, "Trying to remove an object that changed since then. This is " \
493
- "considered a conflict that should be resolved manually. Name of the object is: " \
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
- 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)
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, change)
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.build_files.each do |build_file|
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
- "component change is: #{change}"
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
- "component type #{containing_component.isa}. Change is: #{change}"
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
- "component type #{containing_component.isa}. Change is: #{change}"
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["remoteRef"],
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["remoteRef"])
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
- "#{containing_component.isa}. Change is: #{change}"
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, Xcodeproj::Project::PBXVariantGroup
643
- if find_group_in_group(containing_component, Xcodeproj::Project::PBXVariantGroup,
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
- "#{containing_component.isa}. Change is: #{change}"
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
- "'#{build_phase}'"
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
- "#{containing_component.isa}. Change is: #{change}"
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
- "'#{container_item_proxy_change["remoteInfo"]}'. Using the first one."
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
- "'#{container_item_proxy_change["remoteInfo"]}'."
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
- "#{project_reference_change}. This might mean that the file used to exist in the " \
804
- "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"
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, Xcodeproj::Project::PBXVariantGroup
856
- if find_file_in_group(containing_component, Xcodeproj::Project::PBXFileReference,
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
- "#{containing_component.isa}. Change is: #{change}"
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
- if find_group_in_group(containing_component, Xcodeproj::Project::PBXGroup, change).nil?
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
- "#{containing_component.isa}. Change is: #{change}. Change path: #{change_path}"
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
- "to object #{component}. Attribute name is '#{change_name}'"
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, container_item_proxy_change, reference_filter: ->(_) { true })
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
- find_reference_proxy_in_component(project_ref_and_products[:product_group].children,
948
- container_item_proxy_change,
949
- 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
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
- "'#{container_item_proxy_change["remoteInfo"]}'. Using the first one."
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