kintsugi 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd0bf9dde374b1b29b0e386ff0f4f051f6686e04b140199c8b62d6a73c053285
4
- data.tar.gz: 8845644c3e18fb6244f402d2b432966a0f6e437ced4f441cdd1d43a06369ef51
3
+ metadata.gz: c3024a2418328a3a3fb120f6b2bb7250477079f922dbd1882785ed065b3ff8a8
4
+ data.tar.gz: 98e1e7752eee2e47555b8eb0d21189b06869f284426279ff5aed403c7daa1e7f
5
5
  SHA512:
6
- metadata.gz: 0fd505102998df8f7b0c16d7a0fe7b437e5b6402db784383f752f7b0272fe5b7f2ab2c7b9c18e3de2ee3c071b5cf21577bb4089c792a6ca15286efa3f1f9ae7d
7
- data.tar.gz: 85d252a8135e10b536b042156dc787e9a8d9ac5286736d01113ea4380a49d049f8837430d15490f15c3d14cbdf0f7a3cf75ac5dc4f2047bef55cc7f6fb00f837
6
+ metadata.gz: c3a847fa528aaddb9c537c6f6a7aa91434ddf4d817f8434173d162617a854a3f0f9c012c7c2699c20e6901617f6c2eb91eb429c72a2181163e72a25898dc7ce8
7
+ data.tar.gz: ef5f8ae4c8da4c79aac9510892b714c0801b58829af83a84146add68404be0106c09cd71f219d6a6b1e832d2c05e596a5bd44428388ef91d35e33ff897ef4fa7
@@ -281,7 +281,8 @@ module Kintsugi
281
281
  end
282
282
 
283
283
  if change["isa"]
284
- component = replace_component_with_new_type(parent_component, attribute_name, change)
284
+ component = replace_component_with_new_type(parent_component, attribute_name, change,
285
+ change_path)
285
286
  change = change_for_component_of_new_type(component, change)
286
287
  else
287
288
  component = child_component(parent_component, change_name)
@@ -340,8 +341,9 @@ module Kintsugi
340
341
  else
341
342
  parent_component
342
343
  end
344
+ parent_change_path = change_path.split("/")[0...-1].join("/")
343
345
  add_child_to_component(non_object_list_parent, source_project_component.to_tree_hash,
344
- change_path)
346
+ parent_change_path)
345
347
  component_at_path(non_object_list_parent.project, change_path)
346
348
  end
347
349
 
@@ -381,9 +383,10 @@ module Kintsugi
381
383
  end
382
384
  end
383
385
 
384
- def replace_component_with_new_type(parent_component, name_in_parent_component, change)
386
+ def replace_component_with_new_type(parent_component, name_in_parent_component, change,
387
+ change_path)
385
388
  old_component = parent_component.send(name_in_parent_component)
386
- new_component = component_of_new_type(parent_component, change, old_component)
389
+ new_component = component_of_new_type(parent_component, change, old_component, change_path)
387
390
 
388
391
  copy_attributes_to_new_component(old_component, new_component)
389
392
 
@@ -391,18 +394,32 @@ module Kintsugi
391
394
  new_component
392
395
  end
393
396
 
394
- def component_of_new_type(parent_component, change, old_component)
397
+ def component_of_new_type(parent_component, change, old_component, change_path)
395
398
  if change["isa"][:added] == "PBXFileReference"
396
- path = (change["path"] && change["path"][:added]) || old_component.path
399
+ source_project_component =
400
+ component_at_path(@change_source_project, change_path.split("/")[0...-1].join("/"))
401
+ if source_project_component.nil?
402
+ raise MergeError, "Couldn't find file reference in the project where the file should " \
403
+ "reside. The file's change is #{change}. Change path is #{change_path}"
404
+ end
405
+
397
406
  case parent_component
398
407
  when Xcodeproj::Project::XCBuildConfiguration
399
- parent_component.base_configuration_reference = find_file(parent_component.project, path)
408
+ parent_component.base_configuration_reference =
409
+ parent_component.project.group_or_file_at_path(
410
+ source_project_component.base_configuration_reference.hierarchy_path
411
+ .delete_prefix("/")
412
+ )
400
413
  return parent_component.base_configuration_reference
401
414
  when Xcodeproj::Project::PBXNativeTarget
