##// END OF EJS Templates
merge.
Vadim Gelfer -
r2952:6ba3409f merge default
parent child Browse files
Show More
@@ -0,0 +1,45 b''
1 #!/bin/sh
2 # http://www.selenic.com/mercurial/bts/issue322
3
4 echo % file replaced with directory
5
6 hg init a
7 cd a
8 echo a > a
9 hg commit -Ama
10 rm a
11 mkdir a
12 echo a > a/a
13
14 echo % should fail - would corrupt dirstate
15 hg add a/a
16
17 echo % should fail - if add succeeded, would corrupt manifest
18 hg commit -mb
19
20 echo % should fail if commit succeeded - manifest is corrupt
21 hg verify
22
23 cd ..
24 echo % should succeed, but manifest is corrupt
25 hg --debug --traceback clone a b
26
27 echo % directory replaced with file
28
29 hg init c
30 cd c
31 mkdir a
32 echo a > a/a
33 hg commit -Ama
34
35 rm -rf a
36 echo a > a
37
38 echo % should fail - would corrupt dirstate
39 hg add a
40
41 echo % should fail - if add succeeded, would corrupt manifest
42 hg commit -mb a
43
44 echo % should fail if commit succeeded - manifest is corrupt
45 hg verify
@@ -4,6 +4,7 b' Goffredo Baroncelli <kreijack at libero.'
4 4 Muli Ben-Yehuda <mulix at mulix.org>
5 5 Mikael Berthe <mikael at lilotux.net>
6 6 Benoit Boissinot <bboissin at gmail.com>
7 Brendan Cully <brendan at kublai.com>
7 8 Vincent Danjean <vdanjean.ml at free.fr>
8 9 Jake Edge <jake at edge2.net>
9 10 Michael Fetterman <michael.fetterman at intel.com>
@@ -252,6 +252,9 b' class queue:'
252 252
253 253 for line in file(pf):
254 254 line = line.rstrip()
255 if line.startswith('diff --git'):
256 diffstart = 2
257 break
255 258 if diffstart:
256 259 if line.startswith('+++ '):
257 260 diffstart = 2
@@ -298,8 +301,10 b' class queue:'
298 301 return (message, comments, user, date, diffstart > 1)
299 302
300 303 def printdiff(self, repo, node1, node2=None, files=None,
301 fp=None, changes=None, opts=None):
302 patch.diff(repo, node1, node2, files,
304 fp=None, changes=None, opts={}):
305 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
306
307 patch.diff(repo, node1, node2, fns, match=matchfn,
303 308 fp=fp, changes=changes, opts=self.diffopts())
304 309
305 310 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
@@ -408,7 +413,7 b' class queue:'
408 413 self.ui.warn("patch failed, unable to continue (try -v)\n")
409 414 return (False, [], False)
410 415
411 return (True, files.keys(), fuzz)
416 return (True, files, fuzz)
412 417
413 418 def apply(self, repo, series, list=False, update_status=True,
414 419 strict=False, patchdir=None, merge=None, wlock=None):
@@ -421,42 +426,37 b' class queue:'
421 426 lock = repo.lock()
422 427 tr = repo.transaction()
423 428 n = None
424 for patch in series:
425 pushable, reason = self.pushable(patch)
429 for patchname in series:
430 pushable, reason = self.pushable(patchname)
426 431 if not pushable:
427 self.explain_pushable(patch, all_patches=True)
432 self.explain_pushable(patchname, all_patches=True)
428 433 continue
429 self.ui.warn("applying %s\n" % patch)
430 pf = os.path.join(patchdir, patch)
434 self.ui.warn("applying %s\n" % patchname)
435 pf = os.path.join(patchdir, patchname)
431 436
432 437 try:
433 message, comments, user, date, patchfound = self.readheaders(patch)
438 message, comments, user, date, patchfound = self.readheaders(patchname)
434 439 except:
435 self.ui.warn("Unable to read %s\n" % pf)
440 self.ui.warn("Unable to read %s\n" % patchname)
436 441 err = 1
437 442 break
438 443
439 444 if not message:
440 message = "imported patch %s\n" % patch
445 message = "imported patch %s\n" % patchname
441 446 else:
442 447 if list:
443 message.append("\nimported patch %s" % patch)
448 message.append("\nimported patch %s" % patchname)
444 449 message = '\n'.join(message)
445 450
446 451 (patcherr, files, fuzz) = self.patch(repo, pf)
447 452 patcherr = not patcherr
448 453
449 if merge and len(files) > 0:
454 if merge and files:
450 455 # Mark as merged and update dirstate parent info
451 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
456 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
452 457 p1, p2 = repo.dirstate.parents()
453 458 repo.dirstate.setparents(p1, merge)
454 if len(files) > 0:
455 cwd = repo.getcwd()
456 cfiles = files
457 if cwd:
458 cfiles = [util.pathto(cwd, f) for f in files]
459 cmdutil.addremove(repo, cfiles, wlock=wlock)
459 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
460 460 n = repo.commit(files, message, user, date, force=1, lock=lock,
461 461 wlock=wlock)
462 462
@@ -464,11 +464,11 b' class queue:'
464 464 raise util.Abort(_("repo commit failed"))
465 465
466 466 if update_status:
467 self.applied.append(statusentry(revlog.hex(n), patch))
467 self.applied.append(statusentry(revlog.hex(n), patchname))
468 468
469 469 if patcherr:
470 470 if not patchfound:
471 self.ui.warn("patch %s is empty\n" % patch)
471 self.ui.warn("patch %s is empty\n" % patchname)
472 472 err = 0
473 473 else:
474 474 self.ui.warn("patch failed, rejects left in working dir\n")
@@ -904,15 +904,15 b' class queue:'
904 904 else:
905 905 self.ui.write("Patch queue now empty\n")
906 906
907 def diff(self, repo, files):
907 def diff(self, repo, pats, opts):
908 908 top = self.check_toppatch(repo)
909 909 if not top:
910 910 self.ui.write("No patches applied\n")
911 911 return
912 912 qp = self.qparents(repo, top)
913 self.printdiff(repo, qp, files=files)
913 self.printdiff(repo, qp, files=pats, opts=opts)
914 914
915 def refresh(self, repo, msg='', short=False):
915 def refresh(self, repo, pats=None, **opts):
916 916 if len(self.applied) == 0:
917 917 self.ui.write("No patches applied\n")
918 918 return
@@ -925,7 +925,7 b' class queue:'
925 925 message, comments, user, date, patchfound = self.readheaders(patch)
926 926
927 927 patchf = self.opener(patch, "w")
928 msg = msg.rstrip()
928 msg = opts.get('msg', '').rstrip()
929 929 if msg:
930 930 if comments:
931 931 # Remove existing message.
@@ -939,6 +939,7 b' class queue:'
939 939 comments = "\n".join(comments) + '\n\n'
940 940 patchf.write(comments)
941 941
942 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
942 943 tip = repo.changelog.tip()
943 944 if top == tip:
944 945 # if the top of our patch queue is also the tip, there is an
@@ -956,7 +957,7 b' class queue:'
956 957 # caching against the next repo.status call
957 958 #
958 959 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
959 if short:
960 if opts.get('short'):
960 961 filelist = mm + aa + dd
961 962 else:
962 963 filelist = None
@@ -992,16 +993,27 b' class queue:'
992 993 m = list(util.unique(mm))
993 994 r = list(util.unique(dd))
994 995 a = list(util.unique(aa))
995 filelist = list(util.unique(m + r + a))
996 filelist = filter(matchfn, util.unique(m + r + a))
996 997 self.printdiff(repo, patchparent, files=filelist,
997 998 changes=(m, a, r, [], u), fp=patchf)
998 999 patchf.close()
999 1000
1000 1001 changes = repo.changelog.read(tip)
1001 1002 repo.dirstate.setparents(*cparents)
1003 copies = [(f, repo.dirstate.copied(f)) for f in a]
1002 1004 repo.dirstate.update(a, 'a')
1005 for dst, src in copies:
1006 repo.dirstate.copy(src, dst)
1003 1007 repo.dirstate.update(r, 'r')
1008 # if the patch excludes a modified file, mark that file with mtime=0
1009 # so status can see it.
1010 mm = []
1011 for i in range(len(m)-1, -1, -1):
1012 if not matchfn(m[i]):
1013 mm.append(m[i])
1014 del m[i]
1004 1015 repo.dirstate.update(m, 'n')
1016 repo.dirstate.update(mm, 'n', st_mtime=0)
1005 1017 repo.dirstate.forget(forget)
1006 1018
1007 1019 if not msg:
@@ -1216,7 +1228,7 b' class queue:'
1216 1228 if not self.ui.verbose:
1217 1229 p = pname
1218 1230 else:
1219 p = str(self.series.index(pname)) + " " + p
1231 p = str(self.series.index(pname)) + " " + pname
1220 1232 return p
1221 1233
1222 1234 def top(self, repo):
@@ -1411,17 +1423,24 b' def new(ui, repo, patch, **opts):'
1411 1423 changes unless -f is specified, in which case the patch will
1412 1424 be initialised with them.
1413 1425
1414 -m or -l set the patch header as well as the commit message.
1415 If neither is specified, the patch header is empty and the
1426 -e, -m or -l set the patch header as well as the commit message.
1427 If none is specified, the patch header is empty and the
1416 1428 commit message is 'New patch: PATCH'"""
1417 1429 q = repo.mq
1418 1430 message = commands.logmessage(opts)
1431 if opts['edit']:
1432 message = ui.edit(message, ui.username())
1419 1433 q.new(repo, patch, msg=message, force=opts['force'])
1420 1434 q.save_dirty()
1421 1435 return 0
1422 1436
1423 def refresh(ui, repo, **opts):
1424 """update the current patch"""
1437 def refresh(ui, repo, *pats, **opts):
1438 """update the current patch
1439
1440 If any file patterns are provided, the refreshed patch will contain only
1441 the modifications that match those patterns; the remaining modifications
1442 will remain in the working directory.
1443 """
1425 1444 q = repo.mq
1426 1445 message = commands.logmessage(opts)
1427 1446 if opts['edit']:
@@ -1430,14 +1449,13 b' def refresh(ui, repo, **opts):'
1430 1449 patch = q.applied[-1].name
1431 1450 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1432 1451 message = ui.edit('\n'.join(message), user or ui.username())
1433 q.refresh(repo, msg=message, short=opts['short'])
1452 q.refresh(repo, pats, msg=message, **opts)
1434 1453 q.save_dirty()
1435 1454 return 0
1436 1455
1437 def diff(ui, repo, *files, **opts):
1456 def diff(ui, repo, *pats, **opts):
1438 1457 """diff of the current patch"""
1439 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1440 repo.mq.diff(repo, list(files))
1458 repo.mq.diff(repo, pats, opts)
1441 1459 return 0
1442 1460
1443 1461 def fold(ui, repo, *files, **opts):
@@ -1469,20 +1487,21 b' def fold(ui, repo, *files, **opts):'
1469 1487 patches = []
1470 1488 messages = []
1471 1489 for f in files:
1472 patch = q.lookup(f)
1473 if patch in patches or patch == parent:
1474 ui.warn(_('Skipping already folded patch %s') % patch)
1475 if q.isapplied(patch):
1476 raise util.Abort(_('qfold cannot fold already applied patch %s') % patch)
1477 patches.append(patch)
1490 p = q.lookup(f)
1491 if p in patches or p == parent:
1492 ui.warn(_('Skipping already folded patch %s') % p)
1493 if q.isapplied(p):
1494 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1495 patches.append(p)
1478 1496
1479 for patch in patches:
1497 for p in patches:
1480 1498 if not message:
1481 messages.append(q.readheaders(patch)[0])
1482 pf = q.join(patch)
1499 messages.append(q.readheaders(p)[0])
1500 pf = q.join(p)
1483 1501 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1484 1502 if not patchsuccess:
1485 raise util.Abort(_('Error folding patch %s') % patch)
1503 raise util.Abort(_('Error folding patch %s') % p)
1504 patch.updatedir(ui, repo, files)
1486 1505
1487 1506 if not message:
1488 1507 message, comments, user = q.readheaders(parent)[0:3]
@@ -1495,29 +1514,26 b' def fold(ui, repo, *files, **opts):'
1495 1514 message = ui.edit(message, user or ui.username())
1496 1515
1497 1516 q.refresh(repo, msg=message)
1498
1499 for patch in patches:
1500 q.delete(repo, patch, keep=opts['keep'])
1501
1517 q.delete(repo, patches, keep=opts['keep'])
1502 1518 q.save_dirty()
1503 1519
1504 1520 def guard(ui, repo, *args, **opts):
1505 1521 '''set or print guards for a patch
1506 1522
1507 guards control whether a patch can be pushed. a patch with no
1508 guards is aways pushed. a patch with posative guard ("+foo") is
1509 pushed only if qselect command enables guard "foo". a patch with
1510 nagative guard ("-foo") is never pushed if qselect command enables
1511 guard "foo".
1523 Guards control whether a patch can be pushed. A patch with no
1524 guards is always pushed. A patch with a positive guard ("+foo") is
1525 pushed only if the qselect command has activated it. A patch with
1526 a negative guard ("-foo") is never pushed if the qselect command
1527 has activated it.
1512 1528
1513 with no arguments, default is to print current active guards.
1514 with arguments, set active guards for patch.
1529 With no arguments, print the currently active guards.
1530 With arguments, set guards for the named patch.
1515 1531
1516 to set nagative guard "-foo" on topmost patch ("--" is needed so
1517 hg will not interpret "-foo" as argument):
1532 To set a negative guard "-foo" on topmost patch ("--" is needed so
1533 hg will not interpret "-foo" as an option):
1518 1534 hg qguard -- -foo
1519 1535
1520 to set guards on other patch:
1536 To set guards on another patch:
1521 1537 hg qguard other.patch +2.6.17 -stable
1522 1538 '''
1523 1539 def status(idx):
@@ -1723,32 +1739,34 b' def strip(ui, repo, rev, **opts):'
1723 1739 def select(ui, repo, *args, **opts):
1724 1740 '''set or print guarded patches to push
1725 1741
1726 use qguard command to set or print guards on patch. then use
1727 qselect to tell mq which guards to use. example:
1742 Use the qguard command to set or print guards on patch, then use
1743 qselect to tell mq which guards to use. A patch will be pushed if it
1744 has no guards or any positive guards match the currently selected guard,
1745 but will not be pushed if any negative guards match the current guard.
1746 For example:
1728 1747
1729 qguard foo.patch -stable (nagative guard)
1730 qguard bar.patch +stable (posative guard)
1748 qguard foo.patch -stable (negative guard)
1749 qguard bar.patch +stable (positive guard)
1731 1750 qselect stable
1732 1751
1733 this sets "stable" guard. mq will skip foo.patch (because it has
1734 nagative match) but push bar.patch (because it has posative
1735 match). patch is pushed if any posative guards match and no
1736 nagative guards match.
1752 This activates the "stable" guard. mq will skip foo.patch (because
1753 it has a negative match) but push bar.patch (because it
1754 has a positive match).
1737 1755
1738 with no arguments, default is to print current active guards.
1739 with arguments, set active guards as given.
1756 With no arguments, prints the currently active guards.
1757 With one argument, sets the active guard.
1740 1758
1741 use -n/--none to deactivate guards (no other arguments needed).
1742 when no guards active, patches with posative guards are skipped,
1743 patches with nagative guards are pushed.
1759 Use -n/--none to deactivate guards (no other arguments needed).
1760 When no guards are active, patches with positive guards are skipped
1761 and patches with negative guards are pushed.
1744 1762
1745 qselect can change guards of applied patches. it does not pop
1746 guarded patches by default. use --pop to pop back to last applied
1747 patch that is not guarded. use --reapply (implies --pop) to push
1748 back to current patch afterwards, but skip guarded patches.
1763 qselect can change the guards on applied patches. It does not pop
1764 guarded patches by default. Use --pop to pop back to the last applied
1765 patch that is not guarded. Use --reapply (which implies --pop) to push
1766 back to the current patch afterwards, but skip guarded patches.
1749 1767
1750 use -s/--series to print list of all guards in series file (no
1751 other arguments needed). use -v for more information.'''
1768 Use -s/--series to print a list of all guards in the series file (no
1769 other arguments needed). Use -v for more information.'''
1752 1770
1753 1771 q = repo.mq
1754 1772 guards = q.active()
@@ -1885,7 +1903,10 b' cmdtable = {'
1885 1903 (commit,
1886 1904 commands.table["^commit|ci"][1],
1887 1905 'hg qcommit [OPTION]... [FILE]...'),
1888 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1906 "^qdiff": (diff,
1907 [('I', 'include', [], _('include names matching the given patterns')),
1908 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1909 'hg qdiff [-I] [-X] [FILE]...'),
1889 1910 "qdelete|qremove|qrm":
1890 1911 (delete,
1891 1912 [('k', 'keep', None, _('keep patch file'))],
@@ -1914,10 +1935,11 b' cmdtable = {'
1914 1935 'hg qinit [-c]'),
1915 1936 "qnew":
1916 1937 (new,
1917 [('m', 'message', '', _('use <text> as commit message')),
1938 [('e', 'edit', None, _('edit commit message')),
1939 ('m', 'message', '', _('use <text> as commit message')),
1918 1940 ('l', 'logfile', '', _('read the commit message from <file>')),
1919 1941 ('f', 'force', None, _('import uncommitted changes into patch'))],
1920 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1942 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
1921 1943 "qnext": (next, [], 'hg qnext'),
1922 1944 "qprev": (prev, [], 'hg qprev'),
1923 1945 "^qpop":
@@ -1939,8 +1961,10 b' cmdtable = {'
1939 1961 [('e', 'edit', None, _('edit commit message')),
1940 1962 ('m', 'message', '', _('change commit message with <text>')),
1941 1963 ('l', 'logfile', '', _('change commit message with <file> content')),
1942 ('s', 'short', None, 'short refresh')],
1943 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'),
1964 ('s', 'short', None, 'short refresh'),
1965 ('I', 'include', [], _('include names matching the given patterns')),
1966 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
1967 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
1944 1968 'qrename|qmv':
1945 1969 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
1946 1970 "qrestore":
@@ -1526,7 +1526,6 b' def grep(ui, repo, pattern, *pats, **opt'
1526 1526 if st == 'window':
1527 1527 incrementing = rev
1528 1528 matches.clear()
1529 copies.clear()
1530 1529 elif st == 'add':
1531 1530 change = repo.changelog.read(repo.lookup(str(rev)))
1532 1531 mf = repo.manifest.read(change[0])
@@ -1535,20 +1534,19 b' def grep(ui, repo, pattern, *pats, **opt'
1535 1534 if fn in skip:
1536 1535 continue
1537 1536 fstate.setdefault(fn, {})
1538 copies.setdefault(rev, {})
1539 1537 try:
1540 1538 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1541 1539 if follow:
1542 1540 copied = getfile(fn).renamed(mf[fn])
1543 1541 if copied:
1544 copies[rev][fn] = copied[0]
1542 copies.setdefault(rev, {})[fn] = copied[0]
1545 1543 except KeyError:
1546 1544 pass
1547 1545 elif st == 'iter':
1548 1546 states = matches[rev].items()
1549 1547 states.sort()
1550 1548 for fn, m in states:
1551 copy = copies[rev].get(fn)
1549 copy = copies.get(rev, {}).get(fn)
1552 1550 if fn in skip:
1553 1551 if copy:
1554 1552 skip[copy] = True
@@ -1571,7 +1569,7 b' def grep(ui, repo, pattern, *pats, **opt'
1571 1569 for fn, state in fstate:
1572 1570 if fn in skip:
1573 1571 continue
1574 if fn not in copies[prev[fn]]:
1572 if fn not in copies.get(prev[fn], {}):
1575 1573 display(fn, rev, {}, state)
1576 1574 return (count == 0 and 1) or 0
1577 1575
@@ -1683,44 +1681,7 b' def import_(ui, repo, patch1, *patches, '
1683 1681 ui.debug(_('message:\n%s\n') % message)
1684 1682
1685 1683 files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root)
1686 removes = []
1687 if len(files) > 0:
1688 cfiles = files.keys()
1689 copies = []
1690 copts = {'after': False, 'force': False}
1691 cwd = repo.getcwd()
1692 if cwd:
1693 cfiles = [util.pathto(cwd, f) for f in files.keys()]
1694 for f in files:
1695 ctype, gp = files[f]
1696 if ctype == 'RENAME':
1697 copies.append((gp.oldpath, gp.path, gp.copymod))
1698 removes.append(gp.oldpath)
1699 elif ctype == 'COPY':
1700 copies.append((gp.oldpath, gp.path, gp.copymod))
1701 elif ctype == 'DELETE':
1702 removes.append(gp.path)
1703 for src, dst, after in copies:
1704 absdst = os.path.join(repo.root, dst)
1705 if not after and os.path.exists(absdst):
1706 raise util.Abort(_('patch creates existing file %s') % dst)
1707 if cwd:
1708 src, dst = [util.pathto(cwd, f) for f in (src, dst)]
1709 copts['after'] = after
1710 errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock)
1711 if errs:
1712 raise util.Abort(errs)
1713 if removes:
1714 repo.remove(removes, True, wlock=wlock)
1715 for f in files:
1716 ctype, gp = files[f]
1717 if gp and gp.mode:
1718 x = gp.mode & 0100 != 0
1719 dst = os.path.join(repo.root, gp.path)
1720 util.set_exec(dst, x)
1721 cmdutil.addremove(repo, cfiles, wlock=wlock)
1722 files = files.keys()
1723 files.extend([r for r in removes if r not in files])
1684 files = patch.updatedir(ui, repo, files, wlock=wlock)
1724 1685 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1725 1686 finally:
1726 1687 os.unlink(tmpname)
@@ -3281,18 +3242,11 b' def findext(name):'
3281 3242 return sys.modules[v]
3282 3243 raise KeyError(name)
3283 3244
3284 def dispatch(args):
3285 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3286 num = getattr(signal, name, None)
3287 if num: signal.signal(num, catchterm)
3288
3289 try:
3290 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3291 except util.Abort, inst:
3292 sys.stderr.write(_("abort: %s\n") % inst)
3293 return -1
3294
3295 for ext_name, load_from_name in u.extensions():
3245 def load_extensions(ui):
3246 added = []
3247 for ext_name, load_from_name in ui.extensions():
3248 if ext_name in external:
3249 continue
3296 3250 try:
3297 3251 if load_from_name:
3298 3252 # the module will be loaded in sys.modules
@@ -3312,23 +3266,36 b' def dispatch(args):'
3312 3266 except ImportError:
3313 3267 mod = importh(ext_name)
3314 3268 external[ext_name] = mod.__name__
3269 added.append((mod, ext_name))
3315 3270 except (util.SignalInterrupt, KeyboardInterrupt):
3316 3271 raise
3317 3272 except Exception, inst:
3318 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3319 if u.print_exc():
3273 ui.warn(_("*** failed to import extension %s: %s\n") %
3274 (ext_name, inst))
3275 if ui.print_exc():
3320 3276 return 1
3321 3277
3322 for name in external.itervalues():
3323 mod = sys.modules[name]
3278 for mod, name in added:
3324 3279 uisetup = getattr(mod, 'uisetup', None)
3325 3280 if uisetup:
3326 uisetup(u)
3281 uisetup(ui)
3327 3282 cmdtable = getattr(mod, 'cmdtable', {})
3328 3283 for t in cmdtable:
3329 3284 if t in table:
3330 u.warn(_("module %s overrides %s\n") % (name, t))
3285 ui.warn(_("module %s overrides %s\n") % (name, t))
3331 3286 table.update(cmdtable)
3287
3288 def dispatch(args):
3289 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3290 num = getattr(signal, name, None)
3291 if num: signal.signal(num, catchterm)
3292
3293 try:
3294 u = ui.ui(traceback='--traceback' in sys.argv[1:],
3295 readhooks=[load_extensions])
3296 except util.Abort, inst:
3297 sys.stderr.write(_("abort: %s\n") % inst)
3298 return -1
3332 3299
3333 3300 try:
3334 3301 cmd, func, args, options, cmdoptions = parse(u, args)
@@ -96,31 +96,59 b' class filelog(revlog):'
96 96 return child
97 97
98 98 # find all ancestors
99 needed = {node:1}
100 visit = [node]
99 needed = {(self, node):1}
100 files = [self]
101 visit = [(self, node)]
101 102 while visit:
102 n = visit.pop(0)
103 for p in self.parents(n):
104 if p not in needed:
105 needed[p] = 1
106 visit.append(p)
103 f, n = visit.pop(0)
104 rn = f.renamed(n)
105 if rn:
106 f, n = rn
107 f = filelog(self.opener, f, self.defversion)
108 files.insert(0, f)
109 if (f, n) not in needed:
110 needed[(f, n)] = 1
111 else:
112 needed[(f, n)] += 1
113 for p in f.parents(n):
114 if p == nullid:
115 continue
116 if (f, p) not in needed:
117 needed[(f, p)] = 1
118 visit.append((f, p))
107 119 else:
108 120 # count how many times we'll use this
109 needed[p] += 1
121 needed[(f, p)] += 1
110 122
111 # sort by revision which is a topological order
112 visit = [ (self.rev(n), n) for n in needed.keys() ]
113 visit.sort()
123 # sort by revision (per file) which is a topological order
124 visit = []
125 for f in files:
126 fn = [(f.rev(n[1]), f, n[1]) for n in needed.keys() if n[0] == f]
127 fn.sort()
128 visit.extend(fn)
114 129 hist = {}
115 130
116 for r,n in visit:
117 curr = decorate(self.read(n), self.linkrev(n))
118 for p in self.parents(n):
131 for i in range(len(visit)):
132 r, f, n = visit[i]
133 curr = decorate(f.read(n), f.linkrev(n))
134 if r == -1:
135 continue
136 parents = f.parents(n)
137 # follow parents across renames
138 if r < 1 and i > 0:
139 j = i
140 while j > 0 and visit[j][1] == f:
141 j -= 1
142 parents = (visit[j][2],)
143 f = visit[j][1]
144 else:
145 parents = f.parents(n)
146 for p in parents:
119 147 if p != nullid:
120 148 curr = pair(hist[p], curr)
121 149 # trim the history of unneeded revs
122 needed[p] -= 1
123 if not needed[p]:
150 needed[(f, p)] -= 1
151 if not needed[(f, p)]:
124 152 del hist[p]
125 153 hist[n] = curr
126 154
@@ -11,6 +11,28 b' from node import *'
11 11 demandload(globals(), "cmdutil mdiff util")
12 12 demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
13 13
14 # helper functions
15
16 def copyfile(src, dst, basedir=None):
17 if not basedir:
18 basedir = os.getcwd()
19
20 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 if os.path.exists(absdst):
22 raise util.Abort(_("cannot create %s: destination already exists") %
23 dst)
24
25 targetdir = os.path.dirname(absdst)
26 if not os.path.isdir(targetdir):
27 os.makedirs(targetdir)
28 try:
29 shutil.copyfile(abssrc, absdst)
30 shutil.copymode(abssrc, absdst)
31 except shutil.Error, inst:
32 raise util.Abort(str(inst))
33
34 # public functions
35
14 36 def extract(ui, fileobj):
15 37 '''extract patch from data read from fileobj.
16 38
@@ -174,21 +196,7 b' def dogitpatch(patchname, gitpatches):'
174 196 if not p.copymod:
175 197 continue
176 198
177 if os.path.exists(p.path):
178 raise util.Abort(_("cannot create %s: destination already exists") %
179 p.path)
180
181 (src, dst) = [os.path.join(os.getcwd(), n)
182 for n in (p.oldpath, p.path)]
183
184 targetdir = os.path.dirname(dst)
185 if not os.path.isdir(targetdir):
186 os.makedirs(targetdir)
187 try:
188 shutil.copyfile(src, dst)
189 shutil.copymode(src, dst)
190 except shutil.Error, inst:
191 raise util.Abort(str(inst))
199 copyfile(p.oldpath, p.path)
192 200
193 201 # rewrite patch hunk
194 202 while pfline < p.lineno:
@@ -281,6 +289,45 b' def diffopts(ui, opts={}):'
281 289 ignoreblanklines=(opts.get('ignore_blank_lines') or
282 290 ui.configbool('diff', 'ignoreblanklines', None)))
283 291
292 def updatedir(ui, repo, patches, wlock=None):
293 '''Update dirstate after patch application according to metadata'''
294 if not patches:
295 return
296 copies = []
297 removes = []
298 cfiles = patches.keys()
299 copts = {'after': False, 'force': False}
300 cwd = repo.getcwd()
301 if cwd:
302 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
303 for f in patches:
304 ctype, gp = patches[f]
305 if ctype == 'RENAME':
306 copies.append((gp.oldpath, gp.path, gp.copymod))
307 removes.append(gp.oldpath)
308 elif ctype == 'COPY':
309 copies.append((gp.oldpath, gp.path, gp.copymod))
310 elif ctype == 'DELETE':
311 removes.append(gp.path)
312 for src, dst, after in copies:
313 if not after:
314 copyfile(src, dst, repo.root)
315 repo.copy(src, dst, wlock=wlock)
316 if removes:
317 repo.remove(removes, True, wlock=wlock)
318 for f in patches:
319 ctype, gp = patches[f]
320 if gp and gp.mode:
321 x = gp.mode & 0100 != 0
322 dst = os.path.join(repo.root, gp.path)
323 util.set_exec(dst, x)
324 cmdutil.addremove(repo, cfiles, wlock=wlock)
325 files = patches.keys()
326 files.extend([r for r in removes if r not in files])
327 files.sort()
328
329 return files
330
284 331 def diff(repo, node1=None, node2=None, files=None, match=util.always,
285 332 fp=None, changes=None, opts=None):
286 333 '''print diff of changes to files between two nodes, or node and
@@ -296,10 +343,27 b' def diff(repo, node1=None, node2=None, f'
296 343
297 344 if not node1:
298 345 node1 = repo.dirstate.parents()[0]
346
347 clcache = {}
348 def getchangelog(n):
349 if n not in clcache:
350 clcache[n] = repo.changelog.read(n)
351 return clcache[n]
352 mcache = {}
353 def getmanifest(n):
354 if n not in mcache:
355 mcache[n] = repo.manifest.read(n)
356 return mcache[n]
357 fcache = {}
358 def getfile(f):
359 if f not in fcache:
360 fcache[f] = repo.file(f)
361 return fcache[f]
362
299 363 # reading the data for node1 early allows it to play nicely
300 364 # with repo.status and the revlog cache.
301 change = repo.changelog.read(node1)
302 mmap = repo.manifest.read(change[0])
365 change = getchangelog(node1)
366 mmap = getmanifest(change[0])
303 367 date1 = util.datestr(change[2])
304 368
305 369 if not changes:
@@ -320,17 +384,32 b' def diff(repo, node1=None, node2=None, f'
320 384 if not modified and not added and not removed:
321 385 return
322 386
387 def renamedbetween(f, n1, n2):
388 r1, r2 = map(repo.changelog.rev, (n1, n2))
389 src = None
390 while r2 > r1:
391 cl = getchangelog(n2)[0]
392 m = getmanifest(cl)
393 try:
394 src = getfile(f).renamed(m[f])
395 except KeyError:
396 return None
397 if src:
398 f = src[0]
399 n2 = repo.changelog.parents(n2)[0]
400 r2 = repo.changelog.rev(n2)
401 return src
402
323 403 if node2:
324 change = repo.changelog.read(node2)
325 mmap2 = repo.manifest.read(change[0])
404 change = getchangelog(node2)
405 mmap2 = getmanifest(change[0])
326 406 _date2 = util.datestr(change[2])
327 407 def date2(f):
328 408 return _date2
329 409 def read(f):
330 return repo.file(f).read(mmap2[f])
410 return getfile(f).read(mmap2[f])
331 411 def renamed(f):
332 src = repo.file(f).renamed(mmap2[f])
333 return src and src[0] or None
412 return renamedbetween(f, node1, node2)
334 413 else:
335 414 tz = util.makedate()[1]
336 415 _date2 = util.datestr()
@@ -343,7 +422,18 b' def diff(repo, node1=None, node2=None, f'
343 422 def read(f):
344 423 return repo.wread(f)
345 424 def renamed(f):
346 return repo.dirstate.copies.get(f)
425 src = repo.dirstate.copies.get(f)
426 parent = repo.dirstate.parents()[0]
427 if src:
428 f = src[0]
429 of = renamedbetween(f, node1, parent)
430 if of:
431 return of
432 elif src:
433 cl = getchangelog(parent)[0]
434 return (src, getmanifest(cl)[src])
435 else:
436 return None
347 437
348 438 if repo.ui.quiet:
349 439 r = None
@@ -357,7 +447,7 b' def diff(repo, node1=None, node2=None, f'
357 447 src = renamed(f)
358 448 if src:
359 449 copied[f] = src
360 srcs = [x[1] for x in copied.items()]
450 srcs = [x[1][0] for x in copied.items()]
361 451
362 452 all = modified + added + removed
363 453 all.sort()
@@ -366,7 +456,7 b' def diff(repo, node1=None, node2=None, f'
366 456 tn = None
367 457 dodiff = True
368 458 if f in mmap:
369 to = repo.file(f).read(mmap[f])
459 to = getfile(f).read(mmap[f])
370 460 if f not in removed:
371 461 tn = read(f)
372 462 if opts.git:
@@ -385,13 +475,13 b' def diff(repo, node1=None, node2=None, f'
385 475 else:
386 476 mode = gitmode(util.is_exec(repo.wjoin(f), None))
387 477 if f in copied:
388 a = copied[f]
478 a, arev = copied[f]
389 479 omode = gitmode(mmap.execf(a))
390 480 addmodehdr(header, omode, mode)
391 481 op = a in removed and 'rename' or 'copy'
392 482 header.append('%s from %s\n' % (op, a))
393 483 header.append('%s to %s\n' % (op, f))
394 to = repo.file(a).read(mmap[a])
484 to = getfile(a).read(arev)
395 485 else:
396 486 header.append('new file mode %s\n' % mode)
397 487 elif f in removed:
@@ -12,11 +12,13 b' demandload(globals(), "ConfigParser mdif'
12 12
13 13 class ui(object):
14 14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True, traceback=False, parentui=None):
15 interactive=True, traceback=False, parentui=None,
16 readhooks=[]):
16 17 self.overlay = {}
17 18 if parentui is None:
18 19 # this is the parent of all ui children
19 20 self.parentui = None
21 self.readhooks = list(readhooks)
20 22 self.cdata = ConfigParser.SafeConfigParser()
21 23 self.readconfig(util.rcpath())
22 24
@@ -34,6 +36,7 b' class ui(object):'
34 36 else:
35 37 # parentui may point to an ui object which is already a child
36 38 self.parentui = parentui.parentui or parentui
39 self.readhooks = list(parentui.readhooks or readhooks)
37 40 parent_cdata = self.parentui.cdata
38 41 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 42 # make interpolation work
@@ -78,6 +81,8 b' class ui(object):'
78 81 for name, path in self.configitems("paths"):
79 82 if path and "://" not in path and not os.path.isabs(path):
80 83 self.cdata.set("paths", name, os.path.join(root, path))
84 for hook in self.readhooks:
85 hook(self)
81 86
82 87 def setconfig(self, section, name, val):
83 88 self.overlay[(section, name)] = val
@@ -28,6 +28,6 b' writing tests:'
28 28
29 29 - diff will show the current time
30 30
31 use hg diff | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" to strip
32 dates
33
31 use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
32 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
33 to strip dates
@@ -17,7 +17,7 b' while test $count -lt 32 ; do'
17 17 test $count -eq 0 && hg add
18 18 hg ci -m "msg $count" -d "$count 0"
19 19 echo % committed changeset $count
20 count=$(( $count + 1 ))
20 count=`expr $count + 1`
21 21 done
22 22
23 23 echo % log
@@ -8,7 +8,11 b' hg init a'
8 8 cd a
9 9 echo a > a
10 10 hg add
11 hg extdiff -o -Nr
11 diff -N /dev/null /dev/null 2> /dev/null
12 if [ $? -ne 0 ]; then
13 opt="-p gdiff"
14 fi
15 hg extdiff -o -Nr $opt
12 16
13 17 echo "[extdiff]" >> $HGTMP/.hgrc
14 18 echo "cmd.falabala=echo" >> $HGTMP/.hgrc
@@ -8,22 +8,26 b" hg ci -Amstart -d '0 0'"
8 8 echo new > new
9 9 hg ci -Amnew -d '0 0'
10 10 echo '% new file'
11 hg diff --git -r 0 | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
11 hg diff --git -r 0 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
12 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
12 13
13 14 hg cp new copy
14 15 hg ci -mcopy -d '0 0'
15 16 echo '% copy'
16 hg diff --git -r 1:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
17 hg diff --git -r 1:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
18 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
17 19
18 20 hg mv copy rename
19 21 hg ci -mrename -d '0 0'
20 22 echo '% rename'
21 hg diff --git -r 2:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
23 hg diff --git -r 2:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
24 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
22 25
23 26 hg rm rename
24 27 hg ci -mdelete -d '0 0'
25 28 echo '% delete'
26 hg diff --git -r 3:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
29 hg diff --git -r 3:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
30 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
27 31
28 32 cat > src <<EOF
29 33 1
@@ -36,11 +40,13 b" hg ci -Amsrc -d '0 0'"
36 40 chmod +x src
37 41 hg ci -munexec -d '0 0'
38 42 echo '% chmod 644'
39 hg diff --git -r 5:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
43 hg diff --git -r 5:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
44 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
40 45
41 46 hg mv src dst
42 47 chmod -x dst
43 48 echo a >> dst
44 49 hg ci -mrenamemod -d '0 0'
45 50 echo '% rename+mod+chmod'
46 hg diff --git -r 6:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/"
51 hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
52 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
@@ -126,3 +126,30 b' echo x>x'
126 126 hg ci -Ama
127 127 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
128 128 hg unbundle .hg/strip-backup/*
129
130 cat >>$HGTMP/.hgrc <<EOF
131 [diff]
132 git = True
133 EOF
134 cd ..
135 hg init git
136 cd git
137 hg qinit
138
139 hg qnew -m'new file' new
140 echo foo > new
141 chmod +x new
142 hg add new
143 hg qrefresh
144 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
145 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
146
147 hg qnew -m'copy file' copy
148 hg cp new copy
149 hg qrefresh
150 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
151 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
152
153 hg qpop
154 hg qpush
155 hg qdiff
@@ -127,3 +127,22 b' adding manifests'
127 127 adding file changes
128 128 added 1 changesets with 1 changes to 1 files
129 129 (run 'hg update' to get a working copy)
130 new file
131
132 diff --git a/new b/new
133 new file mode 100755
134 --- /dev/null
135 +++ b/new
136 @@ -0,0 +1,1 @@
137 +foo
138 copy file
139
140 diff --git a/new b/copy
141 copy from new
142 copy to copy
143 Now at: new
144 applying copy
145 Now at: copy
146 diff --git a/new b/copy
147 copy from new
148 copy to copy
General Comments 0
You need to be logged in to leave comments. Login now