/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/patch.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
# Copyright (C) 2005, 2008 Aaron Bentley, 2006 Michael Ellerman
3
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
15
14
# along with this program; if not, write to the Free Software
16
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
16
 
18
 
"""Diff and patch functionality"""
 
17
from __future__ import absolute_import
19
18
 
20
19
import errno
21
20
import os
22
21
from subprocess import Popen, PIPE
23
 
import sys
24
 
import tempfile
25
22
 
26
 
from .errors import NoDiff3, BzrError
 
23
from .errors import NoDiff3
27
24
from .textfile import check_text_path
28
25
 
29
 
class PatchFailed(BzrError):
30
 
 
31
 
    _fmt = """Patch application failed"""
32
 
 
33
 
 
34
 
class PatchInvokeError(BzrError):
35
 
 
36
 
    _fmt = """Error invoking patch: %(errstr)s%(stderr)s"""
37
 
    internal_error = False
38
 
 
39
 
    def __init__(self, e, stderr=''):
40
 
        self.exception = e
41
 
        self.errstr = os.strerror(e.errno)
42
 
        self.stderr = '\n' + stderr
 
26
"""Diff and patch functionality"""
 
27
 
 
28
__docformat__ = "restructuredtext"
43
29
 
44
30
 
45
31
_do_close_fds = True
61
47
    stdout, stderr = process.communicate(input)
62
48
    status = process.wait()
63
49
    if status < 0:
64
 
        raise Exception("%s killed by signal %i" % (args[0], -status))
 
50
        raise Exception("%s killed by signal %i" (args[0], -status))
65
51
    return stdout, stderr, status
66
52
 
67
53
 
110
96
            raise
111
97
    if status not in (0, 1):
112
98
        raise Exception(stderr)
113
 
    with open(out_file, 'wb') as f:
 
99
    f = open(out_file, 'wb')
 
100
    try:
114
101
        f.write(output)
 
102
    finally:
 
103
        f.close()
115
104
    return status
116
 
 
117
 
 
118
 
def patch_tree(tree, patches, strip=0, reverse=False, dry_run=False,
119
 
               quiet=False, out=None):
120
 
    """Apply a patch to a tree.
121
 
 
122
 
    Args:
123
 
      tree: A MutableTree object
124
 
      patches: list of patches as bytes
125
 
      strip: Strip X segments of paths
126
 
      reverse: Apply reversal of patch
127
 
      dry_run: Dry run
128
 
    """
129
 
    return run_patch(tree.basedir, patches, strip, reverse, dry_run,
130
 
                     quiet, out=out)
131
 
 
132
 
 
133
 
def run_patch(directory, patches, strip=0, reverse=False, dry_run=False,
134
 
              quiet=False, _patch_cmd='patch', target_file=None, out=None):
135
 
    args = [_patch_cmd, '-d', directory, '-s', '-p%d' % strip, '-f']
136
 
    if quiet:
137
 
        args.append('--quiet')
138
 
 
139
 
    if sys.platform == "win32":
140
 
        args.append('--binary')
141
 
 
142
 
    if reverse:
143
 
        args.append('-R')
144
 
    if dry_run:
145
 
        if sys.platform.startswith('freebsd'):
146
 
            args.append('--check')
147
 
        else:
148
 
            args.append('--dry-run')
149
 
        stderr = PIPE
150
 
    else:
151
 
        stderr = None
152
 
    if target_file is not None:
153
 
        args.append(target_file)
154
 
 
155
 
    try:
156
 
        process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=stderr)
157
 
    except OSError as e:
158
 
        raise PatchInvokeError(e)
159
 
    try:
160
 
        for patch in patches:
161
 
            process.stdin.write(bytes(patch))
162
 
        process.stdin.close()
163
 
 
164
 
    except IOError as e:
165
 
        raise PatchInvokeError(e, process.stderr.read())
166
 
 
167
 
    result = process.wait()
168
 
    if not dry_run:
169
 
        if out is not None:
170
 
            out.write(process.stdout.read())
171
 
        else:
172
 
            process.stdout.read()
173
 
    if result != 0:
174
 
        raise PatchFailed()
175
 
 
176
 
    return result
177
 
 
178
 
 
179
 
def iter_patched_from_hunks(orig_lines, hunks):
180
 
    """Iterate through a series of lines with a patch applied.
181
 
    This handles a single file, and does exact, not fuzzy patching.
182
 
 
183
 
    :param orig_lines: The unpatched lines.
184
 
    :param hunks: An iterable of Hunk instances.
185
 
 
186
 
    This is different from breezy.patches in that it invokes the patch
187
 
    command.
188
 
    """
189
 
    with tempfile.NamedTemporaryFile() as f:
190
 
        f.writelines(orig_lines)
191
 
        f.flush()
192
 
        # TODO(jelmer): Stream patch contents to command, rather than
193
 
        # serializing the entire patch upfront.
194
 
        serialized = b''.join([hunk.as_bytes() for hunk in hunks])
195
 
        args = ["patch", "-f", "-s", "--posix", "--binary",
196
 
                "-o", "-", f.name, "-r", "-"]
197
 
        stdout, stderr, status = write_to_cmd(args, serialized)
198
 
    if status == 0:
199
 
        return [stdout]
200
 
    raise PatchFailed(stderr)