402
- parent_component.product_reference = find_file(parent_component.project, path)
415
+ parent_component.product_reference = parent_component.project.group_or_file_at_path(
416
+ source_project_component.product_reference.hierarchy_path.delete_prefix("/")
417
+ )
403
418
  return parent_component.product_reference
404
419
  when Xcodeproj::Project::PBXBuildFile
405
- parent_component.file_ref = find_file(parent_component.project, path)
420
+ parent_component.file_ref = parent_component.project.group_or_file_at_path(
421
+ source_project_component.file_ref.hierarchy_path.delete_prefix("/")
422
+ )
406
423
  return parent_component.file_ref
407
424
  end
408
425
  end
@@ -891,14 +908,19 @@ module Kintsugi
891
908
  end
892
909
  return if !Settings.allow_duplicates && !existing_subproject.nil?
893
910
 
894
- filter_subproject_without_project_references = lambda do |file_reference|
911
+ source_project_subproject_reference = component_at_path(@change_source_project, change_path)
912
+ if source_project_subproject_reference.nil?
913
+ raise MergeError, "Project reference with change #{project_reference_change} doesn't " \
914
+ "exist in the source project. Change path is #{change_path}"
915
+ end
916
+
917
+ subproject_reference = root_object.project.files.find do |file_reference|
918
+ file_reference.hierarchy_path ==
919
+ source_project_subproject_reference.project_ref.hierarchy_path &&
895
920
  root_object.project_references.find do |project_reference|
896
921
  project_reference.project_ref.uuid == file_reference.uuid
897
922
  end.nil?
898
923
  end
899
- subproject_reference =
900
- find_file(root_object.project, project_reference_change["ProjectRef"]["path"],
901
- file_filter: filter_subproject_without_project_references)
902
924
 
903
925
  unless subproject_reference
904
926
  raise MergeError, "No file reference was found for project reference with change " \
@@ -941,17 +963,29 @@ module Kintsugi
941
963
  end
942
964
 
943
965
  def add_file_reference(containing_component, change, change_path)
944
- # base configuration reference and product reference always reference a file that exists
945
- # inside a group, therefore in these cases the file is searched for.
966
+ source_project_component =
967
+ component_at_path(@change_source_project, change_path.split("/")[0...-1].join("/"))
968
+ if source_project_component.nil?
969
+ raise MergeError, "Couldn't find file reference in the project where the file should " \
970
+ "reside. The file's change is #{change}. Change path is #{change_path}"
971
+ end
972
+
946
973
  case containing_component
947
974
  when Xcodeproj::Project::XCBuildConfiguration
948
975
  containing_component.base_configuration_reference =
949
- find_file(containing_component.project, change["path"])
976
+ containing_component.project.group_or_file_at_path(
977
+ source_project_component.base_configuration_reference.hierarchy_path.delete_prefix("/")
978
+ )
950
979
  when Xcodeproj::Project::PBXNativeTarget
951
980
  containing_component.product_reference =
952
- find_file(containing_component.project, change["path"])
981
+ containing_component.project.group_or_file_at_path(
982
+ source_project_component.product_reference.hierarchy_path.delete_prefix("/")
983
+ )
953
984
  when Xcodeproj::Project::PBXBuildFile
954
- containing_component.file_ref = find_file(containing_component.project, change["path"])
985
+ containing_component.file_ref =
986
+ containing_component.project.group_or_file_at_path(
987
+ source_project_component.file_ref.hierarchy_path.delete_prefix("/")
988
+ )
955
989
  when Xcodeproj::Project::PBXGroup
956
990
  # Adding files to groups is handled by another part of the code.
957
991
  else
@@ -1002,7 +1036,8 @@ module Kintsugi
1002
1036
  add_child_to_component(component, change_value, change_path)
1003
1037
  when Array
1004
1038
  change_value.each do |added_attribute_element|
1005
- add_child_to_component(component, added_attribute_element, change_path)
1039
+ add_child_to_component(component, added_attribute_element,
1040
+ "#{change_path}/#{change_name}")
1006
1041
  end
1007
1042
  else
1008
1043
  raise MergeError, "Trying to add attribute of unsupported type '#{change_value.class}' " \
@@ -1017,19 +1052,6 @@ module Kintsugi
1017
1052
  end.default_value
1018
1053
  end
