Blame progit/lib/git.py

Pierre-Yves Chibon 33b534
# -*- coding: utf-8 -*-
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
"""
Pierre-Yves Chibon a27356
 (c) 2015 - Copyright Red Hat Inc
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
 Authors:
Pierre-Yves Chibon a27356
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
"""
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
import datetime
Pierre-Yves Chibon a27356
import json
Pierre-Yves Chibon a27356
import os
Pierre-Yves Chibon a27356
import random
Pierre-Yves Chibon a27356
import shutil
Pierre-Yves Chibon a27356
import string
Pierre-Yves Chibon a27356
import tempfile
Pierre-Yves Chibon a27356
import uuid
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
import pygit2
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
import progit.exceptions
Pierre-Yves Chibon f2c72b
import progit.lib.notify
Pierre-Yves Chibon a27356
from progit.lib import model
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
def commit_to_patch(repo_obj, commits):
Pierre-Yves Chibon a27356
    ''' For a given commit (PyGit2 commit object) of a specified git repo,
Pierre-Yves Chibon a27356
    returns a string representation of the changes the commit did in a
Pierre-Yves Chibon a27356
    format that allows it to be used as patch.
Pierre-Yves Chibon a27356
    '''
Pierre-Yves Chibon a27356
    if not isinstance(commits, list):
Pierre-Yves Chibon a27356
        commits = [commits]
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
    patch = ""
Pierre-Yves Chibon a27356
    for cnt, commit in enumerate(commits):
Pierre-Yves Chibon a27356
        if commit.parents:
Pierre-Yves Chibon a27356
            diff = commit.tree.diff_to_tree()
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
            parent = repo_obj.revparse_single('%s^' % commit.oid.hex)
Pierre-Yves Chibon a27356
            diff = repo_obj.diff(parent, commit)
Pierre-Yves Chibon a27356
        else:
Pierre-Yves Chibon a27356
            # First commit in the repo
Pierre-Yves Chibon a27356
            diff = commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
        subject = message = ''
Pierre-Yves Chibon a27356
        if '\n' in commit.message:
Pierre-Yves Chibon a27356
            subject, message = commit.message.split('\n', 1)
Pierre-Yves Chibon a27356
        else:
Pierre-Yves Chibon a27356
            subject = commit.message
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
        if len(commits) > 1:
Pierre-Yves Chibon a27356
            subject = '[PATCH %s/%s] %s' % (cnt + 1, len(commits), subject)
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
        patch += """From %(commit)s Mon Sep 17 00:00:00 2001
Pierre-Yves Chibon a27356
From: %(author_name)s <%(author_email)s>
Pierre-Yves Chibon a27356
Date: %(date)s
Pierre-Yves Chibon a27356
Subject: %(subject)s
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
%(msg)s
Pierre-Yves Chibon a27356
---
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
%(patch)s
Pierre-Yves Chibon a27356
""" % (
Pierre-Yves Chibon a27356
            {
Pierre-Yves Chibon a27356
                'commit': commit.oid.hex,
Pierre-Yves Chibon a27356
                'author_name': commit.author.name,
Pierre-Yves Chibon a27356
                'author_email': commit.author.email,
Pierre-Yves Chibon a27356
                'date': datetime.datetime.utcfromtimestamp(
Pierre-Yves Chibon a27356
                    commit.commit_time).strftime('%b %d %Y %H:%M:%S +0000'),
Pierre-Yves Chibon a27356
                'subject': subject,
Pierre-Yves Chibon a27356
                'msg': message,
Pierre-Yves Chibon a27356
                'patch': diff.patch,
Pierre-Yves Chibon a27356
            }
Pierre-Yves Chibon a27356
        )
Pierre-Yves Chibon a27356
    return patch
Pierre-Yves Chibon 459834
Pierre-Yves Chibon 459834
Pierre-Yves Chibon 40699d
def write_gitolite_acls(session, configfile):
Pierre-Yves Chibon 459834
    ''' Generate the configuration file for gitolite for all projects
Pierre-Yves Chibon 459834
    on the forge.
Pierre-Yves Chibon 459834
    '''
Pierre-Yves Chibon 459834
    config = []
Pierre-Yves Chibon 459834
    for project in session.query(model.Project).all():
Pierre-Yves Chibon 459834
        if project.parent_id:
Pierre-Yves Chibon 459834
            config.append('repo forks/%s' % project.fullname)
Pierre-Yves Chibon 459834
        else:
Pierre-Yves Chibon 459834
            config.append('repo %s' % project.fullname)
Pierre-Yves Chibon 459834
        config.append('  R   = @all')
Pierre-Yves Chibon 459834
        config.append('  RW+ = %s' % project.user.user)
Pierre-Yves Chibon 459834
        for user in project.users:
Pierre-Yves Chibon 459834
            if user != project.user:
Pierre-Yves Chibon 2286f4
                config.append('  RW+ = %s' % user.user)
Pierre-Yves Chibon 459834
        config.append('')
Pierre-Yves Chibon 459834
Pierre-Yves Chibon 459834
        config.append('repo docs/%s' % project.fullname)
