/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 bzrlib/patch.py

  • Committer: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

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
 
from __future__ import absolute_import
19
 
 
20
 
"""Diff and patch functionality"""
21
17
 
22
18
import errno
23
19
import os
24
20
from subprocess import Popen, PIPE
25
 
import sys
26
 
import tempfile
27
 
 
28
 
from .errors import NoDiff3, BzrError
29
 
from .textfile import check_text_path
30
 
 
31
 
class PatchFailed(BzrError):
32
 
 
33
 
    _fmt = """Patch application failed"""
34
 
 
35
 
 
36
 
class PatchInvokeError(BzrError):
37
 
 
38
 
    _fmt = """Error invoking patch: %(errstr)s%(stderr)s"""
39
 
    internal_error = False
40
 
 
41
 
    def __init__(self, e, stderr=''):
42
 
        self.exception = e
43
 
        self.errstr = os.strerror(e.errno)
44
 
        self.stderr = '\n' + stderr
 
21
 
 
22
from bzrlib.errors import NoDiff3
 
23
from bzrlib.textfile import check_text_path
 
24
 
 
25
"""Diff and patch functionality"""
 
26
 
 
27
__docformat__ = "restructuredtext"
45
28
 
46
29
 
47
30
_do_close_fds = True
63
46
    stdout, stderr = process.communicate(input)
64
47
    status = process.wait()
65
48
    if status < 0:
66
 
        raise Exception("%s killed by signal %i" % (args[0], -status))
 
49
        raise Exception("%s killed by signal %i" (args[0], -status))
67
50
    return stdout, stderr, status
68
51
 
69
52
 
105
88
    args.extend((mine_path, older_path, yours_path))
106
89
    try:
107
90
        output, stderr, status = write_to_cmd(args)
108
 
    except OSError as e:
 
91
    except OSError, e:
109
92
        if e.errno == errno.ENOENT:
110
93
            raise NoDiff3
111
94
        else:
112
95
            raise
113
96
    if status not in (0, 1):
114
97
        raise Exception(stderr)
115
 
    with open(out_file, 'wb') as f:
 
98
    f = open(out_file, 'wb')
 
99
    try:
116
100
        f.write(output)
 
101
    finally:
 
102
        f.close()
117
103
    return status
118
 
 
119
 
 
120
 
def patch_tree(tree, patches, strip=0, reverse=False, dry_run=False,
121
 
               quiet=False, out=None):
122
 
    """Apply a patch to a tree.
123
 
 
124
 
    Args:
125
 
      tree: A MutableTree object
126
 
      patches: list of patches as bytes
127
 
      strip: Strip X segments of paths
128
 
      reverse: Apply reversal of patch
129
 
      dry_run: Dry run
130
 
    """
131
 
    return run_patch(tree.basedir, patches, strip, reverse, dry_run,
132
 
                     quiet, out=out)
133
 
 
134
 
 
135
 
def run_patch(directory, patches, strip=0, reverse=False, dry_run=False,
136
 
              quiet=False, _patch_cmd='patch', target_file=None, out=None):
137
 
    args = [_patch_cmd, '-d', directory, '-s', '-p%d' % strip, '-f']
138
 
    if quiet:
139
 
        args.append('--quiet')
140
 
 
141
 
    if sys.platform == "win32":
142
 
        args.append('--binary')
143
 
 
144
 
    if reverse:
145
 
        args.append('-R')
146
 
    if dry_run:
147
 
        if sys.platform.startswith('freebsd'):
148
 
            args.append('--check')
149
 
        else:
150
 
            args.append('--dry-run')
151
 
        stderr = PIPE
152
 
    else:
153
 
        stderr = None
154
 
    if target_file is not None:
155
 
        args.append(target_file)
156
 
 
157
 
    try:
158
 
        process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=stderr)
159
 
    except OSError as e:
160
 
        raise PatchInvokeError(e)
161
 
    try:
162
 
        for patch in patches:
163
 
            process.stdin.write(bytes(patch))
164
 
        process.stdin.close()
165
 
 
166
 
    except IOError as e:
167
 
        raise PatchInvokeError(e, process.stderr.read())
168
 
 
169
 
    result = process.wait()
170
 
    if not dry_run:
171
 
        if out is not None:
172
 
            out.write(process.stdout.read())
173
 
        else:
174
 
            process.stdout.read()
175
 
    if result != 0:
176
 
        raise PatchFailed()
177
 
 
178
 
    return result
179
 
 
180
 
 
181
 
def iter_patched_from_hunks(orig_lines, hunks):
182
 
    """Iterate through a series of lines with a patch applied.
183
 
    This handles a single file, and does exact, not fuzzy patching.
184
 
 
185
 
    :param orig_lines: The unpatched lines.
186
 
    :param hunks: An iterable of Hunk instances.
187
 
 
188
 
    This is different from breezy.patches in that it invokes the patch
189
 
    command.
190
 
    """
191
 
    with tempfile.NamedTemporaryFile() as f:
192
 
        f.writelines(orig_lines)
193
 
        f.flush()
194
 
        # TODO(jelmer): Stream patch contents to command, rather than
195
 
        # serializing the entire patch upfront.
196
 
        serialized = b''.join([hunk.as_bytes() for hunk in hunks])
197
 
        args = ["patch", "-f", "-s", "--posix", "--binary",
198
 
                "-o", "-", f.name, "-r", "-"]
199
 
        stdout, stderr, status = write_to_cmd(args, serialized)
200
 
    if status == 0:
201
 
        return [stdout]
202
 
    raise PatchFailed(stderr)