1019
1054
 
1020
- def find_file(project, path, file_filter: ->(_) { true })
1021
- file_references = project.files.select do |file_reference|
1022
- file_reference.path == path && file_filter.call(file_reference)
1023
- end
1024
- if file_references.length > 1
1025
- puts "Debug: Found more than one matching file with path '#{path}'. Using the first one."
1026
- elsif file_references.empty?
1027
- return
1028
- end
1029
-
1030
- file_references.first
1031
- end
1032
-
1033
1055
  def find_reference_proxy(project, change, reference_filter: ->(_) { true })
1034
1056
  reference_proxies = project.root_object.project_references.map do |project_ref_and_products|
1035
1057
  project_ref_and_products[:product_group].children.find do |reference_proxy|
data/lib/kintsugi/cli.rb CHANGED
@@ -31,21 +31,12 @@ module Kintsugi
31
31
  Command = Struct.new(:option_parser, :action, :description, keyword_init: true)
32
32
 
33
33
  def create_driver_subcommand
34
- option_parser =
35
- OptionParser.new do |opts|
36
- opts.banner = "Usage: kintsugi driver BASE OURS THEIRS ORIGINAL_FILE_PATH [options]\n" \
37
- "Uses Kintsugi as a Git merge driver. Parameters " \
38
- "should be the path to base version of the file, path to ours version, path to " \
39
- "theirs version, and the original file path."
40
-
41
- opts.on("--interactive-resolution=FLAG", TrueClass, "In case a conflict that requires " \
42
- "human decision to resolve, show an interactive prompt with choices to resolve it")
43
-
44
- opts.on("-h", "--help", "Prints this help") do
45
- puts opts
46
- exit
47
- end
48
- end
34
+ option_parser = create_base_option_parser
35
+ option_parser.banner = "Usage: kintsugi driver BASE OURS THEIRS ORIGINAL_FILE_PATH " \
36
+ "[options]\n" \
37
+ "Uses Kintsugi as a Git merge driver. Parameters " \
38
+ "should be the path to base version of the file, path to ours version, path to " \
39
+ "theirs version, and the original file path."
49
40
 
50
41
  driver_action = lambda { |options, arguments|
51
42
  if arguments.count != 4
@@ -54,9 +45,7 @@ module Kintsugi
54
45
  exit(1)
55
46
  end
56
47
 
57
- unless options[:"interactive-resolution"].nil?
58
- Settings.interactive_resolution = options[:"interactive-resolution"]
59
- end
48
+ update_settings(options)
60
49
 
61
50
  Kintsugi.three_way_merge(arguments[0], arguments[1], arguments[2], arguments[3])
62
51
  warn "\e[32mKintsugi auto-merged #{arguments[3]}\e[0m"
@@ -70,17 +59,10 @@ module Kintsugi
70
59
  end
71
60
 
72
61
  def create_install_driver_subcommand
73
- option_parser =
74
- OptionParser.new do |opts|
75
- opts.banner = "Usage: kintsugi install-driver [driver-options]\n" \
76
- "Installs Kintsugi as a Git merge driver globally. `driver-options` will be passed " \
77
- "to `kintsugi driver`."
78
-
79
- opts.on("-h", "--help", "Prints this help") do
80
- puts opts
81
- exit
82
- end
83
- end
62
+ option_parser = create_base_option_parser
63
+ option_parser.banner = "Usage: kintsugi install-driver [driver-options]\n" \
64
+ "Installs Kintsugi as a Git merge driver globally. `driver-options` will be passed " \
65
+ "to `kintsugi driver`."
84
66
 
85
67
  action = lambda { |options, arguments|
86
68
  if arguments.count != 0
@@ -94,7 +76,9 @@ module Kintsugi
94
76
  exit(1)
95
77
  end
96
78
 
97
- install_kintsugi_driver_globally(options)
79
+ driver_options = extract_driver_options(options, option_parser)
80
+
81
+ install_kintsugi_driver_globally(driver_options)
98
82
  puts "Done! 🪄"
99
83
  }
100
84
 
@@ -105,9 +89,30 @@ module Kintsugi
105
89
  )
106
90
  end
107
91
 