Pierre-Yves Chibon 459834
        config.append('  R   = @all')
Pierre-Yves Chibon 459834
        config.append('  RW+ = %s' % project.user.user)
Pierre-Yves Chibon 459834
        for user in project.users:
Pierre-Yves Chibon 459834
            if user != project.user:
Pierre-Yves Chibon 2286f4
                config.append('  RW+ = %s' % user.user)
Pierre-Yves Chibon 459834
        config.append('')
Pierre-Yves Chibon 459834
Pierre-Yves Chibon 459834
        config.append('repo tickets/%s' % project.fullname)
Pierre-Yves Chibon 459834
        config.append('  R   = @all')
Pierre-Yves Chibon 459834
        config.append('  RW+ = %s' % project.user.user)
Pierre-Yves Chibon 459834
        for user in project.users:
Pierre-Yves Chibon 459834
            if user != project.user:
Pierre-Yves Chibon 2286f4
                config.append('  RW+ = %s' % user.user)
Pierre-Yves Chibon 459834
        config.append('')
Pierre-Yves Chibon 459834
Pierre-Yves Chibon 459834
    with open(configfile, 'w') as stream:
Pierre-Yves Chibon 459834
        for row in config:
Pierre-Yves Chibon 459834
            stream.write(row + '\n')
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
def update_git_ticket(issue, repo, ticketfolder):
Pierre-Yves Chibon c8b13b
    """ Update the given issue in its git.
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    This method forks the provided repo, add/edit the issue whose file name
Pierre-Yves Chibon c8b13b
    is defined by the uid field of the issue and if there are additions/
Pierre-Yves Chibon c8b13b
    changes commit them and push them back to the original repo.
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    """
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Get the fork
Pierre-Yves Chibon c8b13b
    repopath = os.path.join(ticketfolder, repo.path)
Pierre-Yves Chibon c8b13b
    ticket_repo = pygit2.Repository(repopath)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Clone the repo into a temp folder
Pierre-Yves Chibon c8b13b
    newpath = tempfile.mkdtemp()
Pierre-Yves Chibon c8b13b
    new_repo = pygit2.clone_repository(repopath, newpath)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    file_path = os.path.join(newpath, issue.uid)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Get the current index
Pierre-Yves Chibon c8b13b
    index = new_repo.index
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Are we adding files
Pierre-Yves Chibon c8b13b
    added = False
Pierre-Yves Chibon c8b13b
    if not os.path.exists(file_path):
Pierre-Yves Chibon c8b13b
        added = True
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Write down what changed
Pierre-Yves Chibon c8b13b
    with open(file_path, 'w') as stream:
Pierre-Yves Chibon c8b13b
        stream.write(issue.to_json())
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Retrieve the list of files that changed
Pierre-Yves Chibon c8b13b
    diff = new_repo.diff()
Pierre-Yves Chibon c8b13b
    files = [patch.new_file_path for patch in diff]
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Add the changes to the index
Pierre-Yves Chibon c8b13b
    if added:
Pierre-Yves Chibon c8b13b
        index.add(issue.uid)
Pierre-Yves Chibon c8b13b
    for filename in files:
Pierre-Yves Chibon c8b13b
        index.add(filename)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # If not change, return
Pierre-Yves Chibon c8b13b
    if not files and not added:
Pierre-Yves Chibon c8b13b
        shutil.rmtree(newpath)
Pierre-Yves Chibon c8b13b
        return
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # See if there is a parent to this commit
Pierre-Yves Chibon c8b13b
    parent = None
Pierre-Yves Chibon c8b13b
    try:
Pierre-Yves Chibon c8b13b
        parent = new_repo.head.get_object().oid
Pierre-Yves Chibon c8b13b
    except pygit2.GitError:
Pierre-Yves Chibon c8b13b
        pass
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    parents = []
Pierre-Yves Chibon c8b13b
    if parent:
Pierre-Yves Chibon c8b13b
        parents.append(parent)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Author/commiter will always be this one
Pierre-Yves Chibon c8b13b
    author = pygit2.Signature(name='progit', email='progit')
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Actually commit
Pierre-Yves Chibon c8b13b
    sha = new_repo.create_commit(
Pierre-Yves Chibon c8b13b
        'refs/heads/master',
Pierre-Yves Chibon c8b13b
        author,
Pierre-Yves Chibon c8b13b
        author,
Pierre-Yves Chibon c8b13b
        'Updated ticket %s: %s' % (issue.uid, issue.title),
Pierre-Yves Chibon c8b13b
        new_repo.index.write_tree(),
Pierre-Yves Chibon c8b13b
        parents)
Pierre-Yves Chibon c8b13b
    index.write()
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Push to origin
Pierre-Yves Chibon c8b13b
    ori_remote = new_repo.remotes[0]
Pierre-Yves Chibon c8b13b
    master_ref = new_repo.lookup_reference('HEAD').resolve()
Pierre-Yves Chibon c8b13b
    refname = '%s:%s' % (master_ref.name, master_ref.name)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    ori_remote.push(refname)
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    # Remove the clone
Pierre-Yves Chibon c8b13b
    shutil.rmtree(newpath)