2019-11-23 18:15:11 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import os
|
|
|
|
import glob
|
|
|
|
import argparse
|
|
|
|
import subprocess
|
|
|
|
import configparser
|
|
|
|
|
|
|
|
from scripts.common import git
|
|
|
|
from scripts.common import Colors
|
|
|
|
|
|
|
|
|
|
|
|
SCRIPTDIR = os.path.normpath(os.path.dirname(__file__))
|
|
|
|
SUBPROJECTS_DIR = os.path.normpath(os.path.join(SCRIPTDIR, "subprojects"))
|
|
|
|
|
2019-11-23 19:13:37 +00:00
|
|
|
|
2019-11-23 18:15:11 +00:00
|
|
|
def repo_has_branch(repo_dir, branch):
|
2019-11-23 19:13:37 +00:00
|
|
|
if not branch:
|
|
|
|
return False
|
2019-11-23 18:15:11 +00:00
|
|
|
try:
|
|
|
|
git("describe", branch, repository_path=repo_dir)
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def parse_wrapfile(wrapf):
|
|
|
|
cgp = configparser.ConfigParser()
|
|
|
|
cgp.read(wrapf)
|
|
|
|
if 'wrap-git' not in cgp:
|
|
|
|
return None
|
|
|
|
section = cgp['wrap-git']
|
|
|
|
return section['directory'], section['revision']
|
|
|
|
|
|
|
|
def get_wrap_subprojects(srcdir, gst_branch):
|
|
|
|
'''
|
|
|
|
Parses wrap files in the subprojects directory for the specified source
|
|
|
|
tree and gets the revisions for all common repos.
|
|
|
|
'''
|
|
|
|
for wrapf in glob.glob(os.path.join(srcdir, 'subprojects', '*.wrap')):
|
|
|
|
entries = parse_wrapfile(wrapf)
|
|
|
|
if not entries:
|
|
|
|
continue
|
|
|
|
|
|
|
|
repo_name, repo_branch = entries
|
|
|
|
parent_repo_dir = os.path.join(SUBPROJECTS_DIR, repo_name)
|
|
|
|
if not os.path.exists(os.path.join(parent_repo_dir, '.git')):
|
|
|
|
continue
|
|
|
|
# If a branch of the same name exists in the gst subproject, use it
|
|
|
|
if repo_name.startswith('gst') and repo_has_branch(parent_repo_dir, gst_branch):
|
|
|
|
repo_branch = gst_branch
|
|
|
|
|
|
|
|
yield repo_name, repo_branch, parent_repo_dir
|
|
|
|
|
2020-01-31 18:56:05 +00:00
|
|
|
def checkout_worktree(repo_name, repo_dir, worktree_dir, branch, new_branch, force=False):
|
2019-11-23 18:15:11 +00:00
|
|
|
print('Checking out worktree for project {!r} into {!r} '
|
|
|
|
'(branch {})'.format(repo_name, worktree_dir, branch))
|
|
|
|
try:
|
|
|
|
args = ["worktree", "add"]
|
|
|
|
if force:
|
|
|
|
args += ["-f", "-f"]
|
|
|
|
args += [worktree_dir, branch]
|
2020-01-31 18:56:05 +00:00
|
|
|
if new_branch:
|
|
|
|
args += ["-b", new_branch]
|
2019-11-23 18:15:11 +00:00
|
|
|
git(*args, repository_path=repo_dir)
|
2019-11-23 19:13:37 +00:00
|
|
|
except subprocess.CalledProcessError as e:
|
2019-11-23 18:15:11 +00:00
|
|
|
out = getattr(e, "output", b"").decode()
|
|
|
|
print("\nCould not checkout worktree %s, please fix and try again."
|
|
|
|
" Error:\n\n%s %s" % (repo_dir, out, e))
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
commit_message = git("show", "--shortstat", repository_path=repo_dir).split("\n")
|
|
|
|
print(u" -> %s%s%s - %s" % (Colors.HEADER, repo_dir, Colors.ENDC,
|
|
|
|
commit_message[4].strip()))
|
|
|
|
return True
|
|
|
|
|
2020-01-31 18:56:05 +00:00
|
|
|
def checkout_subprojects(worktree_dir, branch, new_branch):
|
2019-11-23 18:15:11 +00:00
|
|
|
worktree_subdir = os.path.join(worktree_dir, "subprojects")
|
|
|
|
|
|
|
|
for repo_name, repo_branch, parent_repo_dir in get_wrap_subprojects(worktree_dir, branch):
|
|
|
|
workdir = os.path.normpath(os.path.join(worktree_subdir, repo_name))
|
2020-01-31 18:56:05 +00:00
|
|
|
if not checkout_worktree(repo_name, parent_repo_dir, workdir, repo_branch, new_branch, force=True):
|
2019-11-23 18:15:11 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
2019-11-23 19:13:37 +00:00
|
|
|
def remove_worktree(worktree_dir):
|
|
|
|
worktree_subdir = os.path.join(worktree_dir, "subprojects")
|
|
|
|
|
|
|
|
for repo_name, _, parent_repo_dir in get_wrap_subprojects(worktree_dir, None):
|
|
|
|
workdir = os.path.normpath(os.path.join(worktree_subdir, repo_name))
|
|
|
|
if not os.path.exists(workdir):
|
|
|
|
continue
|
|
|
|
|
|
|
|
subprojdir = os.path.normpath(os.path.join(SUBPROJECTS_DIR, repo_name))
|
|
|
|
if not os.path.exists(subprojdir):
|
|
|
|
continue
|
|
|
|
|
|
|
|
print('Removing worktree {!r}'.format(workdir))
|
|
|
|
try:
|
|
|
|
git('worktree', 'remove', '-f', workdir, repository_path=subprojdir)
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
out = getattr(e, "output", b"").decode()
|
|
|
|
print('Ignoring error while removing worktree {!r}:\n\n{}'.format(workdir, out))
|
|
|
|
|
|
|
|
try:
|
|
|
|
git('worktree', 'remove', '-f', worktree_dir, repository_path=SCRIPTDIR)
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
print('Failed to remove worktree {!r}'.format(worktree_dir))
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2019-11-23 18:15:11 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
parser = argparse.ArgumentParser(prog="gst-worktree")
|
2019-11-23 19:13:37 +00:00
|
|
|
parser.add_argument("--no-color", default=False, action='store_true',
|
|
|
|
help="Do not output ANSI colors")
|
2019-11-23 18:15:11 +00:00
|
|
|
|
2019-11-23 19:13:37 +00:00
|
|
|
subparsers = parser.add_subparsers(help='The sub-command to run', dest='command')
|
|
|
|
|
|
|
|
parser_add = subparsers.add_parser('add',
|
|
|
|
help='Create a worktree for gst-build and all subprojects')
|
|
|
|
parser_add.add_argument('worktree_dir', type=str,
|
|
|
|
help='Directory where to create the new worktree')
|
|
|
|
parser_add.add_argument('branch', type=str, default=None,
|
|
|
|
help='Branch to checkout')
|
2020-01-31 18:56:05 +00:00
|
|
|
parser_add.add_argument('-b', '--new-branch', type=str, default=None,
|
|
|
|
help='Branch to create')
|
2019-11-23 19:13:37 +00:00
|
|
|
|
|
|
|
parser_rm = subparsers.add_parser('rm',
|
|
|
|
help='Remove a gst-build worktree and the subproject worktrees inside it')
|
|
|
|
parser_rm.add_argument('worktree_dir', type=str,
|
|
|
|
help='Worktree directory to remove')
|
2019-11-23 18:15:11 +00:00
|
|
|
|
|
|
|
options = parser.parse_args()
|
|
|
|
|
|
|
|
if options.no_color or not Colors.can_enable():
|
|
|
|
Colors.disable()
|
|
|
|
|
2019-11-23 19:13:37 +00:00
|
|
|
if not options.command:
|
|
|
|
parser.print_usage()
|
2019-11-23 18:15:11 +00:00
|
|
|
exit(1)
|
2019-11-23 19:13:37 +00:00
|
|
|
|
|
|
|
worktree_dir = os.path.abspath(options.worktree_dir)
|
|
|
|
|
|
|
|
if options.command == 'add':
|
2020-01-31 18:56:05 +00:00
|
|
|
if not checkout_worktree('gst-build', SCRIPTDIR, worktree_dir, options.branch, options.new_branch):
|
2019-11-23 19:13:37 +00:00
|
|
|
exit(1)
|
2020-01-31 18:56:05 +00:00
|
|
|
if not checkout_subprojects(worktree_dir, options.branch, options.new_branch):
|
2019-11-23 19:13:37 +00:00
|
|
|
exit(1)
|
|
|
|
elif options.command == 'rm':
|
|
|
|
if not os.path.exists(worktree_dir):
|
|
|
|
print('Cannot remove worktree directory {!r}, it does not exist'.format(worktree_dir))
|
|
|
|
exit(1)
|
|
|
|
if not remove_worktree(worktree_dir):
|
|
|
|
exit(1)
|
|
|
|
else:
|
|
|
|
# Unreachable code
|
|
|
|
raise AssertionError
|