comparison mercurial/patch.py @ 2873:4ec58b157265

refactor text diff/patch code. rename commands.dodiff to patch.diff. rename commands.doexport to patch.export. move some functions from commands to new mercurial.cmdutil module. turn list of diff options into mdiff.diffopts class. patch.diff and patch.export now has clean api for call from 3rd party python code.
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Sat, 12 Aug 2006 16:13:27 -0700
parents 9a2a481ec3ea
children 3d6efcbbd1c9
comparison
equal deleted inserted replaced
2872:5dd6631c8238 2873:4ec58b157265
5 # This software may be used and distributed according to the terms 5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference. 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 from demandload import demandload 8 from demandload import demandload
9 from i18n import gettext as _ 9 from i18n import gettext as _
10 demandload(globals(), "util") 10 from node import *
11 demandload(globals(), "cStringIO email.Parser os re shutil tempfile") 11 demandload(globals(), "cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser os re shutil sys tempfile")
12 13
13 def extract(ui, fileobj): 14 def extract(ui, fileobj):
14 '''extract patch from data read from fileobj. 15 '''extract patch from data read from fileobj.
15 16
16 patch can be normal patch or contained in email message. 17 patch can be normal patch or contained in email message.
247 248
248 for gp in gitpatches: 249 for gp in gitpatches:
249 files[gp.path] = (gp.op, gp) 250 files[gp.path] = (gp.op, gp)
250 251
251 return files 252 return files
253
254 def diff(repo, node1=None, node2=None, files=None, match=util.always,
255 fp=None, changes=None, opts=None):
256 '''print diff of changes to files between two nodes, or node and
257 working directory.
258
259 if node1 is None, use first dirstate parent instead.
260 if node2 is None, compare node1 with working directory.'''
261
262 if opts is None:
263 opts = mdiff.defaultopts
264 if fp is None:
265 fp = repo.ui
266
267 if not node1:
268 node1 = repo.dirstate.parents()[0]
269 # reading the data for node1 early allows it to play nicely
270 # with repo.changes and the revlog cache.
271 change = repo.changelog.read(node1)
272 mmap = repo.manifest.read(change[0])
273 date1 = util.datestr(change[2])
274
275 if not changes:
276 changes = repo.changes(node1, node2, files, match=match)
277 modified, added, removed, deleted, unknown = changes
278 if files:
279 def filterfiles(filters):
280 l = [x for x in files if x in filters]
281
282 for t in filters:
283 if t and t[-1] != "/":
284 t += "/"
285 l += [x for x in files if x.startswith(t)]
286 return l
287
288 modified, added, removed = map(lambda x: filterfiles(x),
289 (modified, added, removed))
290
291 if not modified and not added and not removed:
292 return
293
294 if node2:
295 change = repo.changelog.read(node2)
296 mmap2 = repo.manifest.read(change[0])
297 _date2 = util.datestr(change[2])
298 def date2(f):
299 return _date2
300 def read(f):
301 return repo.file(f).read(mmap2[f])
302 else:
303 tz = util.makedate()[1]
304 _date2 = util.datestr()
305 def date2(f):
306 try:
307 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
308 except OSError, err:
309 if err.errno != errno.ENOENT: raise
310 return _date2
311 def read(f):
312 return repo.wread(f)
313
314 if repo.ui.quiet:
315 r = None
316 else:
317 hexfunc = repo.ui.verbose and hex or short
318 r = [hexfunc(node) for node in [node1, node2] if node]
319
320 all = modified + added + removed
321 all.sort()
322 for f in all:
323 to = None
324 tn = None
325 if f in mmap:
326 to = repo.file(f).read(mmap[f])
327 if f not in removed:
328 tn = read(f)
329 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts))
330
331 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
332 opts=None):
333 '''export changesets as hg patches.'''
334
335 total = len(revs)
336 revwidth = max(map(len, revs))
337
338 def single(node, seqno, fp):
339 parents = [p for p in repo.changelog.parents(node) if p != nullid]
340 if switch_parent:
341 parents.reverse()
342 prev = (parents and parents[0]) or nullid
343 change = repo.changelog.read(node)
344
345 if not fp:
346 fp = cmdutil.make_file(repo, template, node, total=total,
347 seqno=seqno, revwidth=revwidth)
348 if fp not in (sys.stdout, repo.ui):
349 repo.ui.note("%s\n" % fp.name)
350
351 fp.write("# HG changeset patch\n")
352 fp.write("# User %s\n" % change[1])
353 fp.write("# Date %d %d\n" % change[2])
354 fp.write("# Node ID %s\n" % hex(node))
355 fp.write("# Parent %s\n" % hex(prev))
356 if len(parents) > 1:
357 fp.write("# Parent %s\n" % hex(parents[1]))
358 fp.write(change[4].rstrip())
359 fp.write("\n\n")
360
361 diff(repo, prev, node, fp=fp, opts=opts)
362 if fp not in (sys.stdout, repo.ui):
363 fp.close()
364
365 for seqno, cset in enumerate(revs):
366 single(cset, seqno, fp)