92
+ def extract_driver_options(options, option_parser)
93
+ options.map do |option_name, option_value|
94
+ switch = option_parser.top.search(:long, option_name.to_s)
95
+ prefix = "--"
96
+ if switch.nil?
97
+ switch = option_parser.top.search(:short, option_name.to_s)
98
+ prefix = "-"
99
+ end
100
+ case switch
101
+ when OptionParser::Switch::NoArgument
102
+ [prefix + option_name.to_s]
103
+ when NilClass
104
+ puts "Invalid flag #{option_name} passed to 'install-driver' subcommand\n\n"
105
+ puts option_parser
106
+ exit(1)
107
+ else
108
+ [prefix + option_name.to_s, option_value]
109
+ end
110
+ end.flatten
111
+ end
112
+
108
113
  def install_kintsugi_driver_globally(options)
109
114
  `git config --global merge.kintsugi.name "Kintsugi driver"`
110
- kintsugi_command = "kintsugi driver %O %A %B %P #{options}"
115
+ kintsugi_command = "kintsugi driver %O %A %B %P #{options.join(" ")}".rstrip
111
116
  `git config --global merge.kintsugi.driver "#{kintsugi_command}"`
112
117
 
113
118
  attributes_file_path = global_attributes_file_path
@@ -173,33 +178,21 @@ module Kintsugi
173
178
  end
174
179
 
175
180
  def create_root_command
176
- option_parser = OptionParser.new do |opts|
177
- opts.banner = "Kintsugi, version #{Version::STRING}\n" \
178
- "Copyright (c) 2021 Lightricks\n\n" \
179
- "Usage: kintsugi <pbxproj_filepath> [options]\n" \
180
- " kintsugi <subcommand> [options]"
181
-
182
- opts.separator ""
183
- opts.on("--changes-output-path=PATH", "Path to which changes applied to the project are " \
184
- "written in JSON format. Used for debug purposes.")
185
-
186
- opts.on("-h", "--help", "Prints this help") do
187
- puts opts
188
- exit
189
- end
190
-
191
- opts.on("-v", "--version", "Prints version") do
192
- puts Version::STRING
193
- exit
194
- end
195
-
196
- opts.on("--allow-duplicates", "Allow to add duplicates of the same entity")
181
+ option_parser = create_base_option_parser
182
+ option_parser.banner = "Kintsugi, version #{Version::STRING}\n" \
183
+ "Copyright (c) 2021 Lightricks\n\n" \
184
+ "Usage: kintsugi <pbxproj_filepath> [options]\n" \
185
+ " kintsugi <subcommand> [options]"
186
+
187
+ option_parser.on("-v", "--version", "Prints version") do
188
+ puts Version::STRING
189
+ exit
190
+ end
197
191
 
198
- opts.on("--interactive-resolution=FLAG", TrueClass, "In case a conflict that requires " \
199
- "human decision to resolve, show an interactive prompt with choices to resolve it")
192
+ option_parser.on("--changes-output-path=PATH", "Path to which changes applied to the " \
193
+ "project are written in JSON format. Used for debug purposes.")
200
194
 
201
- opts.on_tail("\nSUBCOMMANDS\n#{subcommands_descriptions(subcommands)}")
202
- end
195
+ option_parser.on_tail("\nSUBCOMMANDS\n#{subcommands_descriptions(subcommands)}")
203
196
 
204
197
  root_action = lambda { |options, arguments|
205
198
  if arguments.count != 1
@@ -208,13 +201,7 @@ module Kintsugi
208
201
  exit(1)
209
202
  end
210
203
 
211
- if options[:"allow-duplicates"]
212
- Settings.allow_duplicates = true
213
- end
214
-
215
- unless options[:"interactive-resolution"].nil?
216
- Settings.interactive_resolution = options[:"interactive-resolution"]
217
- end
204
+ update_settings(options)
218
205
 
219
206
  project_file_path = File.expand_path(arguments[0])
220
207
  Kintsugi.resolve_conflicts(project_file_path, options[:"changes-output-path"])
@@ -228,6 +215,32 @@ module Kintsugi
228
215
  )
229
216
  end
230
217
 
