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