/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# Copyright (C) 2005, 2006 Canonical Ltd
# Copyright (C) 2005, 2008 Aaron Bentley, 2006 Michael Ellerman
#
# 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

"""Diff and patch functionality"""

import errno
import os
from subprocess import Popen, PIPE
import sys
import tempfile

from .errors import NoDiff3, BzrError
from .textfile import check_text_path

class PatchFailed(BzrError):

    _fmt = """Patch application failed"""


class PatchInvokeError(BzrError):

    _fmt = """Error invoking patch: %(errstr)s%(stderr)s"""
    internal_error = False

    def __init__(self, e, stderr=''):
        self.exception = e
        self.errstr = os.strerror(e.errno)
        self.stderr = '\n' + stderr


_do_close_fds = True
if os.name == 'nt':
    _do_close_fds = False


def write_to_cmd(args, input=""):
    """Spawn a process, and wait for the result

    If the process is killed, an exception is raised

    :param args: The command line, the first entry should be the program name
    :param input: [optional] The text to send the process on stdin
    :return: (stdout, stderr, status)
    """
    process = Popen(args, bufsize=len(input), stdin=PIPE, stdout=PIPE,
                    stderr=PIPE, close_fds=_do_close_fds)
    stdout, stderr = process.communicate(input)
    status = process.wait()
    if status < 0:
        raise Exception("%s killed by signal %i" % (args[0], -status))
    return stdout, stderr, status


def patch(patch_contents, filename, output_filename=None, reverse=False):
    """Apply a patch to a file, to produce another output file.  This is should
    be suitable for our limited purposes.

    :param patch_contents: The contents of the patch to apply
    :type patch_contents: str
    :param filename: the name of the file to apply the patch to
    :type filename: str
    :param output_filename: The filename to produce.  If None, file is \
    modified in-place
    :type output_filename: str or NoneType
    :param reverse: If true, apply the patch in reverse
    :type reverse: bool
    :return: 0 on success, 1 if some hunks failed
    """
    args = ["patch", "-f", "-s", "--posix", "--binary"]
    if reverse:
        args.append("--reverse")
    if output_filename is not None:
        args.extend(("-o", output_filename))
    args.append(filename)
    stdout, stderr, status = write_to_cmd(args, patch_contents)
    return status


def diff3(out_file, mine_path, older_path, yours_path):
    def add_label(args, label):
        args.extend(("-L", label))
    check_text_path(mine_path)
    check_text_path(older_path)
    check_text_path(yours_path)
    args = ['diff3', "-E", "--merge"]
    add_label(args, "TREE")
    add_label(args, "ANCESTOR")
    add_label(args, "MERGE-SOURCE")
    args.extend((mine_path, older_path, yours_path))
    try:
        output, stderr, status = write_to_cmd(args)
    except OSError as e:
        if e.errno == errno.ENOENT:
            raise NoDiff3
        else:
            raise
    if status not in (0, 1):
        raise Exception(stderr)
    with open(out_file, 'wb') as f:
        f.write(output)
    return status


def patch_tree(tree, patches, strip=0, reverse=False, dry_run=False,
               quiet=False, out=None):
    """Apply a patch to a tree.

    Args:
      tree: A MutableTree object
      patches: list of patches as bytes
      strip: Strip X segments of paths
      reverse: Apply reversal of patch
      dry_run: Dry run
    """
    return run_patch(tree.basedir, patches, strip, reverse, dry_run,
                     quiet, out=out)


def run_patch(directory, patches, strip=0, reverse=False, dry_run=False,
              quiet=False, _patch_cmd='patch', target_file=None, out=None):
    args = [_patch_cmd, '-d', directory, '-s', '-p%d' % strip, '-f']
    if quiet:
        args.append('--quiet')

    if sys.platform == "win32":
        args.append('--binary')

    if reverse:
        args.append('-R')
    if dry_run:
        if sys.platform.startswith('freebsd'):
            args.append('--check')
        else:
            args.append('--dry-run')
        stderr = PIPE
    else:
        stderr = None
    if target_file is not None:
        args.append(target_file)

    try:
        process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=stderr)
    except OSError as e:
        raise PatchInvokeError(e)
    try:
        for patch in patches:
            process.stdin.write(bytes(patch))
        process.stdin.close()

    except IOError as e:
        raise PatchInvokeError(e, process.stderr.read())

    result = process.wait()
    if not dry_run:
        if out is not None:
            out.write(process.stdout.read())
        else:
            process.stdout.read()
    if result != 0:
        raise PatchFailed()

    return result


def iter_patched_from_hunks(orig_lines, hunks):
    """Iterate through a series of lines with a patch applied.
    This handles a single file, and does exact, not fuzzy patching.

    :param orig_lines: The unpatched lines.
    :param hunks: An iterable of Hunk instances.

    This is different from breezy.patches in that it invokes the patch
    command.
    """
    with tempfile.NamedTemporaryFile() as f:
        f.writelines(orig_lines)
        f.flush()
        # TODO(jelmer): Stream patch contents to command, rather than
        # serializing the entire patch upfront.
        serialized = b''.join([hunk.as_bytes() for hunk in hunks])
        args = ["patch", "-f", "-s", "--posix", "--binary",
                "-o", "-", f.name, "-r", "-"]
        stdout, stderr, status = write_to_cmd(args, serialized)
    if status == 0:
        return [stdout]
    raise PatchFailed(stderr)