2023-02-21 15:39:58 +00:00
|
|
|
#!/usr/bin/env python3
|
2023-08-15 14:20:26 +00:00
|
|
|
# SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-06-14 13:06:58 +00:00
|
|
|
#
|
2023-02-21 15:39:58 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
import collections
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# Hashes to be ignored
|
|
|
|
#
|
2023-09-03 11:35:03 +00:00
|
|
|
# The system sometimes fails to match commits and suggests to back-port
|
2023-02-21 15:39:58 +00:00
|
|
|
# revision which was already ported. In order to solve that we can:
|
|
|
|
#
|
|
|
|
# - Explicitly ignore some of the commits.
|
|
|
|
# - Move the synchronization point forward.
|
|
|
|
IGNORE_HASHES = {
|
|
|
|
}
|
|
|
|
|
|
|
|
# Start revisions from both repositories.
|
|
|
|
CYCLES_START_COMMIT = b"b941eccba81bbb1309a0eb4977fc3a77796f4ada" # blender-v2.92
|
|
|
|
BLENDER_START_COMMIT = b"02948a2cab44f74ed101fc1b2ad9fe4431123e85" # v2.92
|
|
|
|
|
|
|
|
# Prefix which is common for all the subjects.
|
|
|
|
GIT_SUBJECT_COMMON_PREFIX = b"Subject: [PATCH] "
|
|
|
|
|
|
|
|
# Marker which indicates begin of new file in the patch set.
|
|
|
|
GIT_FILE_SECTION_MARKER = b"diff --git"
|
|
|
|
|
2023-09-05 00:49:20 +00:00
|
|
|
# Marker of the end of the patch-set.
|
2023-02-21 15:39:58 +00:00
|
|
|
GIT_PATCHSET_END_MARKER = b"-- "
|
|
|
|
|
|
|
|
# Prefix of topic to be omitted
|
|
|
|
SUBJECT_SKIP_PREFIX = (
|
|
|
|
b"Cycles: ",
|
|
|
|
b"cycles: ",
|
|
|
|
b"Cycles Standalone: ",
|
|
|
|
b"Cycles standalone: ",
|
|
|
|
b"cycles standalone: ",
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def subject_strip(common_prefix, subject):
|
|
|
|
for prefix in SUBJECT_SKIP_PREFIX:
|
|
|
|
full_prefix = common_prefix + prefix
|
|
|
|
if subject.startswith(full_prefix):
|
|
|
|
subject = subject[len(full_prefix):].capitalize()
|
|
|
|
subject = common_prefix + subject
|
|
|
|
break
|
|
|
|
return subject
|
|
|
|
|
|
|
|
|
|
|
|
def replace_file_prefix(path, prefix, replace_prefix):
|
|
|
|
tokens = path.split(b' ')
|
|
|
|
prefix_len = len(prefix)
|
|
|
|
for i, t in enumerate(tokens):
|
|
|
|
for x in (b"a/", b"b/"):
|
|
|
|
if t.startswith(x + prefix):
|
|
|
|
tokens[i] = x + replace_prefix + t[prefix_len + 2:]
|
|
|
|
return b' '.join(tokens)
|
|
|
|
|
|
|
|
|
|
|
|
def cleanup_patch(patch, accept_prefix, replace_prefix):
|
|
|
|
assert accept_prefix[0] != b'/'
|
|
|
|
assert replace_prefix[0] != b'/'
|
|
|
|
|
|
|
|
full_accept_prefix = GIT_FILE_SECTION_MARKER + b" a/" + accept_prefix
|
|
|
|
|
|
|
|
with open(patch, "rb") as f:
|
|
|
|
content = f.readlines()
|
|
|
|
|
|
|
|
clean_content = []
|
|
|
|
do_skip = False
|
|
|
|
for line in content:
|
|
|
|
if line.startswith(GIT_SUBJECT_COMMON_PREFIX):
|
|
|
|
# Skip possible prefix like "Cycles:", we already know change is
|
|
|
|
# about Cycles since it's being committed to a Cycles repository.
|
|
|
|
line = subject_strip(GIT_SUBJECT_COMMON_PREFIX, line)
|
|
|
|
|
|
|
|
# Dots usually are omitted in the topic
|
|
|
|
line = line.replace(b".\n", b"\n")
|
|
|
|
elif line.startswith(GIT_FILE_SECTION_MARKER):
|
|
|
|
if not line.startswith(full_accept_prefix):
|
|
|
|
do_skip = True
|
|
|
|
else:
|
|
|
|
do_skip = False
|
|
|
|
line = replace_file_prefix(line, accept_prefix, replace_prefix)
|
|
|
|
elif line.startswith(GIT_PATCHSET_END_MARKER):
|
|
|
|
do_skip = False
|
|
|
|
elif line.startswith(b"---") or line.startswith(b"+++"):
|
|
|
|
line = replace_file_prefix(line, accept_prefix, replace_prefix)
|
|
|
|
|
|
|
|
if not do_skip:
|
|
|
|
clean_content.append(line)
|
|
|
|
|
|
|
|
with open(patch, "wb") as f:
|
|
|
|
f.writelines(clean_content)
|
|
|
|
|
|
|
|
|
|
|
|
# Get mapping from commit subject to commit hash.
|
|
|
|
#
|
|
|
|
# It'll actually include timestamp of the commit to the map key, so commits with
|
|
|
|
# the same subject wouldn't conflict with each other.
|
|
|
|
def commit_map_get(repository, path, start_commit):
|
|
|
|
command = (b"git",
|
|
|
|
b"--git-dir=" + os.path.join(repository, b'.git'),
|
|
|
|
b"--work-tree=" + repository,
|
|
|
|
b"log", b"--format=%H %at %s", b"--reverse",
|
|
|
|
start_commit + b'..HEAD',
|
|
|
|
b'--',
|
|
|
|
os.path.join(repository, path),
|
|
|
|
b':(exclude)' + os.path.join(repository, b'intern/cycles/blender'))
|
|
|
|
lines = subprocess.check_output(command).split(b"\n")
|
|
|
|
commit_map = collections.OrderedDict()
|
|
|
|
for line in lines:
|
|
|
|
if line:
|
|
|
|
commit_sha, stamped_subject = line.split(b' ', 1)
|
|
|
|
stamp, subject = stamped_subject.split(b' ', 1)
|
|
|
|
subject = subject_strip(b"", subject).rstrip(b".")
|
|
|
|
stamped_subject = stamp + b" " + subject
|
|
|
|
|
|
|
|
if commit_sha in IGNORE_HASHES:
|
|
|
|
continue
|
|
|
|
commit_map[stamped_subject] = commit_sha
|
|
|
|
return commit_map
|
|
|
|
|
|
|
|
|
|
|
|
# Get difference between two lists of commits.
|
|
|
|
# Returns two lists: first are the commits to be ported from Cycles to Blender,
|
|
|
|
# second one are the commits to be ported from Blender to Cycles.
|
|
|
|
def commits_get_difference(cycles_map, blender_map):
|
|
|
|
cycles_to_blender = []
|
|
|
|
for stamped_subject, commit_hash in cycles_map.items():
|
|
|
|
if stamped_subject not in blender_map:
|
|
|
|
cycles_to_blender.append(commit_hash)
|
|
|
|
|
|
|
|
blender_to_cycles = []
|
|
|
|
for stamped_subject, commit_hash in blender_map.items():
|
|
|
|
if stamped_subject not in cycles_map:
|
|
|
|
blender_to_cycles.append(commit_hash)
|
|
|
|
|
|
|
|
return cycles_to_blender, blender_to_cycles
|
|
|
|
|
|
|
|
|
|
|
|
# Transfer commits from one repository to another.
|
|
|
|
# Doesn't do actual commit just for the safety.
|
|
|
|
def transfer_commits(commit_hashes,
|
|
|
|
from_repository,
|
|
|
|
to_repository,
|
|
|
|
dst_is_cycles):
|
|
|
|
patch_index = 1
|
|
|
|
for commit_hash in commit_hashes:
|
|
|
|
command = (
|
|
|
|
b"git",
|
|
|
|
b"--git-dir=" + os.path.join(from_repository, b'.git'),
|
|
|
|
b"--work-tree=" + from_repository,
|
|
|
|
b"format-patch", b"-1",
|
|
|
|
b"--start-number", bytes(str(patch_index), 'utf-8'),
|
|
|
|
b"-o", to_repository,
|
|
|
|
commit_hash,
|
|
|
|
b'--',
|
|
|
|
b':(exclude)' + os.path.join(from_repository, b'intern/cycles/blender'),
|
|
|
|
)
|
|
|
|
patch_file = subprocess.check_output(command).rstrip(b"\n")
|
|
|
|
if dst_is_cycles:
|
|
|
|
cleanup_patch(patch_file, b"intern/cycles", b"src")
|
|
|
|
else:
|
|
|
|
cleanup_patch(patch_file, b"src", b"intern/cycles")
|
|
|
|
patch_index += 1
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
if len(sys.argv) != 3:
|
|
|
|
print("Usage: %s /path/to/cycles/ /path/to/blender/" % sys.argv[0])
|
|
|
|
return
|
|
|
|
|
|
|
|
cycles_repository = sys.argv[1].encode()
|
|
|
|
blender_repository = sys.argv[2].encode()
|
|
|
|
|
|
|
|
cycles_map = commit_map_get(cycles_repository, b'', CYCLES_START_COMMIT)
|
|
|
|
blender_map = commit_map_get(blender_repository, b"intern/cycles", BLENDER_START_COMMIT)
|
|
|
|
diff = commits_get_difference(cycles_map, blender_map)
|
|
|
|
|
|
|
|
transfer_commits(diff[0], cycles_repository, blender_repository, False)
|
|
|
|
transfer_commits(diff[1], blender_repository, cycles_repository, True)
|
|
|
|
|
|
|
|
print("Missing commits were saved to the blender and cycles repositories.")
|
|
|
|
print("Check them and if they're all fine run:")
|
|
|
|
print("")
|
|
|
|
print(" git am *.patch")
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|