mercurial/localrepo.py
changeset 1680 c21b54f7f7b8
parent 1679 675ca845c2f8
parent 1674 dee55c4a4963
child 1706 20b621154e17
equal deleted inserted replaced
1679:675ca845c2f8 1680:c21b54f7f7b8
    17         if not path:
    17         if not path:
    18             p = os.getcwd()
    18             p = os.getcwd()
    19             while not os.path.isdir(os.path.join(p, ".hg")):
    19             while not os.path.isdir(os.path.join(p, ".hg")):
    20                 oldp = p
    20                 oldp = p
    21                 p = os.path.dirname(p)
    21                 p = os.path.dirname(p)
    22                 if p == oldp: raise repo.RepoError(_("no repo found"))
    22                 if p == oldp:
       
    23                     raise repo.RepoError(_("no repo found"))
    23             path = p
    24             path = p
    24         self.path = os.path.join(path, ".hg")
    25         self.path = os.path.join(path, ".hg")
    25 
    26 
    26         if not create and not os.path.isdir(self.path):
    27         if not create and not os.path.isdir(self.path):
    27             raise repo.RepoError(_("repository %s not found") % path)
    28             raise repo.RepoError(_("repository %s not found") % path)
    42             os.mkdir(self.join("data"))
    43             os.mkdir(self.join("data"))
    43 
    44 
    44         self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
    45         self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
    45         try:
    46         try:
    46             self.ui.readconfig(self.join("hgrc"))
    47             self.ui.readconfig(self.join("hgrc"))
    47         except IOError: pass
    48         except IOError:
       
    49             pass
    48 
    50 
    49     def hook(self, name, **args):
    51     def hook(self, name, **args):
    50         def runhook(name, cmd):
    52         def runhook(name, cmd):
    51             self.ui.note(_("running hook %s: %s\n") % (name, cmd))
    53             self.ui.note(_("running hook %s: %s\n") % (name, cmd))
    52             old = {}
    54             old = {}
   124         for t, n in self.tags().items():
   126         for t, n in self.tags().items():
   125             try:
   127             try:
   126                 r = self.changelog.rev(n)
   128                 r = self.changelog.rev(n)
   127             except:
   129             except:
   128                 r = -2 # sort to the beginning of the list if unknown
   130                 r = -2 # sort to the beginning of the list if unknown
   129             l.append((r,t,n))
   131             l.append((r, t, n))
   130         l.sort()
   132         l.sort()
   131         return [(t,n) for r,t,n in l]
   133         return [(t, n) for r, t, n in l]
   132 
   134 
   133     def nodetags(self, node):
   135     def nodetags(self, node):
   134         '''return the tags associated with a node'''
   136         '''return the tags associated with a node'''
   135         if not self.nodetagscache:
   137         if not self.nodetagscache:
   136             self.nodetagscache = {}
   138             self.nodetagscache = {}
   137             for t,n in self.tags().items():
   139             for t, n in self.tags().items():
   138                 self.nodetagscache.setdefault(n,[]).append(t)
   140                 self.nodetagscache.setdefault(n, []).append(t)
   139         return self.nodetagscache.get(node, [])
   141         return self.nodetagscache.get(node, [])
   140 
   142 
   141     def lookup(self, key):
   143     def lookup(self, key):
   142         try:
   144         try:
   143             return self.tags()[key]
   145             return self.tags()[key]
   158 
   160 
   159     def wjoin(self, f):
   161     def wjoin(self, f):
   160         return os.path.join(self.root, f)
   162         return os.path.join(self.root, f)
   161 
   163 
   162     def file(self, f):
   164     def file(self, f):
   163         if f[0] == '/': f = f[1:]
   165         if f[0] == '/':
       
   166             f = f[1:]
   164         return filelog.filelog(self.opener, f)
   167         return filelog.filelog(self.opener, f)
   165 
   168 
   166     def getcwd(self):
   169     def getcwd(self):
   167         return self.dirstate.getcwd()
   170         return self.dirstate.getcwd()
   168 
   171 
   224     def recover(self):
   227     def recover(self):
   225         lock = self.lock()
   228         lock = self.lock()
   226         if os.path.exists(self.join("journal")):
   229         if os.path.exists(self.join("journal")):
   227             self.ui.status(_("rolling back interrupted transaction\n"))
   230             self.ui.status(_("rolling back interrupted transaction\n"))
   228             transaction.rollback(self.opener, self.join("journal"))
   231             transaction.rollback(self.opener, self.join("journal"))
       
   232             self.manifest = manifest.manifest(self.opener)
       
   233             self.changelog = changelog.changelog(self.opener)
   229             return True
   234             return True
   230         else:
   235         else:
   231             self.ui.warn(_("no interrupted transaction available\n"))
   236             self.ui.warn(_("no interrupted transaction available\n"))
   232             return False
   237             return False
   233 
   238 
   332         n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
   337         n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
   333         tr.close()
   338         tr.close()
   334         if update_dirstate:
   339         if update_dirstate:
   335             self.dirstate.setparents(n, nullid)
   340             self.dirstate.setparents(n, nullid)
   336 
   341 
   337     def commit(self, files = None, text = "", user = None, date = None,
   342     def commit(self, files=None, text="", user=None, date=None,
   338                match = util.always, force=False):
   343                match=util.always, force=False):
   339         commit = []
   344         commit = []
   340         remove = []
   345         remove = []
   341         changed = []
   346         changed = []
   342 
   347 
   343         if files:
   348         if files:
   348                 elif s == 'r':
   353                 elif s == 'r':
   349                     remove.append(f)
   354                     remove.append(f)
   350                 else:
   355                 else:
   351                     self.ui.warn(_("%s not tracked!\n") % f)
   356                     self.ui.warn(_("%s not tracked!\n") % f)
   352         else:
   357         else:
   353             (c, a, d, u) = self.changes(match=match)
   358             modified, added, removed, deleted, unknown = self.changes(match=match)
   354             commit = c + a
   359             commit = modified + added
   355             remove = d
   360             remove = removed
   356 
   361 
   357         p1, p2 = self.dirstate.parents()
   362         p1, p2 = self.dirstate.parents()
   358         c1 = self.changelog.read(p1)
   363         c1 = self.changelog.read(p1)
   359         c2 = self.changelog.read(p2)
   364         c2 = self.changelog.read(p2)
   360         m1 = self.manifest.read(c1[0])
   365         m1 = self.manifest.read(c1[0])
   396                 fp1, fp2 = nullid, nullid
   401                 fp1, fp2 = nullid, nullid
   397             else:
   402             else:
   398                 fp1 = m1.get(f, nullid)
   403                 fp1 = m1.get(f, nullid)
   399                 fp2 = m2.get(f, nullid)
   404                 fp2 = m2.get(f, nullid)
   400 
   405 
   401             # is the same revision on two branches of a merge?
       
   402             if fp2 == fp1:
       
   403                 fp2 = nullid
       
   404 
       
   405             if fp2 != nullid:
   406             if fp2 != nullid:
   406                 # is one parent an ancestor of the other?
   407                 # is one parent an ancestor of the other?
   407                 fpa = r.ancestor(fp1, fp2)
   408                 fpa = r.ancestor(fp1, fp2)
   408                 if fpa == fp1:
   409                 if fpa == fp1:
   409                     fp1, fp2 = fp2, nullid
   410                     fp1, fp2 = fp2, nullid
   410                 elif fpa == fp2:
   411                 elif fpa == fp2:
   411                     fp2 = nullid
   412                     fp2 = nullid
   412 
   413 
   413                 # is the file unmodified from the parent?
   414                 # is the file unmodified from the parent?
   414                 if not meta and t == r.read(fp1):
   415                 if not meta and t == r.read(fp1) and fp2 == nullid:
   415                     # record the proper existing parent in manifest
   416                     # record the proper existing parent in manifest
   416                     # no need to add a revision
   417                     # no need to add a revision
   417                     new[f] = fp1
   418                     new[f] = fp1
   418                     continue
   419                     continue
   419 
   420 
   421             # remember what we've added so that we can later calculate
   422             # remember what we've added so that we can later calculate
   422             # the files to pull from a set of changesets
   423             # the files to pull from a set of changesets
   423             changed.append(f)
   424             changed.append(f)
   424 
   425 
   425         # update manifest
   426         # update manifest
       
   427         m1 = m1.copy()
   426         m1.update(new)
   428         m1.update(new)
   427         for f in remove:
   429         for f in remove:
   428             if f in m1:
   430             if f in m1:
   429                 del m1[f]
   431                 del m1[f]
   430         mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
   432         mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
   447             if not edittext.rstrip():
   449             if not edittext.rstrip():
   448                 return None
   450                 return None
   449             text = edittext
   451             text = edittext
   450 
   452 
   451         user = user or self.ui.username()
   453         user = user or self.ui.username()
   452         n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
   454         n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
   453         tr.close()
   455         tr.close()
   454 
   456 
   455         self.dirstate.setparents(n)
   457         self.dirstate.setparents(n)
   456         self.dirstate.update(new, "n")
   458         self.dirstate.update(new, "n")
   457         self.dirstate.forget(remove)
   459         self.dirstate.forget(remove)
   472                     util.pathto(self.getcwd(), fn), short(node)))
   474                     util.pathto(self.getcwd(), fn), short(node)))
   473         else:
   475         else:
   474             for src, fn in self.dirstate.walk(files, match):
   476             for src, fn in self.dirstate.walk(files, match):
   475                 yield src, fn
   477                 yield src, fn
   476 
   478 
   477     def changes(self, node1 = None, node2 = None, files = [],
   479     def changes(self, node1=None, node2=None, files=[], match=util.always):
   478                 match = util.always):
   480         """return changes between two nodes or node and working directory
   479         mf2, u = None, []
   481 
       
   482         If node1 is None, use the first dirstate parent instead.
       
   483         If node2 is None, compare node1 with working directory.
       
   484         """
   480 
   485 
   481         def fcmp(fn, mf):
   486         def fcmp(fn, mf):
   482             t1 = self.wread(fn)
   487             t1 = self.wread(fn)
   483             t2 = self.file(fn).read(mf.get(fn, nullid))
   488             t2 = self.file(fn).read(mf.get(fn, nullid))
   484             return cmp(t1, t2)
   489             return cmp(t1, t2)
   485 
   490 
   486         def mfmatches(node):
   491         def mfmatches(node):
   487             mf = dict(self.manifest.read(node))
   492             change = self.changelog.read(node)
       
   493             mf = dict(self.manifest.read(change[0]))
   488             for fn in mf.keys():
   494             for fn in mf.keys():
   489                 if not match(fn):
   495                 if not match(fn):
   490                     del mf[fn]
   496                     del mf[fn]
   491             return mf
   497             return mf
   492 
   498 
   494         if not node2:
   500         if not node2:
   495             try:
   501             try:
   496                 wlock = self.wlock(wait=0)
   502                 wlock = self.wlock(wait=0)
   497             except lock.LockHeld:
   503             except lock.LockHeld:
   498                 wlock = None
   504                 wlock = None
   499             l, c, a, d, u = self.dirstate.changes(files, match)
   505             lookup, modified, added, removed, deleted, unknown = (
       
   506                 self.dirstate.changes(files, match))
   500 
   507 
   501             # are we comparing working dir against its parent?
   508             # are we comparing working dir against its parent?
   502             if not node1:
   509             if not node1:
   503                 if l:
   510                 if lookup:
   504                     # do a full compare of any files that might have changed
   511                     # do a full compare of any files that might have changed
   505                     change = self.changelog.read(self.dirstate.parents()[0])
   512                     mf2 = mfmatches(self.dirstate.parents()[0])
   506                     mf2 = mfmatches(change[0])
   513                     for f in lookup:
   507                     for f in l:
       
   508                         if fcmp(f, mf2):
   514                         if fcmp(f, mf2):
   509                             c.append(f)
   515                             modified.append(f)
   510                         elif wlock is not None:
   516                         elif wlock is not None:
   511                             self.dirstate.update([f], "n")
   517                             self.dirstate.update([f], "n")
   512 
   518             else:
   513                 for l in c, a, d, u:
   519                 # we are comparing working dir against non-parent
   514                     l.sort()
   520                 # generate a pseudo-manifest for the working dir
   515 
   521                 mf2 = mfmatches(self.dirstate.parents()[0])
   516                 return (c, a, d, u)
   522                 for f in lookup + modified + added:
   517 
   523                     mf2[f] = ""
   518         # are we comparing working dir against non-tip?
   524                 for f in removed:
   519         # generate a pseudo-manifest for the working dir
   525                     if f in mf2:
   520         if not node2:
   526                         del mf2[f]
   521             if not mf2:
       
   522                 change = self.changelog.read(self.dirstate.parents()[0])
       
   523                 mf2 = mfmatches(change[0])
       
   524             for f in a + c + l:
       
   525                 mf2[f] = ""
       
   526             for f in d:
       
   527                 if f in mf2: del mf2[f]
       
   528         else:
   527         else:
   529             change = self.changelog.read(node2)
   528             # we are comparing two revisions
   530             mf2 = mfmatches(change[0])
   529             deleted, unknown = [], []
   531 
   530             mf2 = mfmatches(node2)
   532         # flush lists from dirstate before comparing manifests
   531 
   533         c, a = [], []
   532         if node1:
   534 
   533             # flush lists from dirstate before comparing manifests
   535         change = self.changelog.read(node1)
   534             modified, added = [], []
   536         mf1 = mfmatches(change[0])
   535 
   537 
   536             mf1 = mfmatches(node1)
   538         for fn in mf2:
   537 
   539             if mf1.has_key(fn):
   538             for fn in mf2:
   540                 if mf1[fn] != mf2[fn]:
   539                 if mf1.has_key(fn):
   541                     if mf2[fn] != "" or fcmp(fn, mf1):
   540                     if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
   542                         c.append(fn)
   541                         modified.append(fn)
   543                 del mf1[fn]
   542                     del mf1[fn]
   544             else:
   543                 else:
   545                 a.append(fn)
   544                     added.append(fn)
   546 
   545 
   547         d = mf1.keys()
   546             removed = mf1.keys()
   548 
   547 
   549         for l in c, a, d, u:
   548         # sort and return results:
       
   549         for l in modified, added, removed, deleted, unknown:
   550             l.sort()
   550             l.sort()
   551 
   551         return (modified, added, removed, deleted, unknown)
   552         return (c, a, d, u)
       
   553 
   552 
   554     def add(self, list):
   553     def add(self, list):
   555         wlock = self.wlock()
   554         wlock = self.wlock()
   556         for f in list:
   555         for f in list:
   557             p = self.wjoin(f)
   556             p = self.wjoin(f)
   558             if not os.path.exists(p):
   557             if not os.path.exists(p):
   559                 self.ui.warn(_("%s does not exist!\n") % f)
   558                 self.ui.warn(_("%s does not exist!\n") % f)
   560             elif not os.path.isfile(p):
   559             elif not os.path.isfile(p):
   561                 self.ui.warn(_("%s not added: only files supported currently\n") % f)
   560                 self.ui.warn(_("%s not added: only files supported currently\n")
       
   561                              % f)
   562             elif self.dirstate.state(f) in 'an':
   562             elif self.dirstate.state(f) in 'an':
   563                 self.ui.warn(_("%s already tracked!\n") % f)
   563                 self.ui.warn(_("%s already tracked!\n") % f)
   564             else:
   564             else:
   565                 self.dirstate.update([f], "a")
   565                 self.dirstate.update([f], "a")
   566 
   566 
   576         if unlink:
   576         if unlink:
   577             for f in list:
   577             for f in list:
   578                 try:
   578                 try:
   579                     util.unlink(self.wjoin(f))
   579                     util.unlink(self.wjoin(f))
   580                 except OSError, inst:
   580                 except OSError, inst:
   581                     if inst.errno != errno.ENOENT: raise
   581                     if inst.errno != errno.ENOENT:
       
   582                         raise
   582         wlock = self.wlock()
   583         wlock = self.wlock()
   583         for f in list:
   584         for f in list:
   584             p = self.wjoin(f)
   585             p = self.wjoin(f)
   585             if os.path.exists(p):
   586             if os.path.exists(p):
   586                 self.ui.warn(_("%s still exists!\n") % f)
   587                 self.ui.warn(_("%s still exists!\n") % f)
   731                     l = out.setdefault(h, [])
   732                     l = out.setdefault(h, [])
   732                     l[len(l):] = self.nodetags(b)
   733                     l[len(l):] = self.nodetags(b)
   733         return out
   734         return out
   734 
   735 
   735     def branches(self, nodes):
   736     def branches(self, nodes):
   736         if not nodes: nodes = [self.changelog.tip()]
   737         if not nodes:
       
   738             nodes = [self.changelog.tip()]
   737         b = []
   739         b = []
   738         for n in nodes:
   740         for n in nodes:
   739             t = n
   741             t = n
   740             while n:
   742             while n:
   741                 p = self.changelog.parents(n)
   743                 p = self.changelog.parents(n)
   803             while unknown:
   805             while unknown:
   804                 n = unknown.pop(0)
   806                 n = unknown.pop(0)
   805                 if n[0] in seen:
   807                 if n[0] in seen:
   806                     continue
   808                     continue
   807 
   809 
   808                 self.ui.debug(_("examining %s:%s\n") % (short(n[0]), short(n[1])))
   810                 self.ui.debug(_("examining %s:%s\n")
       
   811                               % (short(n[0]), short(n[1])))
   809                 if n[0] == nullid:
   812                 if n[0] == nullid:
   810                     break
   813                     break
   811                 if n in seenbranch:
   814                 if n in seenbranch:
   812                     self.ui.debug(_("branch already found\n"))
   815                     self.ui.debug(_("branch already found\n"))
   813                     continue
   816                     continue
   839                 for p in range(0, len(r), 10):
   842                 for p in range(0, len(r), 10):
   840                     for b in remote.branches(r[p:p+10]):
   843                     for b in remote.branches(r[p:p+10]):
   841                         self.ui.debug(_("received %s:%s\n") %
   844                         self.ui.debug(_("received %s:%s\n") %
   842                                       (short(b[0]), short(b[1])))
   845                                       (short(b[0]), short(b[1])))
   843                         if b[0] in m:
   846                         if b[0] in m:
   844                             self.ui.debug(_("found base node %s\n") % short(b[0]))
   847                             self.ui.debug(_("found base node %s\n")
       
   848                                           % short(b[0]))
   845                             base[b[0]] = 1
   849                             base[b[0]] = 1
   846                         elif b[0] not in seen:
   850                         elif b[0] not in seen:
   847                             unknown.append(b)
   851                             unknown.append(b)
   848 
   852 
   849         # do binary search on the branches we found
   853         # do binary search on the branches we found
   912                 subset.append(n)
   916                 subset.append(n)
   913 
   917 
   914         # this is the set of all roots we have to push
   918         # this is the set of all roots we have to push
   915         return subset
   919         return subset
   916 
   920 
   917     def pull(self, remote, heads = None):
   921     def pull(self, remote, heads=None):
   918         lock = self.lock()
   922         lock = self.lock()
   919 
   923 
   920         # if we have an empty repo, fetch everything
   924         # if we have an empty repo, fetch everything
   921         if self.changelog.tip() == nullid:
   925         if self.changelog.tip() == nullid:
   922             self.ui.status(_("requesting all changes\n"))
   926             self.ui.status(_("requesting all changes\n"))
  1197             # Go through all our files in order sorted by name.
  1201             # Go through all our files in order sorted by name.
  1198             for fname in changedfiles:
  1202             for fname in changedfiles:
  1199                 filerevlog = self.file(fname)
  1203                 filerevlog = self.file(fname)
  1200                 # Toss out the filenodes that the recipient isn't really
  1204                 # Toss out the filenodes that the recipient isn't really
  1201                 # missing.
  1205                 # missing.
  1202                 prune_filenodes(fname, filerevlog)
  1206                 if msng_filenode_set.has_key(fname):
  1203                 msng_filenode_lst = msng_filenode_set[fname].keys()
  1207                     prune_filenodes(fname, filerevlog)
       
  1208                     msng_filenode_lst = msng_filenode_set[fname].keys()
       
  1209                 else:
       
  1210                     msng_filenode_lst = []
  1204                 # If any filenodes are left, generate the group for them,
  1211                 # If any filenodes are left, generate the group for them,
  1205                 # otherwise don't bother.
  1212                 # otherwise don't bother.
  1206                 if len(msng_filenode_lst) > 0:
  1213                 if len(msng_filenode_lst) > 0:
  1207                     yield struct.pack(">l", len(fname) + 4) + fname
  1214                     yield struct.pack(">l", len(fname) + 4) + fname
  1208                     # Sort the filenodes by their revision #
  1215                     # Sort the filenodes by their revision #
  1212                     # from filenodes.
  1219                     # from filenodes.
  1213                     group = filerevlog.group(msng_filenode_lst,
  1220                     group = filerevlog.group(msng_filenode_lst,
  1214                                              lookup_filenode_link_func(fname))
  1221                                              lookup_filenode_link_func(fname))
  1215                     for chnk in group:
  1222                     for chnk in group:
  1216                         yield chnk
  1223                         yield chnk
  1217                 # Don't need this anymore, toss it to free memory.
  1224                 if msng_filenode_set.has_key(fname):
  1218                 del msng_filenode_set[fname]
  1225                     # Don't need this anymore, toss it to free memory.
       
  1226                     del msng_filenode_set[fname]
  1219             # Signal that no more groups are left.
  1227             # Signal that no more groups are left.
  1220             yield struct.pack(">l", 0)
  1228             yield struct.pack(">l", 0)
  1221 
  1229 
  1222         return util.chunkbuffer(gengroup())
  1230         return util.chunkbuffer(gengroup())
  1223 
  1231 
  1283 
  1291 
  1284     def addchangegroup(self, source):
  1292     def addchangegroup(self, source):
  1285 
  1293 
  1286         def getchunk():
  1294         def getchunk():
  1287             d = source.read(4)
  1295             d = source.read(4)
  1288             if not d: return ""
  1296             if not d:
       
  1297                 return ""
  1289             l = struct.unpack(">l", d)[0]
  1298             l = struct.unpack(">l", d)[0]
  1290             if l <= 4: return ""
  1299             if l <= 4:
       
  1300                 return ""
  1291             d = source.read(l - 4)
  1301             d = source.read(l - 4)
  1292             if len(d) < l - 4:
  1302             if len(d) < l - 4:
  1293                 raise repo.RepoError(_("premature EOF reading chunk"
  1303                 raise repo.RepoError(_("premature EOF reading chunk"
  1294                                        " (got %d bytes, expected %d)")
  1304                                        " (got %d bytes, expected %d)")
  1295                                      % (len(d), l - 4))
  1305                                      % (len(d), l - 4))
  1296             return d
  1306             return d
  1297 
  1307 
  1298         def getgroup():
  1308         def getgroup():
  1299             while 1:
  1309             while 1:
  1300                 c = getchunk()
  1310                 c = getchunk()
  1301                 if not c: break
  1311                 if not c:
       
  1312                     break
  1302                 yield c
  1313                 yield c
  1303 
  1314 
  1304         def csmap(x):
  1315         def csmap(x):
  1305             self.ui.debug(_("add changeset %s\n") % short(x))
  1316             self.ui.debug(_("add changeset %s\n") % short(x))
  1306             return self.changelog.count()
  1317             return self.changelog.count()
  1307 
  1318 
  1308         def revmap(x):
  1319         def revmap(x):
  1309             return self.changelog.rev(x)
  1320             return self.changelog.rev(x)
  1310 
  1321 
  1311         if not source: return
  1322         if not source:
       
  1323             return
  1312         changesets = files = revisions = 0
  1324         changesets = files = revisions = 0
  1313 
  1325 
  1314         tr = self.transaction()
  1326         tr = self.transaction()
  1315 
  1327 
  1316         oldheads = len(self.changelog.heads())
  1328         oldheads = len(self.changelog.heads())
  1331 
  1343 
  1332         # process the files
  1344         # process the files
  1333         self.ui.status(_("adding file changes\n"))
  1345         self.ui.status(_("adding file changes\n"))
  1334         while 1:
  1346         while 1:
  1335             f = getchunk()
  1347             f = getchunk()
  1336             if not f: break
  1348             if not f:
       
  1349                 break
  1337             self.ui.debug(_("adding %s revisions\n") % f)
  1350             self.ui.debug(_("adding %s revisions\n") % f)
  1338             fl = self.file(f)
  1351             fl = self.file(f)
  1339             o = fl.count()
  1352             o = fl.count()
  1340             n = fl.addgroup(getgroup(), revmap, tr)
  1353             n = fl.addgroup(getgroup(), revmap, tr)
  1341             revisions += fl.count() - o
  1354             revisions += fl.count() - o
  1352 
  1365 
  1353         tr.close()
  1366         tr.close()
  1354 
  1367 
  1355         if changesets > 0:
  1368         if changesets > 0:
  1356             if not self.hook("changegroup",
  1369             if not self.hook("changegroup",
  1357                               node=hex(self.changelog.node(cor+1))):
  1370                              node=hex(self.changelog.node(cor+1))):
  1358                 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
  1371                 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
  1359                 return 1
  1372                 return 1
  1360 
  1373 
  1361             for i in range(cor + 1, cnr + 1):
  1374             for i in range(cor + 1, cnr + 1):
  1362                 self.hook("commit", node=hex(self.changelog.node(i)))
  1375                 self.hook("commit", node=hex(self.changelog.node(i)))
  1367                moddirstate=True, forcemerge=False):
  1380                moddirstate=True, forcemerge=False):
  1368         pl = self.dirstate.parents()
  1381         pl = self.dirstate.parents()
  1369         if not force and pl[1] != nullid:
  1382         if not force and pl[1] != nullid:
  1370             self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
  1383             self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
  1371             return 1
  1384             return 1
       
  1385 
       
  1386         err = False
  1372 
  1387 
  1373         p1, p2 = pl[0], node
  1388         p1, p2 = pl[0], node
  1374         pa = self.changelog.ancestor(p1, p2)
  1389         pa = self.changelog.ancestor(p1, p2)
  1375         m1n = self.changelog.read(p1)[0]
  1390         m1n = self.changelog.read(p1)[0]
  1376         m2n = self.changelog.read(p2)[0]
  1391         m2n = self.changelog.read(p2)[0]
  1377         man = self.manifest.ancestor(m1n, m2n)
  1392         man = self.manifest.ancestor(m1n, m2n)
  1378         m1 = self.manifest.read(m1n)
  1393         m1 = self.manifest.read(m1n)
  1379         mf1 = self.manifest.readflags(m1n)
  1394         mf1 = self.manifest.readflags(m1n)
  1380         m2 = self.manifest.read(m2n)
  1395         m2 = self.manifest.read(m2n).copy()
  1381         mf2 = self.manifest.readflags(m2n)
  1396         mf2 = self.manifest.readflags(m2n)
  1382         ma = self.manifest.read(man)
  1397         ma = self.manifest.read(man)
  1383         mfa = self.manifest.readflags(man)
  1398         mfa = self.manifest.readflags(man)
  1384 
  1399 
  1385         (c, a, d, u) = self.changes()
  1400         modified, added, removed, deleted, unknown = self.changes()
  1386 
       
  1387         if allow and not forcemerge:
       
  1388             if c or a or d:
       
  1389                 raise util.Abort(_("outstanding uncommited changes"))
       
  1390         if not forcemerge and not force:
       
  1391             for f in u:
       
  1392                 if f in m2:
       
  1393                      t1 = self.wread(f)
       
  1394                      t2 = self.file(f).read(m2[f])
       
  1395                      if cmp(t1, t2) != 0:
       
  1396                         raise util.Abort(_("'%s' already exists in the working"
       
  1397                                            " dir and differs from remote") % f)
       
  1398 
  1401 
  1399         # is this a jump, or a merge?  i.e. is there a linear path
  1402         # is this a jump, or a merge?  i.e. is there a linear path
  1400         # from p1 to p2?
  1403         # from p1 to p2?
  1401         linear_path = (pa == p1 or pa == p2)
  1404         linear_path = (pa == p1 or pa == p2)
       
  1405 
       
  1406         if allow and linear_path:
       
  1407             raise util.Abort(_("there is nothing to merge, "
       
  1408                                "just use 'hg update'"))
       
  1409         if allow and not forcemerge:
       
  1410             if modified or added or removed:
       
  1411                 raise util.Abort(_("outstanding uncommited changes"))
       
  1412         if not forcemerge and not force:
       
  1413             for f in unknown:
       
  1414                 if f in m2:
       
  1415                     t1 = self.wread(f)
       
  1416                     t2 = self.file(f).read(m2[f])
       
  1417                     if cmp(t1, t2) != 0:
       
  1418                         raise util.Abort(_("'%s' already exists in the working"
       
  1419                                            " dir and differs from remote") % f)
  1402 
  1420 
  1403         # resolve the manifest to determine which files
  1421         # resolve the manifest to determine which files
  1404         # we care about merging
  1422         # we care about merging
  1405         self.ui.note(_("resolving manifests\n"))
  1423         self.ui.note(_("resolving manifests\n"))
  1406         self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
  1424         self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
  1413         remove = []
  1431         remove = []
  1414 
  1432 
  1415         # construct a working dir manifest
  1433         # construct a working dir manifest
  1416         mw = m1.copy()
  1434         mw = m1.copy()
  1417         mfw = mf1.copy()
  1435         mfw = mf1.copy()
  1418         umap = dict.fromkeys(u)
  1436         umap = dict.fromkeys(unknown)
  1419 
  1437 
  1420         for f in a + c + u:
  1438         for f in added + modified + unknown:
  1421             mw[f] = ""
  1439             mw[f] = ""
  1422             mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
  1440             mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
  1423 
  1441 
  1424         if moddirstate:
  1442         if moddirstate:
  1425             wlock = self.wlock()
  1443             wlock = self.wlock()
  1426 
  1444 
  1427         for f in d:
  1445         for f in deleted + removed:
  1428             if f in mw: del mw[f]
  1446             if f in mw:
       
  1447                 del mw[f]
  1429 
  1448 
  1430             # If we're jumping between revisions (as opposed to merging),
  1449             # If we're jumping between revisions (as opposed to merging),
  1431             # and if neither the working directory nor the target rev has
  1450             # and if neither the working directory nor the target rev has
  1432             # the file, then we need to remove it from the dirstate, to
  1451             # the file, then we need to remove it from the dirstate, to
  1433             # prevent the dirstate from listing the file when it is no
  1452             # prevent the dirstate from listing the file when it is no
  1435             if moddirstate and linear_path and f not in m2:
  1454             if moddirstate and linear_path and f not in m2:
  1436                 self.dirstate.forget((f,))
  1455                 self.dirstate.forget((f,))
  1437 
  1456 
  1438         # Compare manifests
  1457         # Compare manifests
  1439         for f, n in mw.iteritems():
  1458         for f, n in mw.iteritems():
  1440             if choose and not choose(f): continue
  1459             if choose and not choose(f):
       
  1460                 continue
  1441             if f in m2:
  1461             if f in m2:
  1442                 s = 0
  1462                 s = 0
  1443 
  1463 
  1444                 # is the wfile new since m1, and match m2?
  1464                 # is the wfile new since m1, and match m2?
  1445                 if f not in m1:
  1465                 if f not in m1:
  1478                         util.set_exec(self.wjoin(f), mf2[f])
  1498                         util.set_exec(self.wjoin(f), mf2[f])
  1479                     else:
  1499                     else:
  1480                         a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
  1500                         a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
  1481                         mode = ((a^b) | (a^c)) ^ a
  1501                         mode = ((a^b) | (a^c)) ^ a
  1482                         if mode != b:
  1502                         if mode != b:
  1483                             self.ui.debug(_(" updating permissions for %s\n") % f)
  1503                             self.ui.debug(_(" updating permissions for %s\n")
       
  1504                                           % f)
  1484                             util.set_exec(self.wjoin(f), mode)
  1505                             util.set_exec(self.wjoin(f), mode)
  1485                 del m2[f]
  1506                 del m2[f]
  1486             elif f in ma:
  1507             elif f in ma:
  1487                 if n != ma[f]:
  1508                 if n != ma[f]:
  1488                     r = _("d")
  1509                     r = _("d")
  1508                         self.ui.debug(_("local modified %s, keeping\n") % f)
  1529                         self.ui.debug(_("local modified %s, keeping\n") % f)
  1509                 else:
  1530                 else:
  1510                     self.ui.debug(_("working dir created %s, keeping\n") % f)
  1531                     self.ui.debug(_("working dir created %s, keeping\n") % f)
  1511 
  1532 
  1512         for f, n in m2.iteritems():
  1533         for f, n in m2.iteritems():
  1513             if choose and not choose(f): continue
  1534             if choose and not choose(f):
  1514             if f[0] == "/": continue
  1535                 continue
       
  1536             if f[0] == "/":
       
  1537                 continue
  1515             if f in ma and n != ma[f]:
  1538             if f in ma and n != ma[f]:
  1516                 r = _("k")
  1539                 r = _("k")
  1517                 if not force and (linear_path or allow):
  1540                 if not force and (linear_path or allow):
  1518                     r = self.ui.prompt(
  1541                     r = self.ui.prompt(
  1519                         (_("remote changed %s which local deleted\n") % f) +
  1542                         (_("remote changed %s which local deleted\n") % f) +
  1520                          _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
  1543                          _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
  1521                 if r == _("k"): get[f] = n
  1544                 if r == _("k"):
       
  1545                     get[f] = n
  1522             elif f not in ma:
  1546             elif f not in ma:
  1523                 self.ui.debug(_("remote created %s\n") % f)
  1547                 self.ui.debug(_("remote created %s\n") % f)
  1524                 get[f] = n
  1548                 get[f] = n
  1525             else:
  1549             else:
  1526                 if force or p2 == pa: # going backwards?
  1550                 if force or p2 == pa: # going backwards?
  1546                                  " affecting the following files:\n"))
  1570                                  " affecting the following files:\n"))
  1547                 fl = merge.keys() + get.keys()
  1571                 fl = merge.keys() + get.keys()
  1548                 fl.sort()
  1572                 fl.sort()
  1549                 for f in fl:
  1573                 for f in fl:
  1550                     cf = ""
  1574                     cf = ""
  1551                     if f in merge: cf = _(" (resolve)")
  1575                     if f in merge:
       
  1576                         cf = _(" (resolve)")
  1552                     self.ui.status(" %s%s\n" % (f, cf))
  1577                     self.ui.status(" %s%s\n" % (f, cf))
  1553                 self.ui.warn(_("aborting update spanning branches!\n"))
  1578                 self.ui.warn(_("aborting update spanning branches!\n"))
  1554                 self.ui.status(_("(use update -m to merge across branches"
  1579                 self.ui.status(_("(use update -m to merge across branches"
  1555                                  " or -C to lose changes)\n"))
  1580                                  " or -C to lose changes)\n"))
  1556                 return 1
  1581                 return 1
  1558 
  1583 
  1559         # get the files we don't need to change
  1584         # get the files we don't need to change
  1560         files = get.keys()
  1585         files = get.keys()
  1561         files.sort()
  1586         files.sort()
  1562         for f in files:
  1587         for f in files:
  1563             if f[0] == "/": continue
  1588             if f[0] == "/":
       
  1589                 continue
  1564             self.ui.note(_("getting %s\n") % f)
  1590             self.ui.note(_("getting %s\n") % f)
  1565             t = self.file(f).read(get[f])
  1591             t = self.file(f).read(get[f])
  1566             self.wwrite(f, t)
  1592             self.wwrite(f, t)
  1567             util.set_exec(self.wjoin(f), mf2[f])
  1593             util.set_exec(self.wjoin(f), mf2[f])
  1568             if moddirstate:
  1594             if moddirstate:
  1575         files = merge.keys()
  1601         files = merge.keys()
  1576         files.sort()
  1602         files.sort()
  1577         for f in files:
  1603         for f in files:
  1578             self.ui.status(_("merging %s\n") % f)
  1604             self.ui.status(_("merging %s\n") % f)
  1579             my, other, flag = merge[f]
  1605             my, other, flag = merge[f]
  1580             self.merge3(f, my, other)
  1606             ret = self.merge3(f, my, other)
       
  1607             if ret:
       
  1608                 err = True
  1581             util.set_exec(self.wjoin(f), flag)
  1609             util.set_exec(self.wjoin(f), flag)
  1582             if moddirstate:
  1610             if moddirstate:
  1583                 if branch_merge:
  1611                 if branch_merge:
  1584                     # We've done a branch merge, mark this file as merged
  1612                     # We've done a branch merge, mark this file as merged
  1585                     # so that we properly record the merger later
  1613                     # so that we properly record the merger later
  1608             else:
  1636             else:
  1609                 self.dirstate.forget(remove)
  1637                 self.dirstate.forget(remove)
  1610 
  1638 
  1611         if moddirstate:
  1639         if moddirstate:
  1612             self.dirstate.setparents(p1, p2)
  1640             self.dirstate.setparents(p1, p2)
       
  1641         return err
  1613 
  1642 
  1614     def merge3(self, fn, my, other):
  1643     def merge3(self, fn, my, other):
  1615         """perform a 3-way merge in the working directory"""
  1644         """perform a 3-way merge in the working directory"""
  1616 
  1645 
  1617         def temp(prefix, node):
  1646         def temp(prefix, node):
  1638         if r:
  1667         if r:
  1639             self.ui.warn(_("merging %s failed!\n") % fn)
  1668             self.ui.warn(_("merging %s failed!\n") % fn)
  1640 
  1669 
  1641         os.unlink(b)
  1670         os.unlink(b)
  1642         os.unlink(c)
  1671         os.unlink(c)
       
  1672         return r
  1643 
  1673 
  1644     def verify(self):
  1674     def verify(self):
  1645         filelinkrevs = {}
  1675         filelinkrevs = {}
  1646         filenodes = {}
  1676         filenodes = {}
  1647         changesets = revisions = files = 0
  1677         changesets = revisions = files = 0
  1650 
  1680 
  1651         def err(msg):
  1681         def err(msg):
  1652             self.ui.warn(msg + "\n")
  1682             self.ui.warn(msg + "\n")
  1653             errors[0] += 1
  1683             errors[0] += 1
  1654 
  1684 
       
  1685         def checksize(obj, name):
       
  1686             d = obj.checksize()
       
  1687             if d[0]:
       
  1688                 err(_("%s data length off by %d bytes") % (name, d[0]))
       
  1689             if d[1]:
       
  1690                 err(_("%s index contains %d extra bytes") % (name, d[1]))
       
  1691 
  1655         seen = {}
  1692         seen = {}
  1656         self.ui.status(_("checking changesets\n"))
  1693         self.ui.status(_("checking changesets\n"))
  1657         d = self.changelog.checksize()
  1694         checksize(self.changelog, "changelog")
  1658         if d:
  1695 
  1659             err(_("changeset data short %d bytes") % d)
       
  1660         for i in range(self.changelog.count()):
  1696         for i in range(self.changelog.count()):
  1661             changesets += 1
  1697             changesets += 1
  1662             n = self.changelog.node(i)
  1698             n = self.changelog.node(i)
  1663             l = self.changelog.linkrev(n)
  1699             l = self.changelog.linkrev(n)
  1664             if l != i:
  1700             if l != i:
  1684             for f in changes[3]:
  1720             for f in changes[3]:
  1685                 filelinkrevs.setdefault(f, []).append(i)
  1721                 filelinkrevs.setdefault(f, []).append(i)
  1686 
  1722 
  1687         seen = {}
  1723         seen = {}
  1688         self.ui.status(_("checking manifests\n"))
  1724         self.ui.status(_("checking manifests\n"))
  1689         d = self.manifest.checksize()
  1725         checksize(self.manifest, "manifest")
  1690         if d:
  1726 
  1691             err(_("manifest data short %d bytes") % d)
       
  1692         for i in range(self.manifest.count()):
  1727         for i in range(self.manifest.count()):
  1693             n = self.manifest.node(i)
  1728             n = self.manifest.node(i)
  1694             l = self.manifest.linkrev(n)
  1729             l = self.manifest.linkrev(n)
  1695 
  1730 
  1696             if l < 0 or l >= self.changelog.count():
  1731             if l < 0 or l >= self.changelog.count():
  1721             for f, fn in ff:
  1756             for f, fn in ff:
  1722                 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
  1757                 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
  1723 
  1758 
  1724         self.ui.status(_("crosschecking files in changesets and manifests\n"))
  1759         self.ui.status(_("crosschecking files in changesets and manifests\n"))
  1725 
  1760 
  1726         for m,c in neededmanifests.items():
  1761         for m, c in neededmanifests.items():
  1727             err(_("Changeset %s refers to unknown manifest %s") %
  1762             err(_("Changeset %s refers to unknown manifest %s") %
  1728                 (short(m), short(c)))
  1763                 (short(m), short(c)))
  1729         del neededmanifests
  1764         del neededmanifests
  1730 
  1765 
  1731         for f in filenodes:
  1766         for f in filenodes:
  1738 
  1773 
  1739         self.ui.status(_("checking files\n"))
  1774         self.ui.status(_("checking files\n"))
  1740         ff = filenodes.keys()
  1775         ff = filenodes.keys()
  1741         ff.sort()
  1776         ff.sort()
  1742         for f in ff:
  1777         for f in ff:
  1743             if f == "/dev/null": continue
  1778             if f == "/dev/null":
       
  1779                 continue
  1744             files += 1
  1780             files += 1
  1745             fl = self.file(f)
  1781             fl = self.file(f)
  1746             d = fl.checksize()
  1782             checksize(fl, f)
  1747             if d:
  1783 
  1748                 err(_("%s file data short %d bytes") % (f, d))
  1784             nodes = {nullid: 1}
  1749 
       
  1750             nodes = { nullid: 1 }
       
  1751             seen = {}
  1785             seen = {}
  1752             for i in range(fl.count()):
  1786             for i in range(fl.count()):
  1753                 revisions += 1
  1787                 revisions += 1
  1754                 n = fl.node(i)
  1788                 n = fl.node(i)
  1755 
  1789