/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: Robert Collins
  • Date: 2007-04-19 02:27:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2426.
  • Revision ID: robertc@robertcollins.net-20070419022744-pfdqz42kp1wizh43
``make docs`` now creates a man page at ``man1/bzr.1`` fixing bug 107388.
(Robert Collins)

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
13
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
16
 
18
 
"""Diff and patch functionality"""
19
17
 
20
18
import errno
21
19
import os
22
20
from subprocess import Popen, PIPE
23
 
import sys
24
 
import tempfile
25
 
 
26
 
from .errors import NoDiff3, BzrError
27
 
from .textfile import check_text_path
28
 
 
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
 
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"
43
28
 
44
29
 
45
30
_do_close_fds = True
61
46
    stdout, stderr = process.communicate(input)
62
47
    status = process.wait()
63
48
    if status < 0:
64
 
        raise Exception("%s killed by signal %i" % (args[0], -status))
 
49
        raise Exception("%s killed by signal %i" (args[0], -status))
65
50
    return stdout, stderr, status
66
 
 
 
51
    
67
52
 
68
53
def patch(patch_contents, filename, output_filename=None, reverse=False):
69
54
    """Apply a patch to a file, to produce another output file.  This is should
87
72
        args.extend(("-o", output_filename))
88
73
    args.append(filename)
89
74
    stdout, stderr, status = write_to_cmd(args, patch_contents)
90
 
    return status
 
75
    return status 
91
76
 
92
77
 
93
78
def diff3(out_file, mine_path, older_path, yours_path):
103
88
    args.extend((mine_path, older_path, yours_path))
104
89
    try:
105
90
        output, stderr, status = write_to_cmd(args)
106
 
    except OSError as e:
 
91
    except OSError, e:
107
92
        if e.errno == errno.ENOENT:
108
93
            raise NoDiff3
109
94
        else:
110
95
            raise
111
96
    if status not in (0, 1):
112
97
        raise Exception(stderr)
113
 
    with open(out_file, 'wb') as f:
 
98
    f = open(out_file, 'wb')
 
99
    try:
114
100
        f.write(output)
 
101
    finally:
 
102
        f.close()
115
103
    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)