Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
196 lines
6.6 KiB
Python
Executable File
196 lines
6.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-FileCopyrightText: 2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import collections
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# Hashes to be ignored
|
|
#
|
|
# The system sometimes fails to match commits and suggests to backport
|
|
# 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"
|
|
|
|
# Marker of the end of the patchset.
|
|
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()
|