Blob Blame History Raw
#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Copyright (C) 2018 SUSE LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
# USA.

import argparse
import collections
import re
import shlex

import pygit2_wrapper as pygit2

import git_sort
import lib


proto_match = re.compile("^(git|https?)://")
invalid_match = re.compile("~")
ext = ".git"


def transform(name):
    """
    Transform a remote url into a string that can be used as a git remote name.
    Useful for remotes that do not start with the kernel.org canonical prefix.
    """
    name = proto_match.sub("", name, 1)
    name = invalid_match.sub("_", name)
    if name.endswith(ext):
        name = name[:-1 * len(ext)]

    return name


def sync_remote_list(directory):
    quoted_directory = shlex.quote(directory)
    commands = []

    try:
        repo = pygit2.Repository(directory)
    except pygit2.GitError:
        commands.append("git init %s\n" % (quoted_directory,))
        current_remotes = {}
    else:
        current_remotes = {remote.name : git_sort.RepoURL(remote.url)
                           for remote in repo.remotes}

    commands.append("cd %s\n" % (quoted_directory,))

    new_remotes = collections.OrderedDict(
        ((transform(str(head.repo_url)), head.repo_url,)
         for head in git_sort.remotes))

    # modify existing remotes whose url has changed
    commands.extend(["git remote set-url %s %s\n" % (
        shlex.quote(name), shlex.quote(repr(repo_url)),)
        for name, repo_url in new_remotes.items()
        if name in current_remotes and repo_url != current_remotes[name]
    ])

    # add missing remotes
    current = set(current_remotes)
    new = set(new_remotes)

    mainline = str(git_sort.remotes[0].repo_url)
    def option(name):
        if name == mainline:
            return ""
        else:
            return " --no-tags"

    commands.extend(["git remote add%s %s %s\n" % (
        option(name), shlex.quote(name), shlex.quote(repr(new_remotes[name])),)
        for name in new_remotes
        if name in new - current
    ])

    # remove superfluous remotes
    commands.extend(["git remote remove %s\n" % (
        shlex.quote(name),)
        for name in sorted(current - new)
    ])

    return commands


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Print commands to clone the mainline Linux repository "
        "and add all remotes configured for git-sort. "
        "If the target directory is already a git repository, print commands "
        "to add or remove remotes to match with the remotes from the list "
        "configured for git-sort. "
        "That repository can then be used as an ultimate reference for patch "
        "ordering in series.conf.")
    parser.add_argument("directory", nargs="?", default="linux",
                        help="Directory name to clone into. Default: linux")
    args = parser.parse_args()

    print("".join(sync_remote_list(args.directory)), end="")

    print("git fetch --all")