kintsugi 0.7.0 → 0.7.2

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