mercurial/hg.py
changeset 220 3113a94c1bff
parent 217 e6d6497a6331
child 222 87484f627422
equal deleted inserted replaced
219:8ff4532376a4 220:3113a94c1bff
   147         list.sort()
   147         list.sort()
   148         l = [hex(manifest), user, date] + list + ["", desc]
   148         l = [hex(manifest), user, date] + list + ["", desc]
   149         text = "\n".join(l)
   149         text = "\n".join(l)
   150         return self.addrevision(text, transaction, self.count(), p1, p2)
   150         return self.addrevision(text, transaction, self.count(), p1, p2)
   151 
   151 
   152 class dircache:
   152 class dirstate:
   153     def __init__(self, opener, ui):
   153     def __init__(self, opener, ui):
   154         self.opener = opener
   154         self.opener = opener
   155         self.dirty = 0
   155         self.dirty = 0
   156         self.ui = ui
   156         self.ui = ui
   157         self.map = None
   157         self.map = None
       
   158 
   158     def __del__(self):
   159     def __del__(self):
   159         if self.dirty: self.write()
   160         if self.dirty:
       
   161             self.write()
       
   162 
   160     def __getitem__(self, key):
   163     def __getitem__(self, key):
   161         try:
   164         try:
   162             return self.map[key]
   165             return self.map[key]
   163         except TypeError:
   166         except TypeError:
   164             self.read()
   167             self.read()
   165             return self[key]
   168             return self[key]
   166         
   169 
       
   170     def __contains__(self, key):
       
   171         if not self.map: self.read()
       
   172         return key in self.map
       
   173 
       
   174     def state(self, key):
       
   175         try:
       
   176             return self[key][0]
       
   177         except KeyError:
       
   178             return "?"
       
   179 
   167     def read(self):
   180     def read(self):
   168         if self.map is not None: return self.map
   181         if self.map is not None: return self.map
   169 
   182 
   170         self.map = {}
   183         self.map = {}
   171         try:
   184         try:
   172             st = self.opener("dircache").read()
   185             st = self.opener("dirstate").read()
   173         except: return
   186         except: return
   174 
   187 
   175         pos = 0
   188         pos = 0
   176         while pos < len(st):
   189         while pos < len(st):
   177             e = struct.unpack(">llll", st[pos:pos+16])
   190             e = struct.unpack(">cllll", st[pos:pos+17])
   178             l = e[3]
   191             l = e[4]
   179             pos += 16
   192             pos += 17
   180             f = st[pos:pos + l]
   193             f = st[pos:pos + l]
   181             self.map[f] = e[:3]
   194             self.map[f] = e[:4]
   182             pos += l
   195             pos += l
   183         
   196         
   184     def update(self, files):
   197     def update(self, files, state):
       
   198         ''' current states:
       
   199         n  normal
       
   200         i  invalid
       
   201         r  marked for removal
       
   202         a  marked for addition'''
       
   203 
   185         if not files: return
   204         if not files: return
   186         self.read()
   205         self.read()
   187         self.dirty = 1
   206         self.dirty = 1
   188         for f in files:
   207         for f in files:
   189             try:
   208             if state == "r":
   190                 s = os.stat(f)
   209                 self.map[f] = ('r', 0, 0, 0)
   191                 self.map[f] = (s.st_mode, s.st_size, s.st_mtime)
   210             else:
   192             except IOError:
   211                 try:
   193                 self.remove(f)
   212                     s = os.stat(f)
   194 
   213                     self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
   195     def taint(self, files):
   214                 except OSError:
   196         if not files: return
   215                     if state != "i": raise
   197         self.read()
   216                     self.map[f] = ('r', 0, 0, 0)
   198         self.dirty = 1
   217 
   199         for f in files:
   218     def forget(self, files):
   200             self.map[f] = (0, -1, 0)
       
   201 
       
   202     def remove(self, files):
       
   203         if not files: return
   219         if not files: return
   204         self.read()
   220         self.read()
   205         self.dirty = 1
   221         self.dirty = 1
   206         for f in files:
   222         for f in files:
   207             try:
   223             try:
   208                 del self.map[f]
   224                 del self.map[f]
   209             except KeyError:
   225             except KeyError:
   210                 self.ui.warn("Not in dircache: %s\n" % f)
   226                 self.ui.warn("not in dirstate: %s!\n" % f)
   211                 pass
   227                 pass
   212 
   228 
   213     def clear(self):
   229     def clear(self):
   214         self.map = {}
   230         self.map = {}
   215         self.dirty = 1
   231         self.dirty = 1
   216 
   232 
   217     def write(self):
   233     def write(self):
   218         st = self.opener("dircache", "w")
   234         st = self.opener("dirstate", "w")
   219         for f, e in self.map.items():
   235         for f, e in self.map.items():
   220             e = struct.pack(">llll", e[0], e[1], e[2], len(f))
   236             e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
   221             st.write(e + f)
   237             st.write(e + f)
   222         self.dirty = 0
   238         self.dirty = 0
   223 
   239 
   224     def copy(self):
   240     def copy(self):
   225         self.read()
   241         self.read()
   278         self.changelog = changelog(self.opener)
   294         self.changelog = changelog(self.opener)
   279         self.ignorelist = None
   295         self.ignorelist = None
   280         self.tags = None
   296         self.tags = None
   281 
   297 
   282         if not self.remote:
   298         if not self.remote:
   283             self.dircache = dircache(self.opener, ui)
   299             self.dirstate = dirstate(self.opener, ui)
   284             try:
   300             try:
   285                 self.current = bin(self.opener("current").read())
   301                 self.current = bin(self.opener("current").read())
   286             except IOError:
   302             except IOError:
   287                 self.current = None
   303                 self.current = None
   288 
   304 
   338             self.ui.warn("no interrupted transaction available\n")
   354             self.ui.warn("no interrupted transaction available\n")
   339 
   355 
   340     def undo(self):
   356     def undo(self):
   341         self.lock()
   357         self.lock()
   342         if os.path.exists(self.join("undo")):
   358         if os.path.exists(self.join("undo")):
       
   359             f = self.changelog.read(self.changelog.tip())[3]
   343             self.ui.status("attempting to rollback last transaction\n")
   360             self.ui.status("attempting to rollback last transaction\n")
   344             rollback(self.opener, self.join("undo"))
   361             rollback(self.opener, self.join("undo"))
   345             self.manifest = manifest(self.opener)
   362             self.manifest = manifest(self.opener)
   346             self.changelog = changelog(self.opener)
   363             self.changelog = changelog(self.opener)
   347 
   364 
   348             self.ui.status("discarding dircache\n")
   365             self.ui.status("discarding dirstate\n")
   349             node = self.changelog.tip()
   366             node = self.changelog.tip()
   350             mf = self.changelog.read(node)[0]
       
   351             mm = self.manifest.read(mf)
       
   352             f = mm.keys()
       
   353             f.sort()
   367             f.sort()
   354 
   368 
   355             self.setcurrent(node)
   369             self.setcurrent(node)
   356             self.dircache.clear()
   370             self.dirstate.update(f, 'i')
   357             self.dircache.taint(f)
       
   358         
   371         
   359         else:
   372         else:
   360             self.ui.warn("no undo information available\n")
   373             self.ui.warn("no undo information available\n")
   361 
   374 
   362     def lock(self, wait = 1):
   375     def lock(self, wait = 1):
   387 
   400 
   388         mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
   401         mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
   389         n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
   402         n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
   390         tr.close()
   403         tr.close()
   391         self.setcurrent(n)
   404         self.setcurrent(n)
   392         self.dircache.clear()
   405         self.dirstate.clear()
   393         self.dircache.update(mmap)
   406         self.dirstate.update(mmap.keys(), "n")
   394 
   407 
   395     def commit(self, parent, update = None, text = ""):
   408     def commit(self, parent, files = None, text = ""):
   396         self.lock()
   409         self.lock()
   397         try:
   410 
   398             remove = [ l[:-1] for l in self.opener("to-remove") ]
   411         commit = []
   399             os.unlink(self.join("to-remove"))
   412         remove = []
   400 
   413         if files:
   401         except IOError:
   414             for f in files:
   402             remove = []
   415                 s = self.dirstate.state(f)
   403 
   416                 if s in 'cai':
   404         if update == None:
   417                     commit.append(f)
   405             update = self.diffdir(self.root, parent)[0]
   418                 elif s == 'r':
   406 
   419                     remove.append(f)
   407         if not update:
   420                 else:
       
   421                     self.warn("%s not tracked!\n")
       
   422         else:
       
   423             (c, a, d, u) = self.diffdir(self.root, parent)
       
   424             commit = c + a
       
   425             remove = d
       
   426 
       
   427         if not commit and not remove:
   408             self.ui.status("nothing changed\n")
   428             self.ui.status("nothing changed\n")
   409             return
   429             return
   410 
   430 
   411         tr = self.transaction()
   431         tr = self.transaction()
   412 
   432 
   413         # check in files
   433         # check in files
   414         new = {}
   434         new = {}
   415         linkrev = self.changelog.count()
   435         linkrev = self.changelog.count()
   416         update.sort()
   436         commit.sort()
   417         for f in update:
   437         for f in commit:
   418             self.ui.note(f + "\n")
   438             self.ui.note(f + "\n")
   419             try:
   439             try:
   420                 t = file(f).read()
   440                 t = file(f).read()
   421             except IOError:
   441             except IOError:
   422                 remove.append(f)
   442                 self.warn("trouble committing %s!\n" % f)
   423                 continue
   443                 raise
       
   444 
   424             r = self.file(f)
   445             r = self.file(f)
   425             new[f] = r.add(t, tr, linkrev)
   446             new[f] = r.add(t, tr, linkrev)
   426 
   447 
   427         # update manifest
   448         # update manifest
   428         mmap = self.manifest.read(self.manifest.tip())
   449         mmap = self.manifest.read(self.manifest.tip())
   442 
   463 
   443         n = self.changelog.add(mnode, new, edittext, tr)
   464         n = self.changelog.add(mnode, new, edittext, tr)
   444         tr.close()
   465         tr.close()
   445 
   466 
   446         self.setcurrent(n)
   467         self.setcurrent(n)
   447         self.dircache.update(new)
   468         self.dirstate.update(new, "n")
   448         self.dircache.remove(remove)
   469         self.dirstate.forget(remove)
   449 
   470 
   450     def checkout(self, node):
   471     def checkout(self, node):
   451         # checkout is really dumb at the moment
   472         # checkout is really dumb at the moment
   452         # it ought to basically merge
   473         # it ought to basically merge
   453         change = self.changelog.read(node)
   474         change = self.changelog.read(node)
   463             except IOError:
   484             except IOError:
   464                 os.makedirs(os.path.dirname(f))
   485                 os.makedirs(os.path.dirname(f))
   465                 file(f, "w").write(t)
   486                 file(f, "w").write(t)
   466 
   487 
   467         self.setcurrent(node)
   488         self.setcurrent(node)
   468         self.dircache.clear()
   489         self.dirstate.clear()
   469         self.dircache.update([f for f,n in l])
   490         self.dirstate.update([f for f,n in l], "n")
   470 
   491 
   471     def diffdir(self, path, changeset):
   492     def diffdir(self, path, changeset):
   472         changed = []
   493         changed = []
       
   494         added = []
       
   495         unknown = []
   473         mf = {}
   496         mf = {}
   474         added = []
       
   475 
   497 
   476         if changeset:
   498         if changeset:
   477             change = self.changelog.read(changeset)
   499             change = self.changelog.read(changeset)
   478             mf = self.manifest.read(change[0])
   500             mf = self.manifest.read(change[0])
   479 
   501 
   480         if changeset == self.current:
   502         if changeset == self.current:
   481             dc = self.dircache.copy()
   503             dc = self.dirstate.copy()
   482         else:
   504         else:
   483             dc = dict.fromkeys(mf)
   505             dc = dict.fromkeys(mf)
   484 
   506 
   485         def fcmp(fn):
   507         def fcmp(fn):
   486             t1 = file(os.path.join(self.root, fn)).read()
   508             t1 = file(os.path.join(self.root, fn)).read()
   496                 try: s = os.stat(os.path.join(self.root, fn))
   518                 try: s = os.stat(os.path.join(self.root, fn))
   497                 except: continue
   519                 except: continue
   498                 if fn in dc:
   520                 if fn in dc:
   499                     c = dc[fn]
   521                     c = dc[fn]
   500                     del dc[fn]
   522                     del dc[fn]
   501                     if not c or c[1] < 0:
   523                     if not c:
   502                         if fcmp(fn):
   524                         if fcmp(fn):
   503                             changed.append(fn)
   525                             changed.append(fn)
   504                     elif c[1] != s.st_size:
   526                     if c[0] == 'i':
       
   527                         if fn not in mf:
       
   528                             added.append(fn)
       
   529                         elif fcmp(fn):
       
   530                             changed.append(fn)
       
   531                     elif c[0] == 'a':
       
   532                         added.append(fn)
       
   533                     elif c[0] == 'r':
       
   534                         unknown.append(fn)
       
   535                     elif c[2] != s.st_size:
   505                         changed.append(fn)
   536                         changed.append(fn)
   506                     elif c[0] != s.st_mode or c[2] != s.st_mtime:
   537                     elif c[1] != s.st_mode or c[3] != s.st_mtime:
   507                         if fcmp(fn):
   538                         if fcmp(fn):
   508                             changed.append(fn)
   539                             changed.append(fn)
   509                 else:
   540                 else:
   510                     if self.ignore(fn): continue
   541                     if self.ignore(fn): continue
   511                     added.append(fn)
   542                     unknown.append(fn)
   512 
   543 
   513         deleted = dc.keys()
   544         deleted = dc.keys()
   514         deleted.sort()
   545         deleted.sort()
   515 
   546 
   516         return (changed, added, deleted)
   547         return (changed, added, deleted, unknown)
   517 
   548 
   518     def diffrevs(self, node1, node2):
   549     def diffrevs(self, node1, node2):
   519         changed, added = [], []
   550         changed, added = [], []
   520 
   551 
   521         change = self.changelog.read(node1)
   552         change = self.changelog.read(node1)
   535         deleted.sort()
   566         deleted.sort()
   536     
   567     
   537         return (changed, added, deleted)
   568         return (changed, added, deleted)
   538 
   569 
   539     def add(self, list):
   570     def add(self, list):
   540         self.dircache.taint(list)
   571         for f in list:
       
   572             p = os.path.join(self.root, f)
       
   573             if not os.path.isfile(p):
       
   574                 self.ui.warn("%s does not exist!\n" % f)
       
   575             elif self.dirstate.state(f) == 'n':
       
   576                 self.ui.warn("%s already tracked!\n" % f)
       
   577             else:
       
   578                 self.dirstate.update([f], "a")
       
   579 
       
   580     def forget(self, list):
       
   581         for f in list:
       
   582             if self.dirstate.state(f) not in 'ai':
       
   583                 self.ui.warn("%s not added!\n" % f)
       
   584             else:
       
   585                 self.dirstate.forget([f])
   541 
   586 
   542     def remove(self, list):
   587     def remove(self, list):
   543         dl = self.opener("to-remove", "a")
       
   544         for f in list:
   588         for f in list:
   545             dl.write(f + "\n")
   589             p = os.path.join(self.root, f)
       
   590             if os.path.isfile(p):
       
   591                 self.ui.warn("%s still exists!\n" % f)
       
   592             elif f not in self.dirstate:
       
   593                 self.ui.warn("%s not tracked!\n" % f)
       
   594             else:
       
   595                 self.dirstate.update([f], "r")
   546 
   596 
   547     def branches(self, nodes):
   597     def branches(self, nodes):
   548         if not nodes: nodes = [self.changelog.tip()]
   598         if not nodes: nodes = [self.changelog.tip()]
   549         b = []
   599         b = []
   550         for n in nodes:
   600         for n in nodes: