mercurial/localrepo.py
changeset 1782 b9671b41e360
parent 1781 284fc722c342
parent 1754 fdfe89a3962d
child 1784 2e0a288ca93e
child 1787 e431344e604c
equal deleted inserted replaced
1781:284fc722c342 1782:b9671b41e360
    46         try:
    46         try:
    47             self.ui.readconfig(self.join("hgrc"))
    47             self.ui.readconfig(self.join("hgrc"))
    48         except IOError:
    48         except IOError:
    49             pass
    49             pass
    50 
    50 
    51     def hook(self, name, **args):
    51     def hook(self, name, throw=False, **args):
    52         def runhook(name, cmd):
    52         def runhook(name, cmd):
    53             self.ui.note(_("running hook %s: %s\n") % (name, cmd))
    53             self.ui.note(_("running hook %s: %s\n") % (name, cmd))
    54             old = {}
    54             old = {}
    55             for k, v in args.items():
    55             for k, v in args.items():
    56                 k = k.upper()
    56                 k = k.upper()
       
    57                 old['HG_' + k] = os.environ.get(k, None)
    57                 old[k] = os.environ.get(k, None)
    58                 old[k] = os.environ.get(k, None)
    58                 os.environ[k] = v
    59                 os.environ['HG_' + k] = str(v)
    59 
    60                 os.environ[k] = str(v)
    60             # Hooks run in the repository root
    61 
    61             olddir = os.getcwd()
    62             try:
    62             os.chdir(self.root)
    63                 # Hooks run in the repository root
    63             r = os.system(cmd)
    64                 olddir = os.getcwd()
    64             os.chdir(olddir)
    65                 os.chdir(self.root)
    65 
    66                 r = os.system(cmd)
    66             for k, v in old.items():
    67             finally:
    67                 if v != None:
    68                 for k, v in old.items():
    68                     os.environ[k] = v
    69                     if v is not None:
    69                 else:
    70                         os.environ[k] = v
    70                     del os.environ[k]
    71                     else:
       
    72                         del os.environ[k]
       
    73 
       
    74                 os.chdir(olddir)
    71 
    75 
    72             if r:
    76             if r:
    73                 self.ui.warn(_("abort: %s hook failed with status %d!\n") %
    77                 desc, r = util.explain_exit(r)
    74                              (name, r))
    78                 if throw:
       
    79                     raise util.Abort(_('%s hook %s') % (name, desc))
       
    80                 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
    75                 return False
    81                 return False
    76             return True
    82             return True
    77 
    83 
    78         r = True
    84         r = True
    79         for hname, cmd in self.ui.configitems("hooks"):
    85         for hname, cmd in self.ui.configitems("hooks"):
   223 
   229 
   224         return transaction.transaction(self.ui.warn, self.opener,
   230         return transaction.transaction(self.ui.warn, self.opener,
   225                                        self.join("journal"), after)
   231                                        self.join("journal"), after)
   226 
   232 
   227     def recover(self):
   233     def recover(self):
   228         lock = self.lock()
   234         l = self.lock()
   229         if os.path.exists(self.join("journal")):
   235         if os.path.exists(self.join("journal")):
   230             self.ui.status(_("rolling back interrupted transaction\n"))
   236             self.ui.status(_("rolling back interrupted transaction\n"))
   231             transaction.rollback(self.opener, self.join("journal"))
   237             transaction.rollback(self.opener, self.join("journal"))
   232             self.manifest = manifest.manifest(self.opener)
   238             self.manifest = manifest.manifest(self.opener)
   233             self.changelog = changelog.changelog(self.opener)
   239             self.changelog = changelog.changelog(self.opener)
   237             return False
   243             return False
   238 
   244 
   239     def undo(self, wlock=None):
   245     def undo(self, wlock=None):
   240         if not wlock:
   246         if not wlock:
   241             wlock = self.wlock()
   247             wlock = self.wlock()
   242         lock = self.lock()
   248         l = self.lock()
   243         if os.path.exists(self.join("undo")):
   249         if os.path.exists(self.join("undo")):
   244             self.ui.status(_("rolling back last transaction\n"))
   250             self.ui.status(_("rolling back last transaction\n"))
   245             transaction.rollback(self.opener, self.join("undo"))
   251             transaction.rollback(self.opener, self.join("undo"))
   246             util.rename(self.join("undo.dirstate"), self.join("dirstate"))
   252             util.rename(self.join("undo.dirstate"), self.join("dirstate"))
   247             self.dirstate.read()
   253             self.dirstate.read()
   248         else:
   254         else:
   249             self.ui.warn(_("no undo information available\n"))
   255             self.ui.warn(_("no undo information available\n"))
   250 
   256 
   251     def lock(self, wait=1):
   257     def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
   252         try:
   258         try:
   253             return lock.lock(self.join("lock"), 0)
   259             l = lock.lock(self.join(lockname), 0, releasefn)
   254         except lock.LockHeld, inst:
       
   255             if wait:
       
   256                 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
       
   257                 return lock.lock(self.join("lock"), wait)
       
   258             raise inst
       
   259 
       
   260     def wlock(self, wait=1):
       
   261         try:
       
   262             wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
       
   263         except lock.LockHeld, inst:
   260         except lock.LockHeld, inst:
   264             if not wait:
   261             if not wait:
   265                 raise inst
   262                 raise inst
   266             self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
   263             self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
   267             wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
   264             l = lock.lock(self.join(lockname), wait, releasefn)
   268         self.dirstate.read()
   265         if acquirefn:
   269         return wlock
   266             acquirefn()
       
   267         return l
       
   268 
       
   269     def lock(self, wait=1):
       
   270         return self.do_lock("lock", wait)
       
   271 
       
   272     def wlock(self, wait=1):
       
   273         return self.do_lock("wlock", wait,
       
   274                             self.dirstate.write,
       
   275                             self.dirstate.read)
       
   276 
       
   277     def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
       
   278         "determine whether a new filenode is needed"
       
   279         fp1 = manifest1.get(filename, nullid)
       
   280         fp2 = manifest2.get(filename, nullid)
       
   281 
       
   282         if fp2 != nullid:
       
   283             # is one parent an ancestor of the other?
       
   284             fpa = filelog.ancestor(fp1, fp2)
       
   285             if fpa == fp1:
       
   286                 fp1, fp2 = fp2, nullid
       
   287             elif fpa == fp2:
       
   288                 fp2 = nullid
       
   289 
       
   290             # is the file unmodified from the parent? report existing entry
       
   291             if fp2 == nullid and text == filelog.read(fp1):
       
   292                 return (fp1, None, None)
       
   293 
       
   294         return (None, fp1, fp2)
   270 
   295 
   271     def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
   296     def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
   272         orig_parent = self.dirstate.parents()[0] or nullid
   297         orig_parent = self.dirstate.parents()[0] or nullid
   273         p1 = p1 or self.dirstate.parents()[0] or nullid
   298         p1 = p1 or self.dirstate.parents()[0] or nullid
   274         p2 = p2 or self.dirstate.parents()[1] or nullid
   299         p2 = p2 or self.dirstate.parents()[1] or nullid
   284         else:
   309         else:
   285             update_dirstate = 0
   310             update_dirstate = 0
   286 
   311 
   287         if not wlock:
   312         if not wlock:
   288             wlock = self.wlock()
   313             wlock = self.wlock()
   289         lock = self.lock()
   314         l = self.lock()
   290         tr = self.transaction()
   315         tr = self.transaction()
   291         mm = m1.copy()
   316         mm = m1.copy()
   292         mfm = mf1.copy()
   317         mfm = mf1.copy()
   293         linkrev = self.changelog.count()
   318         linkrev = self.changelog.count()
   294         for f in files:
   319         for f in files:
   296                 t = self.wread(f)
   321                 t = self.wread(f)
   297                 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
   322                 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
   298                 r = self.file(f)
   323                 r = self.file(f)
   299                 mfm[f] = tm
   324                 mfm[f] = tm
   300 
   325 
   301                 fp1 = m1.get(f, nullid)
   326                 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
   302                 fp2 = m2.get(f, nullid)
   327                 if entry:
   303 
   328                     mm[f] = entry
   304                 # is the same revision on two branches of a merge?
   329                     continue
   305                 if fp2 == fp1:
       
   306                     fp2 = nullid
       
   307 
       
   308                 if fp2 != nullid:
       
   309                     # is one parent an ancestor of the other?
       
   310                     fpa = r.ancestor(fp1, fp2)
       
   311                     if fpa == fp1:
       
   312                         fp1, fp2 = fp2, nullid
       
   313                     elif fpa == fp2:
       
   314                         fp2 = nullid
       
   315 
       
   316                     # is the file unmodified from the parent?
       
   317                     if t == r.read(fp1):
       
   318                         # record the proper existing parent in manifest
       
   319                         # no need to add a revision
       
   320                         mm[f] = fp1
       
   321                         continue
       
   322 
   330 
   323                 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
   331                 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
   324                 changed.append(f)
   332                 changed.append(f)
   325                 if update_dirstate:
   333                 if update_dirstate:
   326                     self.dirstate.update([f], "n")
   334                     self.dirstate.update([f], "n")
   370 
   378 
   371         if not commit and not remove and not force and p2 == nullid:
   379         if not commit and not remove and not force and p2 == nullid:
   372             self.ui.status(_("nothing changed\n"))
   380             self.ui.status(_("nothing changed\n"))
   373             return None
   381             return None
   374 
   382 
   375         if not self.hook("precommit"):
   383         xp1 = hex(p1)
   376             return None
   384         if p2 == nullid: xp2 = ''
       
   385         else: xp2 = hex(p2)
       
   386 
       
   387         self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
   377 
   388 
   378         if not wlock:
   389         if not wlock:
   379             wlock = self.wlock()
   390             wlock = self.wlock()
   380         lock = self.lock()
   391         l = self.lock()
   381         tr = self.transaction()
   392         tr = self.transaction()
   382 
   393 
   383         # check in files
   394         # check in files
   384         new = {}
   395         new = {}
   385         linkrev = self.changelog.count()
   396         linkrev = self.changelog.count()
   401                 meta["copy"] = cp
   412                 meta["copy"] = cp
   402                 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
   413                 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
   403                 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
   414                 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
   404                 fp1, fp2 = nullid, nullid
   415                 fp1, fp2 = nullid, nullid
   405             else:
   416             else:
   406                 fp1 = m1.get(f, nullid)
   417                 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
   407                 fp2 = m2.get(f, nullid)
   418                 if entry:
   408 
   419                     new[f] = entry
   409             if fp2 != nullid:
       
   410                 # is one parent an ancestor of the other?
       
   411                 fpa = r.ancestor(fp1, fp2)
       
   412                 if fpa == fp1:
       
   413                     fp1, fp2 = fp2, nullid
       
   414                 elif fpa == fp2:
       
   415                     fp2 = nullid
       
   416 
       
   417                 # is the file unmodified from the parent?
       
   418                 if not meta and t == r.read(fp1) and fp2 == nullid:
       
   419                     # record the proper existing parent in manifest
       
   420                     # no need to add a revision
       
   421                     new[f] = fp1
       
   422                     continue
   420                     continue
   423 
   421 
   424             new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
   422             new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
   425             # remember what we've added so that we can later calculate
   423             # remember what we've added so that we can later calculate
   426             # the files to pull from a set of changesets
   424             # the files to pull from a set of changesets
   457                 return None
   455                 return None
   458             text = edittext
   456             text = edittext
   459 
   457 
   460         user = user or self.ui.username()
   458         user = user or self.ui.username()
   461         n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
   459         n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
       
   460         self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
       
   461                   parent2=xp2)
   462         tr.close()
   462         tr.close()
   463 
   463 
   464         self.dirstate.setparents(n)
   464         self.dirstate.setparents(n)
   465         self.dirstate.update(new, "n")
   465         self.dirstate.update(new, "n")
   466         self.dirstate.forget(remove)
   466         self.dirstate.forget(remove)
   467 
   467 
   468         if not self.hook("commit", node=hex(n)):
   468         self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
   469             return None
       
   470         return n
   469         return n
   471 
   470 
   472     def walk(self, node=None, files=[], match=util.always):
   471     def walk(self, node=None, files=[], match=util.always):
   473         if node:
   472         if node:
   474             fdict = dict.fromkeys(files)
   473             fdict = dict.fromkeys(files)
   507         # are we comparing the working directory?
   506         # are we comparing the working directory?
   508         if not node2:
   507         if not node2:
   509             if not wlock:
   508             if not wlock:
   510                 try:
   509                 try:
   511                     wlock = self.wlock(wait=0)
   510                     wlock = self.wlock(wait=0)
   512                 except lock.LockHeld:
   511                 except lock.LockException:
   513                     wlock = None
   512                     wlock = None
   514             lookup, modified, added, removed, deleted, unknown = (
   513             lookup, modified, added, removed, deleted, unknown = (
   515                 self.dirstate.changes(files, match))
   514                 self.dirstate.changes(files, match))
   516 
   515 
   517             # are we comparing working dir against its parent?
   516             # are we comparing working dir against its parent?
   596         for f in list:
   595         for f in list:
   597             p = self.wjoin(f)
   596             p = self.wjoin(f)
   598             if os.path.exists(p):
   597             if os.path.exists(p):
   599                 self.ui.warn(_("%s still exists!\n") % f)
   598                 self.ui.warn(_("%s still exists!\n") % f)
   600             elif self.dirstate.state(f) == 'a':
   599             elif self.dirstate.state(f) == 'a':
   601                 self.ui.warn(_("%s never committed!\n") % f)
       
   602                 self.dirstate.forget([f])
   600                 self.dirstate.forget([f])
   603             elif f not in self.dirstate:
   601             elif f not in self.dirstate:
   604                 self.ui.warn(_("%s not tracked!\n") % f)
   602                 self.ui.warn(_("%s not tracked!\n") % f)
   605             else:
   603             else:
   606                 self.dirstate.update([f], "r")
   604                 self.dirstate.update([f], "r")
   931 
   929 
   932         # this is the set of all roots we have to push
   930         # this is the set of all roots we have to push
   933         return subset
   931         return subset
   934 
   932 
   935     def pull(self, remote, heads=None):
   933     def pull(self, remote, heads=None):
   936         lock = self.lock()
   934         l = self.lock()
   937 
   935 
   938         # if we have an empty repo, fetch everything
   936         # if we have an empty repo, fetch everything
   939         if self.changelog.tip() == nullid:
   937         if self.changelog.tip() == nullid:
   940             self.ui.status(_("requesting all changes\n"))
   938             self.ui.status(_("requesting all changes\n"))
   941             fetch = [nullid]
   939             fetch = [nullid]
   945         if not fetch:
   943         if not fetch:
   946             self.ui.status(_("no changes found\n"))
   944             self.ui.status(_("no changes found\n"))
   947             return 1
   945             return 1
   948 
   946 
   949         if heads is None:
   947         if heads is None:
   950             cg = remote.changegroup(fetch)
   948             cg = remote.changegroup(fetch, 'pull')
   951         else:
   949         else:
   952             cg = remote.changegroupsubset(fetch, heads)
   950             cg = remote.changegroupsubset(fetch, heads, 'pull')
   953         return self.addchangegroup(cg)
   951         return self.addchangegroup(cg)
   954 
   952 
   955     def push(self, remote, force=False, revs=None):
   953     def push(self, remote, force=False, revs=None):
   956         lock = remote.lock()
   954         lock = remote.lock()
   957 
   955 
   978                 self.ui.status(_("(did you forget to merge?"
   976                 self.ui.status(_("(did you forget to merge?"
   979                                  " use push -f to force)\n"))
   977                                  " use push -f to force)\n"))
   980                 return 1
   978                 return 1
   981 
   979 
   982         if revs is None:
   980         if revs is None:
   983             cg = self.changegroup(update)
   981             cg = self.changegroup(update, 'push')
   984         else:
   982         else:
   985             cg = self.changegroupsubset(update, revs)
   983             cg = self.changegroupsubset(update, revs, 'push')
   986         return remote.addchangegroup(cg)
   984         return remote.addchangegroup(cg)
   987 
   985 
   988     def changegroupsubset(self, bases, heads):
   986     def changegroupsubset(self, bases, heads, source):
   989         """This function generates a changegroup consisting of all the nodes
   987         """This function generates a changegroup consisting of all the nodes
   990         that are descendents of any of the bases, and ancestors of any of
   988         that are descendents of any of the bases, and ancestors of any of
   991         the heads.
   989         the heads.
   992 
   990 
   993         It is fairly complex as determining which filenodes and which
   991         It is fairly complex as determining which filenodes and which
   994         manifest nodes need to be included for the changeset to be complete
   992         manifest nodes need to be included for the changeset to be complete
   995         is non-trivial.
   993         is non-trivial.
   996 
   994 
   997         Another wrinkle is doing the reverse, figuring out which changeset in
   995         Another wrinkle is doing the reverse, figuring out which changeset in
   998         the changegroup a particular filenode or manifestnode belongs to."""
   996         the changegroup a particular filenode or manifestnode belongs to."""
       
   997 
       
   998         self.hook('preoutgoing', throw=True, source=source)
   999 
   999 
  1000         # Set up some initial variables
  1000         # Set up some initial variables
  1001         # Make it easy to refer to self.changelog
  1001         # Make it easy to refer to self.changelog
  1002         cl = self.changelog
  1002         cl = self.changelog
  1003         # msng is short for missing - compute the list of changesets in this
  1003         # msng is short for missing - compute the list of changesets in this
  1247                     # Don't need this anymore, toss it to free memory.
  1247                     # Don't need this anymore, toss it to free memory.
  1248                     del msng_filenode_set[fname]
  1248                     del msng_filenode_set[fname]
  1249             # Signal that no more groups are left.
  1249             # Signal that no more groups are left.
  1250             yield struct.pack(">l", 0)
  1250             yield struct.pack(">l", 0)
  1251 
  1251 
       
  1252             self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
       
  1253 
  1252         return util.chunkbuffer(gengroup())
  1254         return util.chunkbuffer(gengroup())
  1253 
  1255 
  1254     def changegroup(self, basenodes):
  1256     def changegroup(self, basenodes, source):
  1255         """Generate a changegroup of all nodes that we have that a recipient
  1257         """Generate a changegroup of all nodes that we have that a recipient
  1256         doesn't.
  1258         doesn't.
  1257 
  1259 
  1258         This is much easier than the previous function as we can assume that
  1260         This is much easier than the previous function as we can assume that
  1259         the recipient has any changenode we aren't sending them."""
  1261         the recipient has any changenode we aren't sending them."""
       
  1262 
       
  1263         self.hook('preoutgoing', throw=True, source=source)
       
  1264 
  1260         cl = self.changelog
  1265         cl = self.changelog
  1261         nodes = cl.nodesbetween(basenodes, None)[0]
  1266         nodes = cl.nodesbetween(basenodes, None)[0]
  1262         revset = dict.fromkeys([cl.rev(n) for n in nodes])
  1267         revset = dict.fromkeys([cl.rev(n) for n in nodes])
  1263 
  1268 
  1264         def identity(x):
  1269         def identity(x):
  1306                     lookup = lookuprevlink_func(filerevlog)
  1311                     lookup = lookuprevlink_func(filerevlog)
  1307                     for chnk in filerevlog.group(nodeiter, lookup):
  1312                     for chnk in filerevlog.group(nodeiter, lookup):
  1308                         yield chnk
  1313                         yield chnk
  1309 
  1314 
  1310             yield struct.pack(">l", 0)
  1315             yield struct.pack(">l", 0)
       
  1316             self.hook('outgoing', node=hex(nodes[0]), source=source)
  1311 
  1317 
  1312         return util.chunkbuffer(gengroup())
  1318         return util.chunkbuffer(gengroup())
  1313 
  1319 
  1314     def addchangegroup(self, source):
  1320     def addchangegroup(self, source):
  1315 
  1321 
  1341         def revmap(x):
  1347         def revmap(x):
  1342             return self.changelog.rev(x)
  1348             return self.changelog.rev(x)
  1343 
  1349 
  1344         if not source:
  1350         if not source:
  1345             return
  1351             return
       
  1352 
       
  1353         self.hook('prechangegroup', throw=True)
       
  1354 
  1346         changesets = files = revisions = 0
  1355         changesets = files = revisions = 0
  1347 
  1356 
  1348         tr = self.transaction()
  1357         tr = self.transaction()
  1349 
  1358 
  1350         oldheads = len(self.changelog.heads())
  1359         oldheads = len(self.changelog.heads())
  1383 
  1392 
  1384         self.ui.status(_("added %d changesets"
  1393         self.ui.status(_("added %d changesets"
  1385                          " with %d changes to %d files%s\n")
  1394                          " with %d changes to %d files%s\n")
  1386                          % (changesets, revisions, files, heads))
  1395                          % (changesets, revisions, files, heads))
  1387 
  1396 
       
  1397         self.hook('pretxnchangegroup', throw=True,
       
  1398                   node=hex(self.changelog.node(cor+1)))
       
  1399 
  1388         tr.close()
  1400         tr.close()
  1389 
  1401 
  1390         if changesets > 0:
  1402         if changesets > 0:
  1391             if not self.hook("changegroup",
  1403             self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
  1392                              node=hex(self.changelog.node(cor+1))):
       
  1393                 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
       
  1394                 return 1
       
  1395 
  1404 
  1396             for i in range(cor + 1, cnr + 1):
  1405             for i in range(cor + 1, cnr + 1):
  1397                 self.hook("incoming", node=hex(self.changelog.node(i)))
  1406                 self.hook("incoming", node=hex(self.changelog.node(i)))
  1398 
       
  1399         return
       
  1400 
  1407 
  1401     def update(self, node, allow=False, force=False, choose=None,
  1408     def update(self, node, allow=False, force=False, choose=None,
  1402                moddirstate=True, forcemerge=False, wlock=None):
  1409                moddirstate=True, forcemerge=False, wlock=None):
  1403         pl = self.dirstate.parents()
  1410         pl = self.dirstate.parents()
  1404         if not force and pl[1] != nullid:
  1411         if not force and pl[1] != nullid: