bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 1
by mbp at sourcefrog import from baz patch-364 | 1 | #! /usr/bin/env python
 | 
| 2 | # -*- coding: UTF-8 -*-
 | |
| 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| 17 | ||
| 1005
by Martin Pool - split TreeDelta and compare_trees out into new module bzrlib.delta | 18 | from bzrlib.trace import mutter | 
| 19 | from bzrlib.errors import BzrError | |
| 20 | from bzrlib.delta import compare_trees | |
| 1
by mbp at sourcefrog import from baz patch-364 | 21 | |
| 767
by Martin Pool - files are only reported as modified if their name or parent has changed, | 22 | # TODO: Rather than building a changeset object, we should probably
 | 
| 23 | # invoke callbacks on an object.  That object can either accumulate a
 | |
| 24 | # list, write them out directly, etc etc.
 | |
| 25 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 26 | def internal_diff(old_label, oldlines, new_label, newlines, to_file): | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 27 | import difflib | 
| 28 | ||
| 29 |     # FIXME: difflib is wrong if there is no trailing newline.
 | |
| 30 |     # The syntax used by patch seems to be "\ No newline at
 | |
| 31 |     # end of file" following the last diff line from that
 | |
| 32 |     # file.  This is not trivial to insert into the
 | |
| 33 |     # unified_diff output and it might be better to just fix
 | |
| 34 |     # or replace that function.
 | |
| 35 | ||
| 36 |     # In the meantime we at least make sure the patch isn't
 | |
| 37 |     # mangled.
 | |
| 38 | ||
| 39 | ||
| 40 |     # Special workaround for Python2.3, where difflib fails if
 | |
| 41 |     # both sequences are empty.
 | |
| 42 | if not oldlines and not newlines: | |
| 43 |         return
 | |
| 44 | ||
| 45 | nonl = False | |
| 46 | ||
| 47 | if oldlines and (oldlines[-1][-1] != '\n'): | |
| 48 | oldlines[-1] += '\n' | |
| 49 | nonl = True | |
| 50 | if newlines and (newlines[-1][-1] != '\n'): | |
| 51 | newlines[-1] += '\n' | |
| 52 | nonl = True | |
| 53 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 54 | ud = difflib.unified_diff(oldlines, newlines, | 
| 55 | fromfile=old_label, tofile=new_label) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 56 | |
| 57 |     # work-around for difflib being too smart for its own good
 | |
| 58 |     # if /dev/null is "1,0", patch won't recognize it as /dev/null
 | |
| 59 | if not oldlines: | |
| 60 | ud = list(ud) | |
| 61 | ud[2] = ud[2].replace('-1,0', '-0,0') | |
| 62 | elif not newlines: | |
| 63 | ud = list(ud) | |
| 64 | ud[2] = ud[2].replace('+1,0', '+0,0') | |
| 65 | ||
| 804
by Martin Pool Patch from John: | 66 | for line in ud: | 
| 67 | to_file.write(line) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 68 | if nonl: | 
| 69 | print >>to_file, "\\ No newline at end of file" | |
| 70 | print >>to_file | |
| 71 | ||
| 72 | ||
| 550
by Martin Pool - Refactor diff code into one that works purely on | 73 | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 74 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 75 | def external_diff(old_label, oldlines, new_label, newlines, to_file, | 
| 76 | diff_opts): | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 77 | """Display a diff by calling out to the external diff program.""" | 
| 78 | import sys | |
| 79 | ||
| 80 | if to_file != sys.stdout: | |
| 81 | raise NotImplementedError("sorry, can't send external diff other than to stdout yet", | |
| 82 | to_file) | |
| 83 | ||
| 581
by Martin Pool - make sure any bzr output is flushed before | 84 |     # make sure our own output is properly ordered before the diff
 | 
| 85 | to_file.flush() | |
| 86 | ||
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 87 | from tempfile import NamedTemporaryFile | 
| 571
by Martin Pool - new --diff-options to pass options through to external | 88 | import os | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 89 | |
| 90 | oldtmpf = NamedTemporaryFile() | |
| 91 | newtmpf = NamedTemporaryFile() | |
| 92 | ||
| 93 | try: | |
| 94 |         # TODO: perhaps a special case for comparing to or from the empty
 | |
