mercurial/hg.py
changeset 723 9e0f3ba4a9c2
parent 705 574869103985
child 724 1c0c413cccdd
equal deleted inserted replaced
705:574869103985 723:9e0f3ba4a9c2
    10 from revlog import *
    10 from revlog import *
    11 from demandload import *
    11 from demandload import *
    12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
    12 demandload(globals(), "re lock urllib urllib2 transaction time socket")
    13 demandload(globals(), "tempfile httprangereader bdiff")
    13 demandload(globals(), "tempfile httprangereader bdiff")
    14 demandload(globals(), "bisect select")
    14 demandload(globals(), "bisect select")
       
    15 
       
    16 def always(fn):
       
    17     return True
    15 
    18 
    16 class filelog(revlog):
    19 class filelog(revlog):
    17     def __init__(self, opener, path):
    20     def __init__(self, opener, path):
    18         revlog.__init__(self, opener,
    21         revlog.__init__(self, opener,
    19                         os.path.join("data", path + ".i"),
    22                         os.path.join("data", path + ".i"),
   274         self.dirty = 0
   277         self.dirty = 0
   275         self.ui = ui
   278         self.ui = ui
   276         self.map = None
   279         self.map = None
   277         self.pl = None
   280         self.pl = None
   278         self.copies = {}
   281         self.copies = {}
       
   282         self.ignorefunc = None
       
   283 
       
   284     def wjoin(self, f):
       
   285         return os.path.join(self.root, f)
       
   286 
       
   287     def ignore(self, f):
       
   288         if not self.ignorefunc:
       
   289             bigpat = []
       
   290             try:
       
   291                 l = file(self.wjoin(".hgignore"))
       
   292                 for pat in l:
       
   293                     if pat != "\n":
       
   294                         p = util.pconvert(pat[:-1])
       
   295                         try:
       
   296                             r = re.compile(p)
       
   297                         except:
       
   298                             self.ui.warn("ignoring invalid ignore"
       
   299                                          + " regular expression '%s'\n" % p)
       
   300                         else:
       
   301                             bigpat.append(util.pconvert(pat[:-1]))
       
   302             except IOError: pass
       
   303 
       
   304             s = "(?:%s)" % (")|(?:".join(bigpat))
       
   305             r = re.compile(s)
       
   306             self.ignorefunc = r.search
       
   307 
       
   308         return self.ignorefunc(f)
   279 
   309 
   280     def __del__(self):
   310     def __del__(self):
   281         if self.dirty:
   311         if self.dirty:
   282             self.write()
   312             self.write()
   283 
   313 
   295     def parents(self):
   325     def parents(self):
   296         if not self.pl:
   326         if not self.pl:
   297             self.read()
   327             self.read()
   298         return self.pl
   328         return self.pl
   299 
   329 
       
   330     def markdirty(self):
       
   331         if not self.dirty:
       
   332             self.dirty = 1
       
   333 
   300     def setparents(self, p1, p2 = nullid):
   334     def setparents(self, p1, p2 = nullid):
   301         self.dirty = 1
   335         self.markdirty()
   302         self.pl = p1, p2
   336         self.pl = p1, p2
   303 
   337 
   304     def state(self, key):
   338     def state(self, key):
   305         try:
   339         try:
   306             return self[key][0]
   340             return self[key][0]
   331             self.map[f] = e[:4]
   365             self.map[f] = e[:4]
   332             pos += l
   366             pos += l
   333 
   367 
   334     def copy(self, source, dest):
   368     def copy(self, source, dest):
   335         self.read()
   369         self.read()
   336         self.dirty = 1
   370         self.markdirty()
   337         self.copies[dest] = source
   371         self.copies[dest] = source
   338 
   372 
   339     def copied(self, file):
   373     def copied(self, file):
   340         return self.copies.get(file, None)
   374         return self.copies.get(file, None)
   341 
   375 
   346         r  marked for removal
   380         r  marked for removal
   347         a  marked for addition'''
   381         a  marked for addition'''
   348 
   382 
   349         if not files: return
   383         if not files: return
   350         self.read()
   384         self.read()
   351         self.dirty = 1
   385         self.markdirty()
   352         for f in files:
   386         for f in files:
   353             if state == "r":
   387             if state == "r":
   354                 self.map[f] = ('r', 0, 0, 0)
   388                 self.map[f] = ('r', 0, 0, 0)
   355             else:
   389             else:
   356                 s = os.stat(os.path.join(self.root, f))
   390                 s = os.stat(os.path.join(self.root, f))
   357                 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
   391                 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
   358 
   392 
   359     def forget(self, files):
   393     def forget(self, files):
   360         if not files: return
   394         if not files: return
   361         self.read()
   395         self.read()
   362         self.dirty = 1
   396         self.markdirty()
   363         for f in files:
   397         for f in files:
   364             try:
   398             try:
   365                 del self.map[f]
   399                 del self.map[f]
   366             except KeyError:
   400             except KeyError:
   367                 self.ui.warn("not in dirstate: %s!\n" % f)
   401                 self.ui.warn("not in dirstate: %s!\n" % f)
   368                 pass
   402                 pass
   369 
   403 
   370     def clear(self):
   404     def clear(self):
   371         self.map = {}
   405         self.map = {}
   372         self.dirty = 1
   406         self.markdirty()
   373 
   407 
   374     def write(self):
   408     def write(self):
   375         st = self.opener("dirstate", "w")
   409         st = self.opener("dirstate", "w")
   376         st.write("".join(self.pl))
   410         st.write("".join(self.pl))
   377         for f, e in self.map.items():
   411         for f, e in self.map.items():
   380                 f = f + "\0" + c
   414                 f = f + "\0" + c
   381             e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
   415             e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
   382             st.write(e + f)
   416             st.write(e + f)
   383         self.dirty = 0
   417         self.dirty = 0
   384 
   418 
   385     def changes(self, files, ignore):
   419     def walk(self, files = None, match = always):
   386         self.read()
   420         self.read()
   387         dc = self.map.copy()
   421         dc = self.map.copy()
   388         lookup, changed, added, unknown = [], [], [], []
   422         # walk all files by default
   389 
       
   390         # compare all files by default
       
   391         if not files: files = [self.root]
   423         if not files: files = [self.root]
   392 
   424         def traverse():
   393         # recursive generator of all files listed
       
   394         def walk(files):
       
   395             for f in util.unique(files):
   425             for f in util.unique(files):
   396                 f = os.path.join(self.root, f)
   426                 f = os.path.join(self.root, f)
   397                 if os.path.isdir(f):
   427                 if os.path.isdir(f):
   398                     for dir, subdirs, fl in os.walk(f):
   428                     for dir, subdirs, fl in os.walk(f):
   399                         d = dir[len(self.root) + 1:]
   429                         d = dir[len(self.root) + 1:]
       
   430                         if d == '.hg':
       
   431                             subdirs[:] = []
       
   432                             continue
   400                         for sd in subdirs:
   433                         for sd in subdirs:
   401                             if ignore(os.path.join(d, sd +'/')):
   434                             ds = os.path.join(d, sd +'/')
       
   435                             if self.ignore(ds) or not match(ds):
   402                                 subdirs.remove(sd)
   436                                 subdirs.remove(sd)
   403                         for fn in fl:
   437                         for fn in fl:
   404                             fn = util.pconvert(os.path.join(d, fn))
   438                             fn = util.pconvert(os.path.join(d, fn))
   405                             yield fn
   439                             yield fn
   406                 else:
   440                 else:
   407                     yield f[len(self.root) + 1:]
   441                     yield f[len(self.root) + 1:]
   408 
   442 
   409             for k in dc.keys():
   443             for k in dc.keys():
   410                 yield k
   444                 yield k
   411 
   445 
   412         for fn in util.unique(walk(files)):
   446         # yield only files that match: all in dirstate, others only if
       
   447         # not in .hgignore
       
   448 
       
   449         for fn in util.unique(traverse()):
       
   450             if fn in dc:
       
   451                 del dc[fn]
       
   452             elif self.ignore(fn):
       
   453                 continue
       
   454             if match(fn):
       
   455                 yield fn
       
   456 
       
   457     def changes(self, files = None, match = always):
       
   458         self.read()
       
   459         dc = self.map.copy()
       
   460         lookup, changed, added, unknown = [], [], [], []
       
   461 
       
   462         for fn in self.walk(files, match):
   413             try: s = os.stat(os.path.join(self.root, fn))
   463             try: s = os.stat(os.path.join(self.root, fn))
   414             except: continue
   464             except: continue
   415 
   465 
   416             if fn in dc:
   466             if fn in dc:
   417                 c = dc[fn]
   467                 c = dc[fn]
   426                 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
   476                 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
   427                     changed.append(fn)
   477                     changed.append(fn)
   428                 elif c[1] != s.st_mode or c[3] != s.st_mtime:
   478                 elif c[1] != s.st_mode or c[3] != s.st_mtime:
   429                     lookup.append(fn)
   479                     lookup.append(fn)
   430             else:
   480             else:
   431                 if not ignore(fn): unknown.append(fn)
   481                 if match(fn): unknown.append(fn)
   432 
   482 
   433         return (lookup, changed, added, dc.keys(), unknown)
   483         return (lookup, changed, added, dc.keys(), unknown)
   434 
   484 
   435 # used to avoid circular references so destructors work
   485 # used to avoid circular references so destructors work
   436 def opener(base):
   486 def opener(base):
   490 
   540 
   491         self.opener = opener(self.path)
   541         self.opener = opener(self.path)
   492         self.wopener = opener(self.root)
   542         self.wopener = opener(self.root)
   493         self.manifest = manifest(self.opener)
   543         self.manifest = manifest(self.opener)
   494         self.changelog = changelog(self.opener)
   544         self.changelog = changelog(self.opener)
   495         self.ignorefunc = None
       
   496         self.tagscache = None
   545         self.tagscache = None
   497         self.nodetagscache = None
   546         self.nodetagscache = None
   498 
   547 
   499         if not self.remote:
   548         if not self.remote:
   500             self.dirstate = dirstate(self.opener, ui, self.root)
   549             self.dirstate = dirstate(self.opener, ui, self.root)
   501             try:
   550             try:
   502                 self.ui.readconfig(self.opener("hgrc"))
   551                 self.ui.readconfig(self.opener("hgrc"))
   503             except IOError: pass
   552             except IOError: pass
   504 
       
   505     def ignore(self, f):
       
   506         if not self.ignorefunc:
       
   507             bigpat = ["^.hg/$"]
       
   508             try:
       
   509                 l = file(self.wjoin(".hgignore"))
       
   510                 for pat in l:
       
   511                     if pat != "\n":
       
   512                         p = util.pconvert(pat[:-1])
       
   513                         try:
       
   514                             r = re.compile(p)
       
   515                         except:
       
   516                             self.ui.warn("ignoring invalid ignore"
       
   517                                          + " regular expression '%s'\n" % p)
       
   518                         else:
       
   519                             bigpat.append(util.pconvert(pat[:-1]))
       
   520             except IOError: pass
       
   521 
       
   522             s = "(?:%s)" % (")|(?:".join(bigpat))
       
   523             r = re.compile(s)
       
   524             self.ignorefunc = r.search
       
   525 
       
   526         return self.ignorefunc(f)
       
   527 
   553 
   528     def hook(self, name, **args):
   554     def hook(self, name, **args):
   529         s = self.ui.config("hooks", name)
   555         s = self.ui.config("hooks", name)
   530         if s:
   556         if s:
   531             self.ui.note("running hook %s: %s\n" % (name, s))
   557             self.ui.note("running hook %s: %s\n" % (name, s))
   735                 elif s == 'r':
   761                 elif s == 'r':
   736                     remove.append(f)
   762                     remove.append(f)
   737                 else:
   763                 else:
   738                     self.ui.warn("%s not tracked!\n" % f)
   764                     self.ui.warn("%s not tracked!\n" % f)
   739         else:
   765         else:
   740             (c, a, d, u) = self.changes(None, None)
   766             (c, a, d, u) = self.changes()
   741             commit = c + a
   767             commit = c + a
   742             remove = d
   768             remove = d
   743 
   769 
   744         if not commit and not remove:
   770         if not commit and not remove:
   745             self.ui.status("nothing changed\n")
   771             self.ui.status("nothing changed\n")
   812         self.dirstate.forget(remove)
   838         self.dirstate.forget(remove)
   813 
   839 
   814         if not self.hook("commit", node=hex(n)):
   840         if not self.hook("commit", node=hex(n)):
   815             return 1
   841             return 1
   816 
   842 
   817     def changes(self, node1, node2, files=None):
   843     def walk(self, rev = None, files = [], match = always):
       
   844         if rev is None: fns = self.dirstate.walk(files, match)
       
   845         else: fns = filter(match, self.manifest.read(rev))
       
   846         for fn in fns: yield fn
       
   847 
       
   848     def changes(self, node1 = None, node2 = None, files = [], match = always):
   818         mf2, u = None, []
   849         mf2, u = None, []
   819 
   850 
   820         def fcmp(fn, mf):
   851         def fcmp(fn, mf):
   821             t1 = self.wfile(fn).read()
   852             t1 = self.wfile(fn).read()
   822             t2 = self.file(fn).revision(mf[fn])
   853             t2 = self.file(fn).revision(mf[fn])
   823             return cmp(t1, t2)
   854             return cmp(t1, t2)
   824 
   855 
       
   856         def mfmatches(node):
       
   857             mf = dict(self.manifest.read(node))
       
   858             for fn in mf.keys():
       
   859                 if not match(fn):
       
   860                     del mf[fn]
       
   861             return mf
       
   862             
   825         # are we comparing the working directory?
   863         # are we comparing the working directory?
   826         if not node2:
   864         if not node2:
   827             l, c, a, d, u = self.dirstate.changes(files, self.ignore)
   865             l, c, a, d, u = self.dirstate.changes(files, match)
   828 
   866 
   829             # are we comparing working dir against its parent?
   867             # are we comparing working dir against its parent?
   830             if not node1:
   868             if not node1:
   831                 if l:
   869                 if l:
   832                     # do a full compare of any files that might have changed
   870                     # do a full compare of any files that might have changed
   833                     change = self.changelog.read(self.dirstate.parents()[0])
   871                     change = self.changelog.read(self.dirstate.parents()[0])
   834                     mf2 = self.manifest.read(change[0])
   872                     mf2 = mfmatches(change[0])
   835                     for f in l:
   873                     for f in l:
   836                         if fcmp(f, mf2):
   874                         if fcmp(f, mf2):
   837                             c.append(f)
   875                             c.append(f)
   838 
   876 
   839                 for l in c, a, d, u:
   877                 for l in c, a, d, u:
   844         # are we comparing working dir against non-tip?
   882         # are we comparing working dir against non-tip?
   845         # generate a pseudo-manifest for the working dir
   883         # generate a pseudo-manifest for the working dir
   846         if not node2:
   884         if not node2:
   847             if not mf2:
   885             if not mf2:
   848                 change = self.changelog.read(self.dirstate.parents()[0])
   886                 change = self.changelog.read(self.dirstate.parents()[0])
   849                 mf2 = self.manifest.read(change[0]).copy()
   887                 mf2 = mfmatches(change[0])
   850             for f in a + c + l:
   888             for f in a + c + l:
   851                 mf2[f] = ""
   889                 mf2[f] = ""
   852             for f in d:
   890             for f in d:
   853                 if f in mf2: del mf2[f]
   891                 if f in mf2: del mf2[f]
   854         else:
   892         else:
   855             change = self.changelog.read(node2)
   893             change = self.changelog.read(node2)
   856             mf2 = self.manifest.read(change[0])
   894             mf2 = mfmatches(change[0])
   857 
   895 
   858         # flush lists from dirstate before comparing manifests
   896         # flush lists from dirstate before comparing manifests
   859         c, a = [], []
   897         c, a = [], []
   860 
   898 
   861         change = self.changelog.read(node1)
   899         change = self.changelog.read(node1)
   862         mf1 = self.manifest.read(change[0]).copy()
   900         mf1 = mfmatches(change[0])
   863 
   901 
   864         for fn in mf2:
   902         for fn in mf2:
   865             if mf1.has_key(fn):
   903             if mf1.has_key(fn):
   866                 if mf1[fn] != mf2[fn]:
   904                 if mf1[fn] != mf2[fn]:
   867                     if mf2[fn] != "" or fcmp(fn, mf1):
   905                     if mf2[fn] != "" or fcmp(fn, mf1):
  1265         m2 = self.manifest.read(m2n)
  1303         m2 = self.manifest.read(m2n)
  1266         mf2 = self.manifest.readflags(m2n)
  1304         mf2 = self.manifest.readflags(m2n)
  1267         ma = self.manifest.read(man)
  1305         ma = self.manifest.read(man)
  1268         mfa = self.manifest.readflags(man)
  1306         mfa = self.manifest.readflags(man)
  1269 
  1307 
  1270         (c, a, d, u) = self.changes(None, None)
  1308         (c, a, d, u) = self.changes()
  1271 
  1309 
  1272         # is this a jump, or a merge?  i.e. is there a linear path
  1310         # is this a jump, or a merge?  i.e. is there a linear path
  1273         # from p1 to p2?
  1311         # from p1 to p2?
  1274         linear_path = (pa == p1 or pa == p2)
  1312         linear_path = (pa == p1 or pa == p2)
  1275 
  1313