kintsugi 0.1.0 → 0.3.1
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 +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +22 -1
- data/bin/kintsugi +16 -29
- data/kintsugi.gemspec +4 -2
- data/lib/kintsugi/apply_change_to_project.rb +61 -25
- data/lib/kintsugi/cli.rb +105 -0
- data/lib/kintsugi/version.rb +8 -0
- data/lib/kintsugi.rb +70 -23
- data/spec/kintsugi_apply_change_to_project_spec.rb +114 -4
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdc0655ad443f7511adec336aef55e40ed0bb5458cf5fd9ae7747294bf40881d
|
4
|
+
data.tar.gz: 214a8426b955d0c36b2ff51f55b2016ed69861ccc5d99fa2d463567cbc6872fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79d21fb3b3ddcb977a0b09bb5f80d19d6337574cb5b975a342d261bd689b53ff387a4ef2c7f0fb9a33d6698ce0e739b76824515ecf4b869f5858228635d99047
|
7
|
+
data.tar.gz: 4a902033e2f4236d82f3db8b1fa832ec78e8201b264a32a11599b3c0df3b048bb82e5abee8cb3d3fac7e74ae4d6ff37029ae9cd809fee5ebbe65723c997862e7
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -34,10 +34,31 @@ gem 'kintsugi', require: false
|
|
34
34
|
|
35
35
|
## Usage
|
36
36
|
|
37
|
-
When there's a `.pbxproj` file with Git conflicts, run `kintsugi <path_to_pbxproj_file>`.
|
37
|
+
When there's a `.pbxproj` file with Git conflicts, and a 3-way merge is possible, run `kintsugi <path_to_pbxproj_file>`.
|
38
38
|
|
39
39
|
And see the magic happen! :sparkles:
|
40
40
|
|
41
|
+
### Git merge driver
|
42
|
+
|
43
|
+
To use Kintsugi as a Git merge driver, follow these steps:
|
44
|
+
|
45
|
+
- Add it as driver to Git config file by running the following:
|
46
|
+
```sh
|
47
|
+
git config merge.kintsugi.name "Kintsugi driver" # Or any other name you prefer
|
48
|
+
git config merge.kintsugi.driver "kintsugi driver %O %A %B %P"
|
49
|
+
```
|
50
|
+
|
51
|
+
`kintsugi` should be in your `PATH`.
|
52
|
+
Run `git config` with `--global` to add this to the global config file.
|
53
|
+
|
54
|
+
- Add the following line to the `.gitattributes` file at the root of the repository:
|
55
|
+
|
56
|
+
`*.pbxproj merge=kintsugi`
|
57
|
+
|
58
|
+
This will instruct Git to use Kintsugi as a merge driver for `.pbxproj` files.
|
59
|
+
|
60
|
+
See the [official docs](https://git-scm.com/docs/gitattributes) if you want to set this globally.
|
61
|
+
|
41
62
|
## Contribution
|
42
63
|
|
43
64
|
See our [Contribution guidelines](./CONTRIBUTING.md).
|
data/bin/kintsugi
CHANGED
@@ -4,41 +4,28 @@
|
|
4
4
|
# Copyright (c) 2020 Lightricks. All rights reserved.
|
5
5
|
# Created by Ben Yohay.
|
6
6
|
|
7
|
-
require "json"
|
8
|
-
require "optparse"
|
9
|
-
|
10
7
|
require "kintsugi"
|
8
|
+
require_relative "../lib/kintsugi/cli"
|
11
9
|
|
12
|
-
def parse_options!(argv)
|
13
|
-
options_parser = create_options_parser
|
14
|
-
|
10
|
+
def parse_options!(command, argv)
|
15
11
|
options = {}
|
16
|
-
|
17
|
-
|
18
|
-
if argv.length != 1
|
19
|
-
puts "Incorrect number of arguments\n\n"
|
20
|
-
puts options_parser
|
21
|
-
exit(1)
|
22
|
-
end
|
23
|
-
|
12
|
+
command.option_parser.parse!(argv, into: options)
|
24
13
|
options
|
25
14
|
end
|
26
15
|
|
27
|
-
def
|
28
|
-
|
29
|
-
opts.banner = "Usage: kintsugi [pbxproj_filepath] [options]"
|
30
|
-
opts.on("--changes-output-path=PATH", "Path to which changes applied to the project are " \
|
31
|
-
"written in JSON format. Used for debug purposes.")
|
32
|
-
|
33
|
-
opts.on("-h", "--help", "Prints this help") do
|
34
|
-
puts opts
|
35
|
-
exit
|
36
|
-
end
|
37
|
-
end
|
16
|
+
def name_of_subcommand?(subcommands, argument)
|
17
|
+
subcommands.include?(argument)
|
38
18
|
end
|
39
19
|
|
40
|
-
|
41
|
-
|
42
|
-
|
20
|
+
first_argument = ARGV[0]
|
21
|
+
cli = Kintsugi::CLI.new
|
22
|
+
command =
|
23
|
+
if name_of_subcommand?(cli.subcommands, first_argument)
|
24
|
+
ARGV.shift
|
25
|
+
cli.subcommands[first_argument]
|
26
|
+
else
|
27
|
+
cli.root_command
|
28
|
+
end
|
43
29
|
|
44
|
-
|
30
|
+
options = parse_options!(command, ARGV)
|
31
|
+
command.action.call(options, ARGV, command.option_parser)
|
data/kintsugi.gemspec
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "lib/kintsugi/version"
|
4
|
+
|
3
5
|
Gem::Specification.new do |spec|
|
4
6
|
spec.name = "kintsugi"
|
5
|
-
spec.version =
|
7
|
+
spec.version = Kintsugi::Version::STRING
|
6
8
|
spec.authors = ["Ben Yohay"]
|
7
9
|
spec.email = ["ben@lightricks.com"]
|
8
10
|
spec.required_ruby_version = ">= 2.5.0"
|
@@ -20,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
20
22
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
21
23
|
spec.require_paths = ["lib"]
|
22
24
|
|
23
|
-
spec.add_dependency "xcodeproj", "1.19.0"
|
25
|
+
spec.add_dependency "xcodeproj", ">= 1.19.0", "<= 1.21.0"
|
24
26
|
|
25
27
|
spec.add_development_dependency "rake", "~> 13.0"
|
26
28
|
spec.add_development_dependency "rspec", "~> 3.9"
|
@@ -167,7 +167,7 @@ module Kintsugi
|
|
167
167
|
new_value = nil
|
168
168
|
|
169
169
|
if change.key?(:removed)
|
170
|
-
new_value = apply_removal_to_simple_attribute(old_value, change[:removed])
|
170
|
+
new_value = apply_removal_to_simple_attribute(old_value, change[:removed], change[:added])
|
171
171
|
end
|
172
172
|
|
173
173
|
if change.key?(:added)
|
@@ -183,29 +183,31 @@ module Kintsugi
|
|
183
183
|
new_value
|
184
184
|
end
|
185
185
|
|
186
|
-
def apply_removal_to_simple_attribute(old_value,
|
187
|
-
case
|
186
|
+
def apply_removal_to_simple_attribute(old_value, removed_change, added_change)
|
187
|
+
case removed_change
|
188
188
|
when Array
|
189
|
-
(old_value || []) -
|
189
|
+
(old_value || []) - removed_change
|
190
190
|
when Hash
|
191
191
|
(old_value || {}).reject do |key, value|
|
192
|
-
if value !=
|
193
|
-
raise "Trying to remove value #{
|
194
|
-
"to #{value}."
|
192
|
+
if value != removed_change[key] && added_change[key] != value
|
193
|
+
raise "Trying to remove value '#{removed_change[key]}' of hash with key '#{key}' but " \
|
194
|
+
"it changed to #{value}. This is considered a conflict that should be resolved " \
|
195
|
+
"manually."
|
195
196
|
end
|
196
197
|
|
197
|
-
|
198
|
+
removed_change.key?(key)
|
198
199
|
end
|
199
200
|
when String
|
200
|
-
if old_value !=
|
201
|
-
raise "
|
201
|
+
if old_value != removed_change && !old_value.nil? && added_change != old_value
|
202
|
+
raise "Trying to remove value '#{removed_change}', but the existing value is " \
|
203
|
+
"'#{old_value}'. This is considered a conflict that should be resolved manually."
|
202
204
|
end
|
203
205
|
|
204
206
|
nil
|
205
207
|
when nil
|
206
208
|
nil
|
207
209
|
else
|
208
|
-
raise "Unsupported change #{
|
210
|
+
raise "Unsupported change #{removed_change} of type #{removed_change.class}"
|
209
211
|
end
|
210
212
|
end
|
211
213
|
|
@@ -265,6 +267,8 @@ module Kintsugi
|
|
265
267
|
case change["isa"]
|
266
268
|
when "PBXNativeTarget"
|
267
269
|
add_target(component, change)
|
270
|
+
when "PBXAggregateTarget"
|
271
|
+
add_aggregate_target(component, change)
|
268
272
|
when "PBXFileReference"
|
269
273
|
add_file_reference(component, change)
|
270
274
|
when "PBXGroup"
|
@@ -306,7 +310,22 @@ module Kintsugi
|
|
306
310
|
def add_reference_proxy(containing_component, change)
|
307
311
|
case containing_component
|
308
312
|
when Xcodeproj::Project::PBXBuildFile
|
309
|
-
|
313
|
+
# If there are two file references that refer to the same file, one with a build file and
|
314
|
+
# the other one without, this method will prefer to take the one without the build file.
|
315
|
+
# This assumes that it's preferred to have a file reference with build file than a file
|
316
|
+
# reference without/with two build files.
|
317
|
+
filter_references_without_build_files = lambda do |reference|
|
318
|
+
reference.referrers.find do |referrer|
|
319
|
+
referrer.is_a?(Xcodeproj::Project::PBXBuildFile)
|
320
|
+
end.nil?
|
321
|
+
end
|
322
|
+
file_reference =
|
323
|
+
find_reference_proxy(containing_component.project, change["remoteRef"],
|
324
|
+
reference_filter: filter_references_without_build_files)
|
325
|
+
if file_reference.nil?
|
326
|
+
file_reference = find_reference_proxy(containing_component.project, change["remoteRef"])
|
327
|
+
end
|
328
|
+
containing_component.file_ref = file_reference
|
310
329
|
when Xcodeproj::Project::PBXGroup
|
311
330
|
reference_proxy = containing_component.project.new(Xcodeproj::Project::PBXReferenceProxy)
|
312
331
|
containing_component << reference_proxy
|
@@ -458,7 +477,14 @@ module Kintsugi
|
|
458
477
|
end
|
459
478
|
|
460
479
|
def add_subproject_reference(root_object, project_reference_change)
|
461
|
-
|
480
|
+
filter_subproject_without_project_references = lambda do |file_reference|
|
481
|
+
root_object.project_references.find do |project_reference|
|
482
|
+
project_reference.project_ref.uuid == file_reference.uuid
|
483
|
+
end.nil?
|
484
|
+
end
|
485
|
+
subproject_reference =
|
486
|
+
find_file(root_object.project, project_reference_change["ProjectRef"],
|
487
|
+
file_filter: filter_subproject_without_project_references)
|
462
488
|
|
463
489
|
attribute =
|
464
490
|
Xcodeproj::Project::PBXProject.references_by_keys_attributes
|
@@ -488,6 +514,12 @@ module Kintsugi
|
|
488
514
|
add_attributes_to_component(target, change)
|
489
515
|
end
|
490
516
|
|
517
|
+
def add_aggregate_target(root_object, change)
|
518
|
+
target = root_object.project.new(Xcodeproj::Project::PBXAggregateTarget)
|
519
|
+
root_object.project.targets << target
|
520
|
+
add_attributes_to_component(target, change)
|
521
|
+
end
|
522
|
+
|
491
523
|
def add_file_reference(containing_component, change)
|
492
524
|
# base configuration reference and product reference always reference a file that exists
|
493
525
|
# inside a group, therefore in these cases the file is searched for.
|
@@ -555,25 +587,29 @@ module Kintsugi
|
|
555
587
|
end
|
556
588
|
end
|
557
589
|
|
558
|
-
def find_file(project, file_reference_change)
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
590
|
+
def find_file(project, file_reference_change, file_filter: ->(_) { true })
|
591
|
+
file_references = project.files.select do |file_reference|
|
592
|
+
file_reference.path == file_reference_change["path"] && file_filter.call(file_reference)
|
593
|
+
end
|
594
|
+
if file_references.length > 1
|
595
|
+
puts "Debug: Found more than one matching file with path " \
|
596
|
+
"'#{file_reference_change["path"]}'. Using the first one."
|
597
|
+
elsif file_references.empty?
|
598
|
+
puts "Debug: No file reference found for file with path " \
|
599
|
+
"'#{file_reference_change["path"]}'."
|
600
|
+
return
|
568
601
|
end
|
602
|
+
|
603
|
+
file_references.first
|
569
604
|
end
|
570
605
|
|
571
|
-
def find_reference_proxy(project, container_item_proxy_change)
|
606
|
+
def find_reference_proxy(project, container_item_proxy_change, reference_filter: ->(_) { true })
|
572
607
|
reference_proxies = project.root_object.project_references.map do |project_ref_and_products|
|
573
608
|
project_ref_and_products[:product_group].children.find do |product|
|
574
609
|
product.remote_ref.remote_global_id_string ==
|
575
610
|
container_item_proxy_change["remoteGlobalIDString"] &&
|
576
|
-
product.remote_ref.remote_info == container_item_proxy_change["remoteInfo"]
|
611
|
+
product.remote_ref.remote_info == container_item_proxy_change["remoteInfo"] &&
|
612
|
+
reference_filter.call(product)
|
577
613
|
end
|
578
614
|
end.compact
|
579
615
|
|
data/lib/kintsugi/cli.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright (c) 2021 Lightricks. All rights reserved.
|
2
|
+
# Created by Ben Yohay.
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
require_relative "version"
|
8
|
+
|
9
|
+
module Kintsugi
|
10
|
+
# Class resposible for creating the logic of various options for Kintsugi CLI.
|
11
|
+
class CLI
|
12
|
+
# Subcommands of Kintsugi CLI.
|
13
|
+
attr_reader :subcommands
|
14
|
+
|
15
|
+
# Root command Kintsugi CLI.
|
16
|
+
attr_reader :root_command
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@subcommands = {
|
20
|
+
"driver" => create_driver_subcommand
|
21
|
+
}.freeze
|
22
|
+
@root_command = create_root_command
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
Command = Struct.new(:option_parser, :action, :description, keyword_init: true)
|
28
|
+
|
29
|
+
def create_driver_subcommand
|
30
|
+
driver_option_parser =
|
31
|
+
OptionParser.new do |opts|
|
32
|
+
opts.banner = "Usage: kintsugi driver BASE OURS THEIRS ORIGINAL_FILE_PATH\n" \
|
33
|
+
"Uses Kintsugi as a Git merge driver. Parameters " \
|
34
|
+
"should be the path to base version of the file, path to ours version, path to " \
|
35
|
+
"theirs version, and the original file path."
|
36
|
+
|
37
|
+
opts.on("-h", "--help", "Prints this help") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
driver_action = lambda { |_, arguments, option_parser|
|
44
|
+
if arguments.count != 4
|
45
|
+
puts "Incorrect number of arguments to 'driver' subcommand\n\n"
|
46
|
+
puts option_parser
|
47
|
+
exit(1)
|
48
|
+
end
|
49
|
+
Kintsugi.three_way_merge(arguments[0], arguments[1], arguments[2], arguments[3])
|
50
|
+
}
|
51
|
+
|
52
|
+
Command.new(
|
53
|
+
option_parser: driver_option_parser,
|
54
|
+
action: driver_action,
|
55
|
+
description: "3-way merge compatible with Git merge driver"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_root_command
|
60
|
+
root_option_parser = OptionParser.new do |opts|
|
61
|
+
opts.banner = "Kintsugi, version #{Version::STRING}\n" \
|
62
|
+
"Copyright (c) 2021 Lightricks\n\n" \
|
63
|
+
"Usage: kintsugi <pbxproj_filepath> [options]\n" \
|
64
|
+
" kintsugi <subcommand> [options]"
|
65
|
+
|
66
|
+
opts.separator ""
|
67
|
+
opts.on("--changes-output-path=PATH", "Path to which changes applied to the project are " \
|
68
|
+
"written in JSON format. Used for debug purposes.")
|
69
|
+
|
70
|
+
opts.on("-h", "--help", "Prints this help") do
|
71
|
+
puts opts
|
72
|
+
exit
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on("-v", "--version", "Prints version") do
|
76
|
+
puts Version::STRING
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
subcommands_descriptions = @subcommands.map do |command_name, command|
|
81
|
+
" #{command_name}: #{command.description}"
|
82
|
+
end.join("\n")
|
83
|
+
opts.on_tail("\nSUBCOMMANDS\n#{subcommands_descriptions}")
|
84
|
+
end
|
85
|
+
|
86
|
+
root_action = lambda { |options, arguments, option_parser|
|
87
|
+
if arguments.count != 1
|
88
|
+
puts "Incorrect number of arguments\n\n"
|
89
|
+
puts option_parser
|
90
|
+
exit(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
project_file_path = File.expand_path(arguments[0])
|
94
|
+
Kintsugi.resolve_conflicts(project_file_path, options[:"changes-output-path"])
|
95
|
+
puts "Resolved conflicts successfully"
|
96
|
+
}
|
97
|
+
|
98
|
+
Command.new(
|
99
|
+
option_parser: root_option_parser,
|
100
|
+
action: root_action,
|
101
|
+
description: nil
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/kintsugi.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# Created by Ben Yohay.
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
+
require "json"
|
5
6
|
require "tmpdir"
|
6
7
|
require "tempfile"
|
7
8
|
require "xcodeproj"
|
@@ -16,7 +17,7 @@ module Kintsugi
|
|
16
17
|
# @param [String] project_file_path
|
17
18
|
# Project to which to apply the changes.
|
18
19
|
#
|
19
|
-
# @param [String]
|
20
|
+
# @param [String] changes_output_path
|
20
21
|
# Path to where the changes to apply to the project are written in JSON format.
|
21
22
|
#
|
22
23
|
# @raise [ArgumentError]
|
@@ -39,33 +40,68 @@ module Kintsugi
|
|
39
40
|
File.write(changes_output_path, JSON.pretty_generate(change))
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
project_in_temp_directory.save
|
45
|
-
|
46
|
-
Dir.chdir(File.dirname(project_file_path)) do
|
47
|
-
`git reset #{project_file_path}`
|
48
|
-
end
|
49
|
-
FileUtils.cp(File.join(project_in_temp_directory.path, "project.pbxproj"), project_file_path)
|
43
|
+
apply_change_and_copy_to_original_path(project_in_temp_directory, change, project_file_path)
|
44
|
+
end
|
50
45
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
# Merges the changes done between `theirs_project_path` and `base_project_path` to the file at
|
47
|
+
# `ours_project_path`. The files may not be at the original path, and therefore the
|
48
|
+
# `original_project_path` is required in order for the project metadata to be written properly.
|
49
|
+
#
|
50
|
+
# @param [String] base_project_path
|
51
|
+
# Path to the base version of the project.
|
52
|
+
#
|
53
|
+
# @param [String] ours_project_path
|
54
|
+
# Path to ours version of the project.
|
55
|
+
#
|
56
|
+
# @param [String] theirs_project_path
|
57
|
+
# Path to theirs version of the project.
|
58
|
+
#
|
59
|
+
# @param [String] original_project_path
|
60
|
+
# Path to the original path of the file.
|
61
|
+
#
|
62
|
+
# @raise [RuntimeError]
|
63
|
+
# If there was an error applying the change to the project.
|
64
|
+
#
|
65
|
+
# @return [void]
|
66
|
+
def three_way_merge(base_project_path, ours_project_path, theirs_project_path,
|
67
|
+
original_project_path)
|
68
|
+
original_directory_name = File.basename(File.dirname(original_project_path))
|
69
|
+
base_temporary_project =
|
70
|
+
copy_project_to_temporary_path_in_directory_with_name(base_project_path,
|
71
|
+
original_directory_name)
|
72
|
+
ours_temporary_project =
|
73
|
+
copy_project_to_temporary_path_in_directory_with_name(ours_project_path,
|
74
|
+
original_directory_name)
|
75
|
+
theirs_temporary_project =
|
76
|
+
copy_project_to_temporary_path_in_directory_with_name(theirs_project_path,
|
77
|
+
original_directory_name)
|
78
|
+
|
79
|
+
change =
|
80
|
+
Xcodeproj::Differ.project_diff(theirs_temporary_project, base_temporary_project,
|
81
|
+
:added, :removed)
|
82
|
+
|
83
|
+
apply_change_and_copy_to_original_path(ours_temporary_project, change, ours_project_path)
|
56
84
|
end
|
57
85
|
|
58
86
|
private
|
59
87
|
|
60
|
-
|
61
|
-
if File.extname(project_file_path) != ".pbxproj"
|
62
|
-
raise ArgumentError, "Wrong file extension, please provide file with extension .pbxproj\""
|
63
|
-
end
|
88
|
+
PROJECT_FILE_NAME = "project.pbxproj"
|
64
89
|
|
90
|
+
def apply_change_and_copy_to_original_path(project, change, original_project_file_path)
|
91
|
+
apply_change_to_project(project, change)
|
92
|
+
project.save
|
93
|
+
FileUtils.cp(File.join(project.path, PROJECT_FILE_NAME), original_project_file_path)
|
94
|
+
end
|
95
|
+
|
96
|
+
def validate_project(project_file_path)
|
65
97
|
unless File.exist?(project_file_path)
|
66
98
|
raise ArgumentError, "File '#{project_file_path}' doesn't exist"
|
67
99
|
end
|
68
100
|
|
101
|
+
if File.extname(project_file_path) != ".pbxproj"
|
102
|
+
raise ArgumentError, "Wrong file extension, please provide file with extension .pbxproj\""
|
103
|
+
end
|
104
|
+
|
69
105
|
Dir.chdir(File.dirname(project_file_path)) do
|
70
106
|
unless file_has_base_ours_and_theirs_versions?(project_file_path)
|
71
107
|
raise ArgumentError, "File '#{project_file_path}' doesn't have conflicts, or a 3-way " \
|
@@ -74,8 +110,19 @@ module Kintsugi
|
|
74
110
|
end
|
75
111
|
end
|
76
112
|
|
113
|
+
def copy_project_to_temporary_path_in_directory_with_name(project_file_path, directory_name)
|
114
|
+
temp_directory_name = File.join(Dir.mktmpdir, directory_name)
|
115
|
+
Dir.mkdir(temp_directory_name)
|
116
|
+
temp_project_file_path = File.join(temp_directory_name, PROJECT_FILE_NAME)
|
117
|
+
FileUtils.cp(project_file_path, temp_project_file_path)
|
118
|
+
Xcodeproj::Project.open(File.dirname(temp_project_file_path))
|
119
|
+
end
|
120
|
+
|
77
121
|
def open_project_of_current_commit_in_temporary_directory(project_file_path)
|
78
|
-
|
122
|
+
project_directory_name = File.basename(File.dirname(project_file_path))
|
123
|
+
temp_directory_name = File.join(Dir.mktmpdir, project_directory_name)
|
124
|
+
Dir.mkdir(temp_directory_name)
|
125
|
+
temp_project_file_path = File.join(temp_directory_name, PROJECT_FILE_NAME)
|
79
126
|
Dir.chdir(File.dirname(project_file_path)) do
|
80
127
|
`git show HEAD:./project.pbxproj > #{temp_project_file_path}`
|
81
128
|
end
|
@@ -99,11 +146,11 @@ module Kintsugi
|
|
99
146
|
|
100
147
|
def change_of_conflicting_commit_with_parent(project_file_path)
|
101
148
|
Dir.chdir(File.dirname(project_file_path)) do
|
102
|
-
conflicting_commit_project_file_path = File.join(Dir.mktmpdir,
|
103
|
-
`git show :3
|
149
|
+
conflicting_commit_project_file_path = File.join(Dir.mktmpdir, PROJECT_FILE_NAME)
|
150
|
+
`git show :3:./#{PROJECT_FILE_NAME} > #{conflicting_commit_project_file_path}`
|
104
151
|
|
105
|
-
conflicting_commit_parent_project_file_path = File.join(Dir.mktmpdir,
|
106
|
-
`git show :1
|
152
|
+
conflicting_commit_parent_project_file_path = File.join(Dir.mktmpdir, PROJECT_FILE_NAME)
|
153
|
+
`git show :1:./#{PROJECT_FILE_NAME} > #{conflicting_commit_parent_project_file_path}`
|
107
154
|
|
108
155
|
conflicting_commit_project = Xcodeproj::Project.open(
|
109
156
|
File.dirname(conflicting_commit_project_file_path)
|
@@ -38,6 +38,18 @@ describe Kintsugi, :apply_change_to_project do
|
|
38
38
|
expect(base_project).to be_equivalent_to_project(theirs_project)
|
39
39
|
end
|
40
40
|
|
41
|
+
it "adds new aggregate target" do
|
42
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
43
|
+
theirs_project.new_aggregate_target("foo")
|
44
|
+
|
45
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
46
|
+
|
47
|
+
described_class.apply_change_to_project(base_project, changes_to_apply)
|
48
|
+
base_project.save
|
49
|
+
|
50
|
+
expect(base_project).to be_equivalent_to_project(theirs_project)
|
51
|
+
end
|
52
|
+
|
41
53
|
it "adds new subproject" do
|
42
54
|
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
43
55
|
add_new_subproject_to_project(theirs_project, "foo", "foo")
|
@@ -50,6 +62,26 @@ describe Kintsugi, :apply_change_to_project do
|
|
50
62
|
expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
|
51
63
|
end
|
52
64
|
|
65
|
+
it "adds subproject that already exists" do
|
66
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
67
|
+
|
68
|
+
subproject = add_new_subproject_to_project(theirs_project, "foo", "foo")
|
69
|
+
theirs_project.save
|
70
|
+
|
71
|
+
ours_project = create_copy_of_project(base_project.path, "ours")
|
72
|
+
add_existing_subproject_to_project(ours_project, subproject, "foo")
|
73
|
+
|
74
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
75
|
+
|
76
|
+
described_class.apply_change_to_project(ours_project, changes_to_apply)
|
77
|
+
ours_project.save
|
78
|
+
|
79
|
+
expect(ours_project.root_object.project_references[0][:project_ref].uuid)
|
80
|
+
.not_to equal(ours_project.root_object.project_references[1][:project_ref].uuid)
|
81
|
+
expect(ours_project.root_object.project_references[0][:project_ref].proxy_containers).not_to be_empty
|
82
|
+
expect(ours_project.root_object.project_references[1][:project_ref].proxy_containers).not_to be_empty
|
83
|
+
end
|
84
|
+
|
53
85
|
# Checks that the order the changes are applied in is correct.
|
54
86
|
it "adds new subproject and reference to its framework" do
|
55
87
|
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
@@ -144,6 +176,23 @@ describe Kintsugi, :apply_change_to_project do
|
|
144
176
|
expect(base_project).to be_equivalent_to_project(theirs_project)
|
145
177
|
end
|
146
178
|
|
179
|
+
it "changes simple attribute of a file that has a build file" do
|
180
|
+
target = base_project.new_target("com.apple.product-type.library.static", "bar", :ios)
|
181
|
+
file_reference = base_project.main_group.find_file_by_path(filepath)
|
182
|
+
target.frameworks_build_phase.add_file_reference(file_reference)
|
183
|
+
base_project.save
|
184
|
+
|
185
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
186
|
+
file_reference = theirs_project.main_group.find_file_by_path(filepath)
|
187
|
+
file_reference.include_in_index = "4"
|
188
|
+
|
189
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
190
|
+
|
191
|
+
described_class.apply_change_to_project(base_project, changes_to_apply)
|
192
|
+
|
193
|
+
expect(base_project).to be_equivalent_to_project(theirs_project)
|
194
|
+
end
|
195
|
+
|
147
196
|
it "removes build files of a removed file" do
|
148
197
|
target = base_project.new_target("com.apple.product-type.library.static", "foo", :ios)
|
149
198
|
target.source_build_phase.add_file_reference(
|
@@ -417,6 +466,30 @@ describe Kintsugi, :apply_change_to_project do
|
|
417
466
|
expect(base_project).to be_equivalent_to_project(theirs_project, ignore_keys: ["containerPortal"])
|
418
467
|
end
|
419
468
|
|
469
|
+
it "adds build file to a file reference that already exist" do
|
470
|
+
file_reference = base_project.main_group.new_reference("bar")
|
471
|
+
base_project.targets[0].frameworks_build_phase.add_file_reference(file_reference)
|
472
|
+
|
473
|
+
base_project.main_group.new_reference("bar")
|
474
|
+
|
475
|
+
base_project.save
|
476
|
+
|
477
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
478
|
+
|
479
|
+
theirs_file_reference = theirs_project.main_group.files.find do |file|
|
480
|
+
!file.referrers.find { |referrer| referrer.is_a?(Xcodeproj::Project::PBXBuildFile) } &&
|
481
|
+
file.display_name == "bar"
|
482
|
+
end
|
483
|
+
theirs_project.targets[0].frameworks_build_phase.add_file_reference(theirs_file_reference)
|
484
|
+
|
485
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
486
|
+
|
487
|
+
described_class.apply_change_to_project(base_project, changes_to_apply)
|
488
|
+
base_project.save
|
489
|
+
|
490
|
+
expect(base_project).to be_equivalent_to_project(theirs_project)
|
491
|
+
end
|
492
|
+
|
420
493
|
it "adds file reference to build file" do
|
421
494
|
file_reference = base_project.main_group.new_reference("bar")
|
422
495
|
|
@@ -770,7 +843,44 @@ describe Kintsugi, :apply_change_to_project do
|
|
770
843
|
expect(base_project).to be_equivalent_to_project(theirs_project)
|
771
844
|
end
|
772
845
|
|
773
|
-
it "
|
846
|
+
it "removes attribute target changes from a project it was removed from already" do
|
847
|
+
base_project.root_object.attributes["TargetAttributes"] =
|
848
|
+
{"foo" => {"LastSwiftMigration" => "1140"}}
|
849
|
+
base_project.save
|
850
|
+
|
851
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
852
|
+
theirs_project.root_object.attributes["TargetAttributes"]["foo"] = {}
|
853
|
+
|
854
|
+
ours_project = create_copy_of_project(base_project.path, "ours")
|
855
|
+
ours_project.root_object.attributes["TargetAttributes"]["foo"] = {}
|
856
|
+
|
857
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
858
|
+
|
859
|
+
described_class.apply_change_to_project(ours_project, changes_to_apply)
|
860
|
+
ours_project.save
|
861
|
+
|
862
|
+
expect(ours_project).to be_equivalent_to_project(theirs_project)
|
863
|
+
end
|
864
|
+
|
865
|
+
it "doesn't throw if existing attribute target change is same as added change" do
|
866
|
+
base_project.root_object.attributes["TargetAttributes"] = {"foo" => "1140"}
|
867
|
+
base_project.save
|
868
|
+
|
869
|
+
theirs_project = create_copy_of_project(base_project.path, "theirs")
|
870
|
+
theirs_project.root_object.attributes["TargetAttributes"]["foo"] = "1111"
|
871
|
+
|
872
|
+
ours_project = create_copy_of_project(base_project.path, "ours")
|
873
|
+
ours_project.root_object.attributes["TargetAttributes"]["foo"] = "1111"
|
874
|
+
|
875
|
+
changes_to_apply = get_diff(theirs_project, base_project)
|
876
|
+
|
877
|
+
described_class.apply_change_to_project(ours_project, changes_to_apply)
|
878
|
+
ours_project.save
|
879
|
+
|
880
|
+
expect(ours_project).to be_equivalent_to_project(theirs_project)
|
881
|
+
end
|
882
|
+
|
883
|
+
it "identifies subproject added at separate times when adding a product to the subproject" do
|
774
884
|
framework_filename = "baz"
|
775
885
|
|
776
886
|
subproject = new_subproject("subproj", framework_filename)
|
@@ -851,10 +961,10 @@ describe Kintsugi, :apply_change_to_project do
|
|
851
961
|
file_reference.path == subproject_product_name
|
852
962
|
end.remove_from_project
|
853
963
|
|
854
|
-
project.root_object.project_references[
|
964
|
+
project.root_object.project_references[-1][:product_group] =
|
855
965
|
project.new(Xcodeproj::Project::PBXGroup)
|
856
|
-
project.root_object.project_references[
|
857
|
-
project.root_object.project_references[
|
966
|
+
project.root_object.project_references[-1][:product_group].name = "Products"
|
967
|
+
project.root_object.project_references[-1][:product_group] <<
|
858
968
|
create_reference_proxy_from_product_reference(project, subproject_reference,
|
859
969
|
subproject.products_group.files[0])
|
860
970
|
end
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kintsugi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Yohay
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: xcodeproj
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.19.0
|
20
|
+
- - "<="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.21.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: 1.19.0
|
30
|
+
- - "<="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.21.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: rake
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,7 +122,9 @@ files:
|
|
116
122
|
- kintsugi.gemspec
|
117
123
|
- lib/kintsugi.rb
|
118
124
|
- lib/kintsugi/apply_change_to_project.rb
|
125
|
+
- lib/kintsugi/cli.rb
|
119
126
|
- lib/kintsugi/utils.rb
|
127
|
+
- lib/kintsugi/version.rb
|
120
128
|
- lib/kintsugi/xcodeproj_extensions.rb
|
121
129
|
- logo/kintsugi.png
|
122
130
|
- spec/be_equivalent_to_project.rb
|
@@ -141,7 +149,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
149
|
- !ruby/object:Gem::Version
|
142
150
|
version: '0'
|
143
151
|
requirements: []
|
144
|
-
|
152
|
+
rubyforge_project:
|
153
|
+
rubygems_version: 2.7.3
|
145
154
|
signing_key:
|
146
155
|
specification_version: 4
|
147
156
|
summary: pbxproj files git conflicts solver
|