kintsugi 0.6.3 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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