218
+ def create_base_option_parser
219
+ OptionParser.new do |opts|
220
+ opts.separator ""
221
+
222
+ opts.on("-h", "--help", "Prints this help") do
223
+ puts opts
224
+ exit
225
+ end
226
+
227
+ opts.on("--allow-duplicates", "Allow to add duplicates of the same entity")
228
+
229
+ opts.on("--interactive-resolution=FLAG", TrueClass, "In case a conflict that requires " \
230
+ "human decision to resolve, show an interactive prompt with choices to resolve it")
231
+ end
232
+ end
233
+
234
+ def update_settings(options)
235
+ if options[:"allow-duplicates"]
236
+ Settings.allow_duplicates = true
237
+ end
238
+
239
+ unless options[:"interactive-resolution"].nil?
240
+ Settings.interactive_resolution = options[:"interactive-resolution"]
241
+ end
242
+ end
243
+
231
244
  def subcommands_descriptions(subcommands)
232
245
  longest_subcommand_length = subcommands.keys.map(&:length).max + 4
233
246
  format_string = " %-#{longest_subcommand_length}s%s"
@@ -3,6 +3,6 @@
3
3
  module Kintsugi
4
4
  # This module holds the Kintsugi version information.
5
5
  module Version
6
- STRING = "0.7.0"
6
+ STRING = "0.7.2"
7
7
  end
8
8
  end
@@ -113,6 +113,18 @@ module Xcodeproj
113
113
  string.scan(/ *((['"]?).*?[^\\]\2)(?=( |\z))/).map(&:first)
114
114
  end
115
115
  end
116
+
117
+ # Modifies `PBXTargetDependency`'s `to_tree_hash` to not crash if `target_proxy` is `nil`.
118
+ # The same fix was done in https://github.com/CocoaPods/Xcodeproj/pull/915/.
119
+ class PBXTargetDependency
120
+ def to_tree_hash
121
+ hash = {}
122
+ hash['displayName'] = display_name
123
+ hash['isa'] = isa
124
+ hash['targetProxy'] = target_proxy.to_tree_hash if target_proxy
125
+ hash
126
+ end
127
+ end
116
128
  end
117
129
  end
118
130
 
@@ -585,6 +585,26 @@ describe Kintsugi, :apply_change_to_project do
585
585
  expect(base_project).to be_equivalent_to_project(theirs_project)
586
586
  end
587
587
 
588
+ it "keeps different files with the same name in their respective targets" do
589
+ file_reference = base_project.main_group.new_reference("file1.swift")
590
+ base_project.targets[0].source_build_phase.add_file_reference(file_reference)
591
+
592
+ base_project.new_target("com.apple.product-type.library.static", "bar", :ios)
593
+ base_project.main_group.find_subpath("new_group", true).new_reference("file1.swift")
594
+ base_project.save
595
+
596
+ theirs_project = create_copy_of_project(base_project.path, "theirs")
597
+ second_target = theirs_project.targets[1]
598
+ second_file_reference = theirs_project.main_group.find_subpath("new_group/file1.swift")
599
+ second_target.source_build_phase.add_file_reference(second_file_reference)
600
+
601
+ changes_to_apply = get_diff(theirs_project, base_project)
602
+
603
+ described_class.apply_change_to_project(base_project, changes_to_apply, theirs_project)
604
+ expect(base_project).to be_equivalent_to_project(theirs_project)
605
+ expect(base_project.main_group.find_subpath("new_group/file1.swift").build_files).not_to be_empty
606
+ end
607
+
588
608
  it "moves file that is referenced by a target from a group to the main group" do
589
609
  file_reference = base_project.main_group.find_subpath("new_group", true).new_reference("bar")
590
610
  base_project.targets[0].source_build_phase.add_file_reference(file_reference)
@@ -1764,7 +1784,7 @@ describe Kintsugi, :apply_change_to_project do
1764
1784
  end
1765
1785
  end
1766
1786
 
1767
- describe "resovling conflicts interactively" do
1787
+ describe "resolving conflicts interactively" do
1768
1788
  let(:test_prompt) { TTY::Prompt::Test.new }
1769
1789
 
1770
1790
  before do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kintsugi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Yohay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-29 00:00:00.000000000 Z
11
+ date: 2023-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: tty-prompt
@@ -184,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
184
  - !ruby/object:Gem::Version
185
185
  version: '0'
186
186
  requirements: []
187
- rubygems_version: 3.4.10
187
+ rubygems_version: 3.4.18
188
188
  signing_key:
189
189
  specification_version: 4
190
190
  summary: pbxproj files git conflicts solver