/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: 2019-10-20 15:03:13 UTC
  • mto: This revision was merged to the branch mainline in revision 7407.
  • Revision ID: jelmer@jelmer.uk-20191020150313-q06o6pncwr6ndu3t
Fix send with git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
# Copyright (C) 2005, 2008 Aaron Bentley, 2006 Michael Ellerman
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
 
 
18
from __future__ import absolute_import
 
19
 
 
20
"""Diff and patch functionality"""
 
21
 
 
22
import errno
 
23
import os
 
24
from subprocess import Popen, PIPE
 
25
import sys
 
26
 
 
27
from .errors import NoDiff3, BzrError
 
28
from .textfile import check_text_path
 
29
 
 
30
class PatchFailed(BzrError):
 
31
 
 
32
    _fmt = """Patch application failed"""
 
33
 
 
34
 
 
35
class PatchInvokeError(BzrError):
 
36
 
 
37
    _fmt = """Error invoking patch: %(errstr)s%(stderr)s"""
 
38
    internal_error = False
 
39
 
 
40
    def __init__(self, e, stderr=''):
 
41
        self.exception = e
 
42
        self.errstr = os.strerror(e.errno)
 
43
        self.stderr = '\n' + stderr
 
44
 
 
45
 
 
46
_do_close_fds = True
 
47
if os.name == 'nt':
 
48
    _do_close_fds = False
 
49
 
 
50
 
 
51
def write_to_cmd(args, input=""):
 
52
    """Spawn a process, and wait for the result
 
53
 
 
54
    If the process is killed, an exception is raised
 
55
 
 
56
    :param args: The command line, the first entry should be the program name
 
57
    :param input: [optional] The text to send the process on stdin
 
58
    :return: (stdout, stderr, status)
 
59
    """
 
60
    process = Popen(args, bufsize=len(input), stdin=PIPE, stdout=PIPE,
 
61
                    stderr=PIPE, close_fds=_do_close_fds)
 
62
    stdout, stderr = process.communicate(input)
 
63
    status = process.wait()
 
64
    if status < 0:
 
65
        raise Exception("%s killed by signal %i" % (args[0], -status))
 
66
    return stdout, stderr, status
 
67
 
 
68
 
 
69
def patch(patch_contents, filename, output_filename=None, reverse=False):
 
70
    """Apply a patch to a file, to produce another output file.  This is should
 
71
    be suitable for our limited purposes.
 
72
 
 
73
    :param patch_contents: The contents of the patch to apply
 
74
    :type patch_contents: str
 
75
    :param filename: the name of the file to apply the patch to
 
76
    :type filename: str
 
77
    :param output_filename: The filename to produce.  If None, file is \
 
78
    modified in-place
 
79
    :type output_filename: str or NoneType
 
80
    :param reverse: If true, apply the patch in reverse
 
81
    :type reverse: bool
 
82
    :return: 0 on success, 1 if some hunks failed
 
83
    """
 
84
    args = ["patch", "-f", "-s", "--posix", "--binary"]
 
85
    if reverse:
 
86
        args.append("--reverse")
 
87
    if output_filename is not None:
 
88
        args.extend(("-o", output_filename))
 
89
    args.append(filename)
 
90
    stdout, stderr, status = write_to_cmd(args, patch_contents)
 
91
    return status
 
92
 
 
93
 
 
94
def diff3(out_file, mine_path, older_path, yours_path):
 
95
    def add_label(args, label):
 
96
        args.extend(("-L", label))
 
97
    check_text_path(mine_path)
 
98
    check_text_path(older_path)
 
99
    check_text_path(yours_path)
 
100
    args = ['diff3', "-E", "--merge"]
 
101
    add_label(args, "TREE")
 
102
    add_label(args, "ANCESTOR")
 
103
    add_label(args, "MERGE-SOURCE")
 
104
    args.extend((mine_path, older_path, yours_path))
 
105
    try:
 
106
        output, stderr, status = write_to_cmd(args)
 
107
    except OSError as e:
 
108
        if e.errno == errno.ENOENT:
 
109
            raise NoDiff3
 
110
        else:
 
111
            raise
 
112
    if status not in (0, 1):
 
113
        raise Exception(stderr)
 
114
    with open(out_file, 'wb') as f:
 
115
        f.write(output)
 
116
    return status
 
117
 
 
118
 
 
119
def patch_tree(tree, patches, strip=0, reverse=False, dry_run=False,
 
120
               quiet=False, out=None):
 
121
    """Apply a patch to a tree.
 
122
 
 
123
    Args:
 
124
      tree: A MutableTree object
 
125
      patches: list of patches as bytes
 
126
      strip: Strip X segments of paths
 
127
      reverse: Apply reversal of patch
 
128
      dry_run: Dry run
 
129
    """
 
130
    return run_patch(tree.basedir, patches, strip, reverse, dry_run,
 
131
                     quiet, out=out)
 
132
 
 
133
 
 
134
def run_patch(directory, patches, strip=0, reverse=False, dry_run=False,
 
135
              quiet=False, _patch_cmd='patch', target_file=None, out=None):
 
136
    args = [_patch_cmd, '-d', directory, '-s', '-p%d' % strip, '-f']
 
137
    if quiet:
 
138
        args.append('--quiet')
 
139
 
 
140
    if sys.platform == "win32":
 
141
        args.append('--binary')
 
142
 
 
143
    if reverse:
 
144
        args.append('-R')
 
145
    if dry_run:
 
146
        if sys.platform.startswith('freebsd'):
 
147
            args.append('--check')
 
148
        else:
 
149
            args.append('--dry-run')
 
150
        stderr = PIPE
 
151
    else:
 
152
        stderr = None
 
153
    if target_file is not None:
 
154
        args.append(target_file)
 
155
 
 
156
    try:
 
157
        process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=stderr)
 
158
    except OSError as e:
 
159
        raise PatchInvokeError(e)
 
160
    try:
 
161
        for patch in patches:
 
162
            process.stdin.write(bytes(patch))
 
163
        process.stdin.close()
 
164
 
 
165
    except IOError as e:
 
166
        raise PatchInvokeError(e, process.stderr.read())
 
167
 
 
168
    result = process.wait()
 
169
    if not dry_run:
 
170
        if out is not None:
 
171
            out.write(process.stdout.read())
 
172
        else:
 
173
            process.stdout.read()
 
174
    if result != 0:
 
175
        raise PatchFailed()
 
176
 
 
177
    return result