示例#1
0
def generate():
    '''
    Generate patch for specific branch.
    '''
    repo = Repo('.')

    if repo.is_dirty:
        abort('Working tree is dirty. Working tree must be clean to perform this operation.')

    if confirm('Use master as base?'):
        for branch in repo.branches:
            if branch.name == 'master':
                master = branch
    else:
        branch, master = prompt_branch_select(repo, 'Select base branch')

    name, selected_branch = prompt_branch_select(repo)

    while exists(path(name)):
        name = prompt('Directory already exists: %s.\nWhat should I call this patch?'%path(name))

    os.mkdir(path(name))

    rawpath = join(path(name), 'raw.diff')
    local('git diff --binary %s %s > %s'%(selected_branch.commit, master.commit, rawpath))

    with open(rawpath, 'r') as raw:
        changed = raw.read()

    config = RawConfigParser()
    config.add_section('target')
    config.add_section('iteration')
    config.set('target', 'branch', master.name)
    config.set('target', 'commit', master.commit)
    config.set('iteration', 'branch', selected_branch.name)
    config.set('iteration', 'commit', selected_branch.commit)

    print("Generated %s patch directory."%name)

    diffs = Diff.list_from_string(repo, changed)

    def filter(diff):
        def match(pattern):
            if fnmatch.fnmatch(diff.b_path, pattern):
                return pattern
        matches = [match for match in map(match, patterns) if not match is None]
        if not bool(matches):
            return diff

    ignore = os.path.join(os.getcwd(), '.diffignore')
    if os.path.exists(ignore):
        patterns = open(ignore, 'r').read().split('\n')
        diffs = [diff for diff in map(filter, diffs) if not diff is None]

    binary_ext = ['.png', '.gif', '.jpg', '.jpeg', '.flv', '.swf','.zip', '.gz', '.rar', '.fla']

    # TODO: change this to use git generated binary marker
    binaries = [diff for diff in diffs if splitext(diff.b_path)[1] in binary_ext]
    text = set(diffs) - set(binaries)

    config.add_section('patch')
    config.set('patch', 'binary', int(len(binaries) > 0))
    config.set('patch', 'text', int(len(text) > 0))

    print 'Generated patch config file'

    if text:
        text_patch = join(path(name), 'text.patch')
        with open(text_patch, 'w') as pf:
            for diff in text:
                pf.write('%s\n\n'%diff.diff)
        print 'Generated %s text patch'%name
    else:
        text_patch = None

    if binaries:
        binary_list = join(path(name), 'binary.changes')
        binary_base = join(path(name), 'binaries')
        os.mkdir(binary_base)
        with open(binary_list, 'w') as pf:
            for binary in binaries:
                def mkdirs(path):
                    path = join(binary_base, path)
                    try:
                        os.makedirs(path)
                    except:
                        pass
                    return path
                def extractfile(commit, path):
                    dirs, file = os.path.split(path)
                    dirs = mkdirs('%s/%s'%(commit, dirs))
                    with settings(show('stdout'), warn_only = True):
                        if commit == 'target':
                            commit_id = master.commit
                        else:
                            commit_id = selected_branch.commit
                        get_file(commit_id, path, join(binary_base, commit, path))
                if binary.a_commit is None:
                    # new file is being created
                    extractfile('iteration', binary.b_path)
                elif binary.b_commit is None:
                    # file is being removed
                    extractfile('target', binary.a_path)
                else:
                    # file is being updated or renamed
                    extractfile('target', binary.a_path)
                    extractfile('iteration', binary.b_path)

                if binary.new_file:
                    pf.write('new %s\n'%binary.b_path)
                elif binary.deleted_file:
                    pf.write('rm %s\n'%binary.b_path)
                elif binary.renamed:
                    pf.write('mv %s %s\n'%(binary.a_path, binary.b_path))
                else:
                    pf.write('up %s\n'%binary.b_path)
                    
        print 'Generated %s'%binary_list
    else:
        binary_list = None

    with open(join(path(name), 'patch.cfg'), 'wb') as configfile:
        config.write(configfile)

    os.mkdir(join(path(name), 'db'))
    source = join(path(name), 'db', 'source.sql')
    target = join(path(name), 'db', 'target.sql')
    get_file(selected_branch.commit, join('db', 'last'), source)
    get_file(master.commit, join('db', 'last'), target)
    with open(join(path(name), 'db', 'source.sql'), 'r') as sfp:
        with open(join(path(name), 'db', 'target.sql'), 'r') as tfp:
            dbchanged = md5sum(sfp) != md5sum(tfp)
    if dbchanged and confirm('Generate database patch?'):
        dbpatch.generate(join(path(name), 'db'), source, target)
    else:
        shutil.rmtree(join(path(name), 'db'), ignore_errors=True)
        config.set('patch', 'schema', 0)
        config.set('patch', 'data', 0)

    with open(join(path(name), 'patch.cfg'), 'wb') as configfile:
        config.write(configfile)

    return name, path(name)