mirror of https://github.com/apache/lucene.git
144 lines
5.3 KiB
Python
144 lines
5.3 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership.
|
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
|
# (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
'''
|
|
Creates a unified diff, applyable by the patch tool, between two source checkouts.
|
|
|
|
Note that .gitignore or svn:ignore rules are used to filter out files that would normally
|
|
not be checked in.
|
|
|
|
While you could use this to make a committable patch from a branch,
|
|
that approach loses the svn history from the branch (better to use
|
|
"svn merge --reintegrate", for example). This diff output should
|
|
not be considered "authoritative" from a merging standpoint as it
|
|
does not reflect what svn will do on merge.
|
|
'''
|
|
|
|
from argparse import ArgumentParser, RawTextHelpFormatter
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
def make_filter_func(src_root, src_dir):
|
|
git_root = os.path.join(src_root, '.git')
|
|
if os.path.exists(git_root):
|
|
def git_filter(filename):
|
|
rc = subprocess.call('git --git-dir=%s check-ignore %s' % (git_root, filename), shell=True, stdout=subprocess.DEVNULL)
|
|
return rc == 0
|
|
return git_filter
|
|
|
|
else:
|
|
def svn_filter(filename):
|
|
# we can't find if svn will ignore a file unless it exists...
|
|
created = False
|
|
if not os.path.exists(filename):
|
|
head,tail = os.path.split(filename)
|
|
# find a parent directory that already exists, so we
|
|
# can see if that is ignored by svn
|
|
while not os.path.exists(head):
|
|
filename = head
|
|
head,tail = os.path.split(filename)
|
|
created = True
|
|
subprocess.check_call('touch %s' % filename, shell=True)
|
|
try:
|
|
output = subprocess.check_output('svn status %s' % filename,
|
|
shell=True, stderr=subprocess.STDOUT).strip()
|
|
return output.startswith(b'I')
|
|
finally:
|
|
if created and os.path.exists(filename):
|
|
os.remove(filename)
|
|
|
|
return svn_filter
|
|
|
|
def print_filtered_output(output, should_filter):
|
|
filtering = False
|
|
line = output.readline()
|
|
while line:
|
|
|
|
if line.startswith(b'diff '):
|
|
fromfile, tofile = line.decode('utf-8').split()[-2:]
|
|
if os.path.exists(fromfile) or \
|
|
os.path.exists(tofile):
|
|
filtering = should_filter(fromfile)
|
|
else:
|
|
# If both files do not exist, then the filename must contain spaces,
|
|
# which breaks our split logic. In this case, just ignore, since
|
|
# patch cannot handle filenames with spaces anyways.
|
|
filtering = True
|
|
elif line.startswith(b'Binary files'):
|
|
filtering = True
|
|
|
|
if not filtering:
|
|
print(line.decode('utf-8'), end='')
|
|
|
|
line = output.readline()
|
|
|
|
def run_diff(from_dir, to_dir, skip_whitespace):
|
|
flags = '-ruN'
|
|
if skip_whitespace:
|
|
flags += 'bBw'
|
|
|
|
args = ['diff', flags]
|
|
for ignore in ('.svn', '.git', 'build', '.caches', '.idea', 'idea-build', 'eclipse-build', '.settings'):
|
|
args.append('-x')
|
|
args.append(ignore)
|
|
args.append(from_dir)
|
|
args.append(to_dir)
|
|
|
|
return subprocess.Popen(args, shell=False, stdout=subprocess.PIPE)
|
|
|
|
def find_root(path):
|
|
relative = []
|
|
while not os.path.exists(os.path.join(path, 'lucene', 'CHANGES.txt')):
|
|
path, base = os.path.split(path)
|
|
relative.insert(0, base)
|
|
return path, '' if not relative else os.path.normpath(os.path.join(*relative))
|
|
|
|
def parse_config():
|
|
parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
|
|
parser.add_argument('--skip-whitespace', action='store_true', default=False,
|
|
help='Ignore whitespace differences')
|
|
parser.add_argument('from_dir', help='Source directory to diff from')
|
|
parser.add_argument('to_dir', help='Source directory to diff to')
|
|
c = parser.parse_args()
|
|
|
|
if not os.path.isdir(c.from_dir):
|
|
parser.error('\'from\' path %s is not a valid directory' % c.from_dir)
|
|
(c.from_root, from_relative) = find_root(c.from_dir)
|
|
if c.from_root is None:
|
|
parser.error('\'from\' path %s is not relative to a lucene/solr checkout' % c.from_dir)
|
|
if not os.path.isdir(c.to_dir):
|
|
parser.error('\'to\' path %s is not a valid directory' % c.to_dir)
|
|
(c.to_root, to_relative) = find_root(c.to_dir)
|
|
if c.to_root is None:
|
|
parser.error('\'to\' path %s is not relative to a lucene/solr checkout' % c.to_dir)
|
|
if from_relative != to_relative:
|
|
parser.error('\'from\' and \'to\' path are not equivalent relative paths within their'
|
|
' checkouts: %r != %r' % (from_relative, to_relative))
|
|
return c
|
|
|
|
def main():
|
|
c = parse_config()
|
|
|
|
p = run_diff(c.from_dir, c.to_dir, c.skip_whitespace)
|
|
should_filter = make_filter_func(c.from_root, c.from_dir)
|
|
print_filtered_output(p.stdout, should_filter)
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print('\nReceived Ctrl-C, exiting early')
|
|
|