diff options
Diffstat (limited to 'gnu/llvm/utils/git-svn/git-llvm')
| -rwxr-xr-x | gnu/llvm/utils/git-svn/git-llvm | 429 |
1 files changed, 0 insertions, 429 deletions
diff --git a/gnu/llvm/utils/git-svn/git-llvm b/gnu/llvm/utils/git-svn/git-llvm deleted file mode 100755 index bf234301f13..00000000000 --- a/gnu/llvm/utils/git-svn/git-llvm +++ /dev/null @@ -1,429 +0,0 @@ -#!/usr/bin/env python -# -# ======- git-llvm - LLVM Git Help Integration ---------*- python -*--========# -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -# ==------------------------------------------------------------------------==# - -""" -git-llvm integration -==================== - -This file provides integration for git. -""" - -from __future__ import print_function -import argparse -import collections -import contextlib -import errno -import os -import re -import subprocess -import sys -import tempfile -import time -assert sys.version_info >= (2, 7) - -try: - dict.iteritems -except AttributeError: - # Python 3 - def iteritems(d): - return iter(d.items()) -else: - # Python 2 - def iteritems(d): - return d.iteritems() - -# It's *almost* a straightforward mapping from the monorepo to svn... -GIT_TO_SVN_DIR = { - d: (d + '/branches/release_80') - for d in [ - 'clang-tools-extra', - 'compiler-rt', - 'debuginfo-tests', - 'dragonegg', - 'klee', - 'libclc', - 'libcxx', - 'libcxxabi', - 'libunwind', - 'lld', - 'lldb', - 'llgo', - 'llvm', - 'openmp', - 'parallel-libs', - 'polly', - 'pstl', - ] -} -GIT_TO_SVN_DIR.update({'clang': 'cfe/branches/release_80'}) -GIT_TO_SVN_DIR.update({'': 'monorepo-root/branches/release_80'}) - -VERBOSE = False -QUIET = False -dev_null_fd = None - - -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - - -def log(*args, **kwargs): - if QUIET: - return - print(*args, **kwargs) - - -def log_verbose(*args, **kwargs): - if not VERBOSE: - return - print(*args, **kwargs) - - -def die(msg): - eprint(msg) - sys.exit(1) - - -def split_first_path_component(d): - # Assuming we have a git path, it'll use slashes even on windows...I hope. - if '/' in d: - return d.split('/', 1) - else: - return (d, None) - - -def get_dev_null(): - """Lazily create a /dev/null fd for use in shell()""" - global dev_null_fd - if dev_null_fd is None: - dev_null_fd = open(os.devnull, 'w') - return dev_null_fd - - -def shell(cmd, strip=True, cwd=None, stdin=None, die_on_failure=True, - ignore_errors=False, text=True): - log_verbose('Running in %s: %s' % (cwd, ' '.join(cmd))) - - err_pipe = subprocess.PIPE - if ignore_errors: - # Silence errors if requested. - err_pipe = get_dev_null() - - start = time.time() - p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=err_pipe, - stdin=subprocess.PIPE, - universal_newlines=text) - stdout, stderr = p.communicate(input=stdin) - elapsed = time.time() - start - - log_verbose('Command took %0.1fs' % elapsed) - - if p.returncode == 0 or ignore_errors: - if stderr and not ignore_errors: - eprint('`%s` printed to stderr:' % ' '.join(cmd)) - eprint(stderr.rstrip()) - if strip: - if text: - stdout = stdout.rstrip('\r\n') - else: - stdout = stdout.rstrip(b'\r\n') - if VERBOSE: - for l in stdout.splitlines(): - log_verbose("STDOUT: %s" % l) - return stdout - err_msg = '`%s` returned %s' % (' '.join(cmd), p.returncode) - eprint(err_msg) - if stderr: - eprint(stderr.rstrip()) - if die_on_failure: - sys.exit(2) - raise RuntimeError(err_msg) - - -def git(*cmd, **kwargs): - return shell(['git'] + list(cmd), **kwargs) - - -def svn(cwd, *cmd, **kwargs): - return shell(['svn'] + list(cmd), cwd=cwd, **kwargs) - -def program_exists(cmd): - if sys.platform == 'win32' and not cmd.endswith('.exe'): - cmd += '.exe' - for path in os.environ["PATH"].split(os.pathsep): - if os.access(os.path.join(path, cmd), os.X_OK): - return True - return False - -def get_default_rev_range(): - # Get the branch tracked by the current branch, as set by - # git branch --set-upstream-to See http://serverfault.com/a/352236/38694. - cur_branch = git('rev-parse', '--symbolic-full-name', 'HEAD') - upstream_branch = git('for-each-ref', '--format=%(upstream:short)', - cur_branch) - if not upstream_branch: - upstream_branch = 'origin/master' - - # Get the newest common ancestor between HEAD and our upstream branch. - upstream_rev = git('merge-base', 'HEAD', upstream_branch) - return '%s..' % upstream_rev - - -def get_revs_to_push(rev_range): - if not rev_range: - rev_range = get_default_rev_range() - # Use git show rather than some plumbing command to figure out which revs - # are in rev_range because it handles single revs (HEAD^) and ranges - # (foo..bar) like we want. - revs = git('show', '--reverse', '--quiet', - '--pretty=%h', rev_range).splitlines() - if not revs: - die('Nothing to push: No revs in range %s.' % rev_range) - return revs - - -def clean_svn(svn_repo): - svn(svn_repo, 'revert', '-R', '.') - - # Unfortunately it appears there's no svn equivalent for git clean, so we - # have to do it ourselves. - for line in svn(svn_repo, 'status', '--no-ignore').split('\n'): - if not line.startswith('?'): - continue - filename = line[1:].strip() - os.remove(os.path.join(svn_repo, filename)) - - -def svn_init(svn_root): - if not os.path.exists(svn_root): - log('Creating svn staging directory: (%s)' % (svn_root)) - os.makedirs(svn_root) - svn(svn_root, 'checkout', '--depth=empty', - 'https://llvm.org/svn/llvm-project/', '.') - log("svn staging area ready in '%s'" % svn_root) - if not os.path.isdir(svn_root): - die("Can't initialize svn staging dir (%s)" % svn_root) - - -def fix_eol_style_native(rev, svn_sr_path, files): - """Fix line endings before applying patches with Unix endings - - SVN on Windows will check out files with CRLF for files with the - svn:eol-style property set to "native". This breaks `git apply`, which - typically works with Unix-line ending patches. Work around the problem here - by doing a dos2unix up front for files with svn:eol-style set to "native". - SVN will not commit a mass line ending re-doing because it detects the line - ending format for files with this property. - """ - # Skip files that don't exist in SVN yet. - files = [f for f in files if os.path.exists(os.path.join(svn_sr_path, f))] - # Use ignore_errors because 'svn propget' prints errors if the file doesn't - # have the named property. There doesn't seem to be a way to suppress that. - eol_props = svn(svn_sr_path, 'propget', 'svn:eol-style', *files, - ignore_errors=True) - crlf_files = [] - if len(files) == 1: - # No need to split propget output on ' - ' when we have one file. - if eol_props.strip() in ['native', 'CRLF']: - crlf_files = files - else: - for eol_prop in eol_props.split('\n'): - # Remove spare CR. - eol_prop = eol_prop.strip('\r') - if not eol_prop: - continue - prop_parts = eol_prop.rsplit(' - ', 1) - if len(prop_parts) != 2: - eprint("unable to parse svn propget line:") - eprint(eol_prop) - continue - (f, eol_style) = prop_parts - if eol_style == 'native': - crlf_files.append(f) - if crlf_files: - # Reformat all files with native SVN line endings to Unix format. SVN - # knows files with native line endings are text files. It will commit - # just the diff, and not a mass line ending change. - shell(['dos2unix'] + crlf_files, ignore_errors=True, cwd=svn_sr_path) - -def split_subrepo(f): - # Given a path, splits it into (subproject, rest-of-path). If the path is - # not in a subproject, returns ('', full-path). - - subproject, remainder = split_first_path_component(f) - - if subproject in GIT_TO_SVN_DIR: - return subproject, remainder - else: - return '', f - -def get_all_parent_dirs(name): - parts = [] - head, tail = os.path.split(name) - while head: - parts.append(head) - head, tail = os.path.split(head) - return parts - -def svn_push_one_rev(svn_repo, rev, dry_run): - files = git('diff-tree', '--no-commit-id', '--name-only', '-r', - rev).split('\n') - if not files: - raise RuntimeError('Empty diff for rev %s?' % rev) - - # Split files by subrepo - subrepo_files = collections.defaultdict(list) - for f in files: - subrepo, remainder = split_subrepo(f) - subrepo_files[subrepo].append(remainder) - - status = svn(svn_repo, 'status', '--no-ignore') - if status: - die("Can't push git rev %s because svn status is not empty:\n%s" % - (rev, status)) - - svn_dirs_to_update = set() - for sr, files in iteritems(subrepo_files): - svn_sr_path = GIT_TO_SVN_DIR[sr] - for f in files: - svn_dirs_to_update.add( - os.path.dirname(os.path.join(svn_sr_path, f))) - - # We also need to svn update any parent directories which are not yet present - parent_dirs = set() - for dir in svn_dirs_to_update: - parent_dirs.update(get_all_parent_dirs(dir)) - parent_dirs = set(dir for dir in parent_dirs - if not os.path.exists(os.path.join(svn_repo, dir))) - svn_dirs_to_update.update(parent_dirs) - - # Sort by length to ensure that the parent directories are passed to svn - # before child directories. - sorted_dirs_to_update = sorted(svn_dirs_to_update, key=len) - - # SVN update only in the affected directories. - svn(svn_repo, 'update', '--depth=files', *sorted_dirs_to_update) - - for sr, files in iteritems(subrepo_files): - svn_sr_path = os.path.join(svn_repo, GIT_TO_SVN_DIR[sr]) - if os.name == 'nt': - fix_eol_style_native(rev, svn_sr_path, files) - # We use text=False (and pass '--binary') so that we can get an exact - # diff that can be passed as-is to 'git apply' without any line ending, - # encoding, or other mangling. - diff = git('show', '--binary', rev, '--', - *(os.path.join(sr, f) for f in files), - strip=False, text=False) - # git is the only thing that can handle its own patches... - if sr == '': - prefix_strip = '-p1' - else: - prefix_strip = '-p2' - try: - shell(['git', 'apply', prefix_strip, '-'], cwd=svn_sr_path, - stdin=diff, die_on_failure=False, text=False) - except RuntimeError as e: - eprint("Patch doesn't apply: maybe you should try `git pull -r` " - "first?") - sys.exit(2) - - status_lines = svn(svn_repo, 'status', '--no-ignore').split('\n') - - for l in (l for l in status_lines if (l.startswith('?') or - l.startswith('I'))): - svn(svn_repo, 'add', '--no-ignore', l[1:].strip()) - for l in (l for l in status_lines if l.startswith('!')): - svn(svn_repo, 'remove', l[1:].strip()) - - # Now we're ready to commit. - commit_msg = git('show', '--pretty=%B', '--quiet', rev) - if not dry_run: - log(svn(svn_repo, 'commit', '-m', commit_msg, '--force-interactive')) - log('Committed %s to svn.' % rev) - else: - log("Would have committed %s to svn, if this weren't a dry run." % rev) - - -def cmd_push(args): - '''Push changes back to SVN: this is extracted from Justin Lebar's script - available here: https://github.com/jlebar/llvm-repo-tools/ - - Note: a current limitation is that git does not track file rename, so they - will show up in SVN as delete+add. - ''' - # Get the git root - git_root = git('rev-parse', '--show-toplevel') - if not os.path.isdir(git_root): - die("Can't find git root dir") - - # Push from the root of the git repo - os.chdir(git_root) - - # We need a staging area for SVN, let's hide it in the .git directory. - dot_git_dir = git('rev-parse', '--git-common-dir') - svn_root = os.path.join(dot_git_dir, 'llvm-upstream-svn') - svn_init(svn_root) - - rev_range = args.rev_range - dry_run = args.dry_run - revs = get_revs_to_push(rev_range) - log('Pushing %d commit%s:\n%s' % - (len(revs), 's' if len(revs) != 1 - else '', '\n'.join(' ' + git('show', '--oneline', '--quiet', c) - for c in revs))) - for r in revs: - clean_svn(svn_root) - svn_push_one_rev(svn_root, r, dry_run) - - -if __name__ == '__main__': - if not program_exists('svn'): - die('error: git-llvm needs svn command, but svn is not installed.') - - argv = sys.argv[1:] - p = argparse.ArgumentParser( - prog='git llvm', formatter_class=argparse.RawDescriptionHelpFormatter, - description=__doc__) - subcommands = p.add_subparsers(title='subcommands', - description='valid subcommands', - help='additional help') - verbosity_group = p.add_mutually_exclusive_group() - verbosity_group.add_argument('-q', '--quiet', action='store_true', - help='print less information') - verbosity_group.add_argument('-v', '--verbose', action='store_true', - help='print more information') - - parser_push = subcommands.add_parser( - 'push', description=cmd_push.__doc__, - help='push changes back to the LLVM SVN repository') - parser_push.add_argument( - '-n', - '--dry-run', - dest='dry_run', - action='store_true', - help='Do everything other than commit to svn. Leaves junk in the svn ' - 'repo, so probably will not work well if you try to commit more ' - 'than one rev.') - parser_push.add_argument( - 'rev_range', - metavar='GIT_REVS', - type=str, - nargs='?', - help="revs to push (default: everything not in the branch's " - 'upstream, or not in origin/master if the branch lacks ' - 'an explicit upstream)') - parser_push.set_defaults(func=cmd_push) - args = p.parse_args(argv) - VERBOSE = args.verbose - QUIET = args.quiet - - # Dispatch to the right subcommand - args.func(args) |