| 95 |         # sequence; can just use /dev/null on Unix
 | |
| 96 | ||
| 97 |         # TODO: if either of the files being compared already exists as a
 | |
| 98 |         # regular named file (e.g. in the working directory) then we can
 | |
| 99 |         # compare directly to that, rather than copying it.
 | |
| 100 | ||
| 101 | oldtmpf.writelines(oldlines) | |
| 102 | newtmpf.writelines(newlines) | |
| 103 | ||
| 104 | oldtmpf.flush() | |
| 105 | newtmpf.flush() | |
| 106 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 107 | if not diff_opts: | 
| 108 | diff_opts = [] | |
| 109 | diffcmd = ['diff', | |
| 110 | '--label', old_label, | |
| 111 | oldtmpf.name, | |
| 112 | '--label', new_label, | |
| 113 | newtmpf.name] | |
| 114 | ||
| 115 |         # diff only allows one style to be specified; they don't override.
 | |
| 116 |         # note that some of these take optargs, and the optargs can be
 | |
| 117 |         # directly appended to the options.
 | |
| 118 |         # this is only an approximate parser; it doesn't properly understand
 | |
| 119 |         # the grammar.
 | |
| 120 | for s in ['-c', '-u', '-C', '-U', | |
| 121 | '-e', '--ed', | |
| 122 | '-q', '--brief', | |
| 123 | '--normal', | |
| 124 | '-n', '--rcs', | |
| 125 | '-y', '--side-by-side', | |
| 126 | '-D', '--ifdef']: | |
| 127 | for j in diff_opts: | |
| 128 | if j.startswith(s): | |
| 129 |                     break
 | |
| 130 | else: | |
| 131 |                 continue
 | |
| 132 |             break
 | |
| 133 | else: | |
| 134 | diffcmd.append('-u') | |
| 135 | ||
| 136 | if diff_opts: | |
| 137 | diffcmd.extend(diff_opts) | |
| 138 | ||
| 139 | rc = os.spawnvp(os.P_WAIT, 'diff', diffcmd) | |
| 140 | ||
| 141 | if rc != 0 and rc != 1: | |
| 142 |             # returns 1 if files differ; that's OK
 | |
| 143 | if rc < 0: | |
| 144 | msg = 'signal %d' % (-rc) | |
| 145 | else: | |
| 146 | msg = 'exit code %d' % rc | |
| 147 | ||
| 148 | raise BzrError('external diff failed with %s; command: %r' % (rc, diffcmd)) | |
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 149 | finally: | 
| 150 | oldtmpf.close() # and delete | |
| 151 | newtmpf.close() | |
| 152 | ||
| 153 | ||
| 154 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 155 | def show_diff(b, revision, specific_files, external_diff_options=None): | 
| 619
by Martin Pool doc | 156 | """Shortcut for showing the diff to the working tree. | 
| 157 | ||
| 158 |     b
 | |
| 159 |         Branch.
 | |
| 160 | ||
| 161 |     revision
 | |
| 162 |         None for each, or otherwise the old revision to compare against.
 | |
| 163 |     
 | |
| 164 |     The more general form is show_diff_trees(), where the caller
 | |
| 165 |     supplies any two trees.
 | |
| 166 |     """
 | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 167 | import sys | 
| 168 | ||
| 329
by Martin Pool - refactor command functions into command classes | 169 | if revision == None: | 
| 170 | old_tree = b.basis_tree() | |
| 171 | else: | |
| 172 | old_tree = b.revision_tree(b.lookup_revision(revision)) | |
| 173 | ||
| 174 | new_tree = b.working_tree() | |
| 175 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 176 | show_diff_trees(old_tree, new_tree, sys.stdout, specific_files, | 
| 177 | external_diff_options) | |
| 178 | ||
| 179 | ||
| 180 | ||
| 181 | def show_diff_trees(old_tree, new_tree, to_file, specific_files=None, | |
| 182 | external_diff_options=None): | |
| 550
by Martin Pool - Refactor diff code into one that works purely on | 183 | """Show in text form the changes from one tree to another. | 
| 184 | ||
| 185 |     to_files
 | |
| 186 |         If set, include only changes to these files.
 | |
| 571
by Martin Pool - new --diff-options to pass options through to external | 187 | |
| 188 |     external_diff_options
 | |
| 189 |         If set, use an external GNU diff and pass these options.
 | |
| 550
by Martin Pool - Refactor diff code into one that works purely on | 190 |     """
 | 
| 191 | ||
| 329
by Martin Pool - refactor command functions into command classes | 192 |     # TODO: Options to control putting on a prefix or suffix, perhaps as a format string
 | 
| 193 | old_label = '' | |
| 194 | new_label = '' | |
| 195 | ||
| 196 | DEVNULL = '/dev/null' | |
| 197 |     # Windows users, don't panic about this filename -- it is a
 | |
| 198 |     # special signal to GNU patch that the file should be created or
 | |
| 199 |     # deleted respectively.
 | |
| 200 | ||
| 201 |     # TODO: Generation of pseudo-diffs for added/deleted files could
 | |
| 202 |     # be usefully made into a much faster special case.
 | |
| 203 | ||
| 571
by Martin Pool - new --diff-options to pass options through to external | 204 | if external_diff_options: | 
| 205 | assert isinstance(external_diff_options, basestring) | |
| 206 | opts = external_diff_options.split() | |
| 207 | def diff_file(olab, olines, nlab, nlines, to_file): | |
| 208 | external_diff(olab, olines, nlab, nlines, to_file, opts) | |
| 209 | else: | |
| 210 | diff_file = internal_diff | |
| 211 | ||
| 212 | ||
| 478
by Martin Pool - put back support for running diff or status on | 213 | delta = compare_trees(old_tree, new_tree, want_unchanged=False, | 
| 483
by Martin Pool - change 'file_list' to more explanatory 'specific_files' | 214 | specific_files=specific_files) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 215 | |
| 216 | for path, file_id, kind in delta.removed: | |
| 643
by Martin Pool - fix redirection of messages to file in diff | 217 | print >>to_file, '*** removed %s %r' % (kind, path) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 218 | if kind == 'file': | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 219 | diff_file(old_label + path, | 
| 220 | old_tree.get_file(file_id).readlines(), | |
| 221 | DEVNULL, | |
| 222 |                       [],
 | |
| 223 | to_file) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 224 | |
| 225 | for path, file_id, kind in delta.added: | |
| 643
by Martin Pool - fix redirection of messages to file in diff | 226 | print >>to_file, '*** added %s %r' % (kind, path) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 227 | if kind == 'file': | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 228 | diff_file(DEVNULL, | 
| 229 |                       [],
 | |
| 230 | new_label + path, | |
| 231 | new_tree.get_file(file_id).readlines(), | |
| 232 | to_file) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 233 | |
| 234 | for old_path, new_path, file_id, kind, text_modified in delta.renamed: | |
| 643
by Martin Pool - fix redirection of messages to file in diff | 235 | print >>to_file, '*** renamed %s %r => %r' % (kind, old_path, new_path) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 236 | if text_modified: | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 237 | diff_file(old_label + old_path, | 
| 238 | old_tree.get_file(file_id).readlines(), | |
| 239 | new_label + new_path, | |
| 240 | new_tree.get_file(file_id).readlines(), | |
| 241 | to_file) | |
| 475
by Martin Pool - rewrite diff using compare_trees() | 242 | |
| 243 | for path, file_id, kind in delta.modified: | |
| 643
by Martin Pool - fix redirection of messages to file in diff | 244 | print >>to_file, '*** modified %s %r' % (kind, path) | 
| 475
by Martin Pool - rewrite diff using compare_trees() | 245 | if kind == 'file': | 
| 568
by Martin Pool - start adding support for showing diffs by calling out to | 246 | diff_file(old_label + path, | 
| 247 | old_tree.get_file(file_id).readlines(), | |
| 248 | new_label + path, | |
| 249 | new_tree.get_file(file_id).readlines(), | |
| 250 | to_file) | |
| 329
by Martin Pool - refactor command functions into command classes | 251 | |
| 252 | ||
| 379
by Martin Pool - Simpler compare_inventories() to possibly replace diff_trees | 253 | |
| 1005
by Martin Pool - split TreeDelta and compare_trees out into new module bzrlib.delta | 254 | |
| 255 |