mercurial/localrepo.py
changeset 4915 97b734fb9c6f
parent 4914 9a2a73ea6135
child 4916 5c5d23d93447
equal deleted inserted replaced
4914:9a2a73ea6135 4915:97b734fb9c6f
   514         self.transhandle = tr
   514         self.transhandle = tr
   515         return tr
   515         return tr
   516 
   516 
   517     def recover(self):
   517     def recover(self):
   518         l = self.lock()
   518         l = self.lock()
   519         if os.path.exists(self.sjoin("journal")):
   519         try:
   520             self.ui.status(_("rolling back interrupted transaction\n"))
   520             if os.path.exists(self.sjoin("journal")):
   521             transaction.rollback(self.sopener, self.sjoin("journal"))
   521                 self.ui.status(_("rolling back interrupted transaction\n"))
   522             self.invalidate()
   522                 transaction.rollback(self.sopener, self.sjoin("journal"))
   523             return True
   523                 self.invalidate()
   524         else:
   524                 return True
   525             self.ui.warn(_("no interrupted transaction available\n"))
   525             else:
   526             return False
   526                 self.ui.warn(_("no interrupted transaction available\n"))
       
   527                 return False
       
   528         finally:
       
   529             del l
   527 
   530 
   528     def rollback(self, wlock=None, lock=None):
   531     def rollback(self, wlock=None, lock=None):
   529         if not wlock:
   532         try:
   530             wlock = self.wlock()
   533             if not wlock:
   531         if not lock:
   534                 wlock = self.wlock()
   532             lock = self.lock()
   535             if not lock:
   533         if os.path.exists(self.sjoin("undo")):
   536                 lock = self.lock()
   534             self.ui.status(_("rolling back last transaction\n"))
   537             if os.path.exists(self.sjoin("undo")):
   535             transaction.rollback(self.sopener, self.sjoin("undo"))
   538                 self.ui.status(_("rolling back last transaction\n"))
   536             util.rename(self.join("undo.dirstate"), self.join("dirstate"))
   539                 transaction.rollback(self.sopener, self.sjoin("undo"))
   537             self.invalidate()
   540                 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
   538             self.dirstate.invalidate()
   541                 self.invalidate()
   539         else:
   542                 self.dirstate.invalidate()
   540             self.ui.warn(_("no rollback information available\n"))
   543             else:
       
   544                 self.ui.warn(_("no rollback information available\n"))
       
   545         finally:
       
   546             del wlock, lock
   541 
   547 
   542     def invalidate(self):
   548     def invalidate(self):
   543         for a in "changelog manifest".split():
   549         for a in "changelog manifest".split():
   544             if hasattr(self, a):
   550             if hasattr(self, a):
   545                 self.__delattr__(a)
   551                 self.__delattr__(a)
   637                            p1=p1, p2=p2, wlock=wlock, extra=extra)
   643                            p1=p1, p2=p2, wlock=wlock, extra=extra)
   638 
   644 
   639     def commit(self, files=None, text="", user=None, date=None,
   645     def commit(self, files=None, text="", user=None, date=None,
   640                match=util.always, force=False, lock=None, wlock=None,
   646                match=util.always, force=False, lock=None, wlock=None,
   641                force_editor=False, p1=None, p2=None, extra={}):
   647                force_editor=False, p1=None, p2=None, extra={}):
   642 
   648         tr = None
   643         commit = []
   649         try:
   644         remove = []
   650             commit = []
   645         changed = []
   651             remove = []
   646         use_dirstate = (p1 is None) # not rawcommit
   652             changed = []
   647         extra = extra.copy()
   653             use_dirstate = (p1 is None) # not rawcommit
   648 
   654             extra = extra.copy()
   649         if use_dirstate:
   655 
   650             if files:
   656             if use_dirstate:
   651                 for f in files:
   657                 if files:
   652                     s = self.dirstate[f]
   658                     for f in files:
   653                     if s in 'nma':
   659                         s = self.dirstate[f]
   654                         commit.append(f)
   660                         if s in 'nma':
   655                     elif s == 'r':
   661                             commit.append(f)
       
   662                         elif s == 'r':
       
   663                             remove.append(f)
       
   664                         else:
       
   665                             self.ui.warn(_("%s not tracked!\n") % f)
       
   666                 else:
       
   667                     changes = self.status(match=match)[:5]
       
   668                     modified, added, removed, deleted, unknown = changes
       
   669                     commit = modified + added
       
   670                     remove = removed
       
   671             else:
       
   672                 commit = files
       
   673 
       
   674             if use_dirstate:
       
   675                 p1, p2 = self.dirstate.parents()
       
   676                 update_dirstate = True
       
   677             else:
       
   678                 p1, p2 = p1, p2 or nullid
       
   679                 update_dirstate = (self.dirstate.parents()[0] == p1)
       
   680 
       
   681             c1 = self.changelog.read(p1)
       
   682             c2 = self.changelog.read(p2)
       
   683             m1 = self.manifest.read(c1[0]).copy()
       
   684             m2 = self.manifest.read(c2[0])
       
   685 
       
   686             if use_dirstate:
       
   687                 branchname = self.workingctx().branch()
       
   688                 try:
       
   689                     branchname = branchname.decode('UTF-8').encode('UTF-8')
       
   690                 except UnicodeDecodeError:
       
   691                     raise util.Abort(_('branch name not in UTF-8!'))
       
   692             else:
       
   693                 branchname = ""
       
   694 
       
   695             if use_dirstate:
       
   696                 oldname = c1[5].get("branch") # stored in UTF-8
       
   697                 if (not commit and not remove and not force and p2 == nullid
       
   698                     and branchname == oldname):
       
   699                     self.ui.status(_("nothing changed\n"))
       
   700                     return None
       
   701 
       
   702             xp1 = hex(p1)
       
   703             if p2 == nullid: xp2 = ''
       
   704             else: xp2 = hex(p2)
       
   705 
       
   706             self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
       
   707 
       
   708             if not wlock:
       
   709                 wlock = self.wlock()
       
   710             if not lock:
       
   711                 lock = self.lock()
       
   712             tr = self.transaction()
       
   713 
       
   714             # check in files
       
   715             new = {}
       
   716             linkrev = self.changelog.count()
       
   717             commit.sort()
       
   718             is_exec = util.execfunc(self.root, m1.execf)
       
   719             is_link = util.linkfunc(self.root, m1.linkf)
       
   720             for f in commit:
       
   721                 self.ui.note(f + "\n")
       
   722                 try:
       
   723                     new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
       
   724                     new_exec = is_exec(f)
       
   725                     new_link = is_link(f)
       
   726                     if not changed or changed[-1] != f:
       
   727                         # mention the file in the changelog if some
       
   728                         # flag changed, even if there was no content
       
   729                         # change.
       
   730                         old_exec = m1.execf(f)
       
   731                         old_link = m1.linkf(f)
       
   732                         if old_exec != new_exec or old_link != new_link:
       
   733                             changed.append(f)
       
   734                     m1.set(f, new_exec, new_link)
       
   735                 except (OSError, IOError):
       
   736                     if use_dirstate:
       
   737                         self.ui.warn(_("trouble committing %s!\n") % f)
       
   738                         raise
       
   739                     else:
   656                         remove.append(f)
   740                         remove.append(f)
   657                     else:
   741 
   658                         self.ui.warn(_("%s not tracked!\n") % f)
   742             # update manifest
   659             else:
   743             m1.update(new)
   660                 changes = self.status(match=match)[:5]
   744             remove.sort()
   661                 modified, added, removed, deleted, unknown = changes
   745             removed = []
   662                 commit = modified + added
   746 
   663                 remove = removed
   747             for f in remove:
   664         else:
   748                 if f in m1:
   665             commit = files
   749                     del m1[f]
   666 
   750                     removed.append(f)
   667         if use_dirstate:
   751                 elif f in m2:
   668             p1, p2 = self.dirstate.parents()
   752                     removed.append(f)
   669             update_dirstate = True
   753             mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0],
   670         else:
   754                                    (new, removed))
   671             p1, p2 = p1, p2 or nullid
   755 
   672             update_dirstate = (self.dirstate.parents()[0] == p1)
   756             # add changeset
   673 
   757             new = new.keys()
   674         c1 = self.changelog.read(p1)
   758             new.sort()
   675         c2 = self.changelog.read(p2)
   759 
   676         m1 = self.manifest.read(c1[0]).copy()
   760             user = user or self.ui.username()
   677         m2 = self.manifest.read(c2[0])
   761             if not text or force_editor:
   678 
   762                 edittext = []
   679         if use_dirstate:
   763                 if text:
   680             branchname = self.workingctx().branch()
   764                     edittext.append(text)
   681             try:
   765                 edittext.append("")
   682                 branchname = branchname.decode('UTF-8').encode('UTF-8')
   766                 edittext.append("HG: user: %s" % user)
   683             except UnicodeDecodeError:
   767                 if p2 != nullid:
   684                 raise util.Abort(_('branch name not in UTF-8!'))
   768                     edittext.append("HG: branch merge")
   685         else:
   769                 if branchname:
   686             branchname = ""
   770                     edittext.append("HG: branch %s" % util.tolocal(branchname))
   687 
   771                 edittext.extend(["HG: changed %s" % f for f in changed])
   688         if use_dirstate:
   772                 edittext.extend(["HG: removed %s" % f for f in removed])
   689             oldname = c1[5].get("branch") # stored in UTF-8
   773                 if not changed and not remove:
   690             if (not commit and not remove and not force and p2 == nullid
   774                     edittext.append("HG: no files changed")
   691                 and branchname == oldname):
   775                 edittext.append("")
   692                 self.ui.status(_("nothing changed\n"))
   776                 # run editor in the repository root
       
   777                 olddir = os.getcwd()
       
   778                 os.chdir(self.root)
       
   779                 text = self.ui.edit("\n".join(edittext), user)
       
   780                 os.chdir(olddir)
       
   781 
       
   782             lines = [line.rstrip() for line in text.rstrip().splitlines()]
       
   783             while lines and not lines[0]:
       
   784                 del lines[0]
       
   785             if not lines:
   693                 return None
   786                 return None
   694 
   787             text = '\n'.join(lines)
   695         xp1 = hex(p1)
   788             if branchname:
   696         if p2 == nullid: xp2 = ''
   789                 extra["branch"] = branchname
   697         else: xp2 = hex(p2)
   790             n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
   698 
   791                                    user, date, extra)
   699         self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
   792             self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
   700 
   793                       parent2=xp2)
   701         if not wlock:
   794             tr.close()
   702             wlock = self.wlock()
   795 
   703         if not lock:
   796             if self.branchcache and "branch" in extra:
   704             lock = self.lock()
   797                 self.branchcache[util.tolocal(extra["branch"])] = n
   705         tr = self.transaction()
   798 
   706 
   799             if use_dirstate or update_dirstate:
   707         # check in files
   800                 self.dirstate.setparents(n)
   708         new = {}
       
   709         linkrev = self.changelog.count()
       
   710         commit.sort()
       
   711         is_exec = util.execfunc(self.root, m1.execf)
       
   712         is_link = util.linkfunc(self.root, m1.linkf)
       
   713         for f in commit:
       
   714             self.ui.note(f + "\n")
       
   715             try:
       
   716                 new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
       
   717                 new_exec = is_exec(f)
       
   718                 new_link = is_link(f)
       
   719                 if not changed or changed[-1] != f:
       
   720                     # mention the file in the changelog if some flag changed,
       
   721                     # even if there was no content change.
       
   722                     old_exec = m1.execf(f)
       
   723                     old_link = m1.linkf(f)
       
   724                     if old_exec != new_exec or old_link != new_link:
       
   725                         changed.append(f)
       
   726                 m1.set(f, new_exec, new_link)
       
   727             except (OSError, IOError):
       
   728                 if use_dirstate:
   801                 if use_dirstate:
   729                     self.ui.warn(_("trouble committing %s!\n") % f)
   802                     for f in new:
   730                     raise
   803                         self.dirstate.normal(f)
   731                 else:
   804                     for f in removed:
   732                     remove.append(f)
   805                         self.dirstate.forget(f)
   733 
   806 
   734         # update manifest
   807             self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
   735         m1.update(new)
   808             return n
   736         remove.sort()
   809         finally:
   737         removed = []
   810             del lock, wlock, tr
   738 
       
   739         for f in remove:
       
   740             if f in m1:
       
   741                 del m1[f]
       
   742                 removed.append(f)
       
   743             elif f in m2:
       
   744                 removed.append(f)
       
   745         mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed))
       
   746 
       
   747         # add changeset
       
   748         new = new.keys()
       
   749         new.sort()
       
   750 
       
   751         user = user or self.ui.username()
       
   752         if not text or force_editor:
       
   753             edittext = []
       
   754             if text:
       
   755                 edittext.append(text)
       
   756             edittext.append("")
       
   757             edittext.append("HG: user: %s" % user)
       
   758             if p2 != nullid:
       
   759                 edittext.append("HG: branch merge")
       
   760             if branchname:
       
   761                 edittext.append("HG: branch %s" % util.tolocal(branchname))
       
   762             edittext.extend(["HG: changed %s" % f for f in changed])
       
   763             edittext.extend(["HG: removed %s" % f for f in removed])
       
   764             if not changed and not remove:
       
   765                 edittext.append("HG: no files changed")
       
   766             edittext.append("")
       
   767             # run editor in the repository root
       
   768             olddir = os.getcwd()
       
   769             os.chdir(self.root)
       
   770             text = self.ui.edit("\n".join(edittext), user)
       
   771             os.chdir(olddir)
       
   772 
       
   773         lines = [line.rstrip() for line in text.rstrip().splitlines()]
       
   774         while lines and not lines[0]:
       
   775             del lines[0]
       
   776         if not lines:
       
   777             return None
       
   778         text = '\n'.join(lines)
       
   779         if branchname:
       
   780             extra["branch"] = branchname
       
   781         n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
       
   782                                user, date, extra)
       
   783         self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
       
   784                   parent2=xp2)
       
   785         tr.close()
       
   786 
       
   787         if self.branchcache and "branch" in extra:
       
   788             self.branchcache[util.tolocal(extra["branch"])] = n
       
   789 
       
   790         if use_dirstate or update_dirstate:
       
   791             self.dirstate.setparents(n)
       
   792             if use_dirstate:
       
   793                 for f in new:
       
   794                     self.dirstate.normal(f)
       
   795                 for f in removed:
       
   796                     self.dirstate.forget(f)
       
   797 
       
   798         self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
       
   799         return n
       
   800 
   811 
   801     def walk(self, node=None, files=[], match=util.always, badmatch=None):
   812     def walk(self, node=None, files=[], match=util.always, badmatch=None):
   802         '''
   813         '''
   803         walk recursively through the directory tree or a given
   814         walk recursively through the directory tree or a given
   804         changeset, finding all files matched by the match
   815         changeset, finding all files matched by the match
   893                             if list_clean:
   904                             if list_clean:
   894                                 clean.append(f)
   905                                 clean.append(f)
   895 
   906 
   896                     # update dirstate for files that are actually clean
   907                     # update dirstate for files that are actually clean
   897                     if fixup:
   908                     if fixup:
   898                         cleanup = False
   909                         fixlock = wlock
   899                         if not wlock:
   910                         try:
   900                             try:
   911                             if not fixlock:
   901                                 wlock = self.wlock(False)
   912                                 try:
   902                                 cleanup = True
   913                                     fixlock = self.wlock(False)
   903                             except lock.LockException:
   914                                 except lock.LockException:
   904                                 pass
   915                                     pass
   905                         if wlock:
   916                             if fixlock:
   906                             for f in fixup:
   917                                 for f in fixup:
   907                                 self.dirstate.normal(f)
   918                                     self.dirstate.normal(f)
   908                         if cleanup:
   919                         finally:
   909                                 wlock.release()
   920                             del fixlock
   910             else:
   921             else:
   911                 # we are comparing working dir against non-parent
   922                 # we are comparing working dir against non-parent
   912                 # generate a pseudo-manifest for the working dir
   923                 # generate a pseudo-manifest for the working dir
   913                 # XXX: create it in dirstate.py ?
   924                 # XXX: create it in dirstate.py ?
   914                 mf2 = mfmatches(self.dirstate.parents()[0])
   925                 mf2 = mfmatches(self.dirstate.parents()[0])
   952         for l in modified, added, removed, deleted, unknown, ignored, clean:
   963         for l in modified, added, removed, deleted, unknown, ignored, clean:
   953             l.sort()
   964             l.sort()
   954         return (modified, added, removed, deleted, unknown, ignored, clean)
   965         return (modified, added, removed, deleted, unknown, ignored, clean)
   955 
   966 
   956     def add(self, list, wlock=None):
   967     def add(self, list, wlock=None):
   957         if not wlock:
   968         try:
   958             wlock = self.wlock()
       
   959         for f in list:
       
   960             p = self.wjoin(f)
       
   961             try:
       
   962                 st = os.lstat(p)
       
   963             except:
       
   964                 self.ui.warn(_("%s does not exist!\n") % f)
       
   965                 continue
       
   966             if st.st_size > 10000000:
       
   967                 self.ui.warn(_("%s: files over 10MB may cause memory and"
       
   968                                " performance problems\n"
       
   969                                "(use 'hg revert %s' to unadd the file)\n")
       
   970                                % (f, f))
       
   971             if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
       
   972                 self.ui.warn(_("%s not added: only files and symlinks "
       
   973                                "supported currently\n") % f)
       
   974             elif self.dirstate[f] in 'an':
       
   975                 self.ui.warn(_("%s already tracked!\n") % f)
       
   976             else:
       
   977                 self.dirstate.add(f)
       
   978 
       
   979     def forget(self, list, wlock=None):
       
   980         if not wlock:
       
   981             wlock = self.wlock()
       
   982         for f in list:
       
   983             if self.dirstate[f] != 'a':
       
   984                 self.ui.warn(_("%s not added!\n") % f)
       
   985             else:
       
   986                 self.dirstate.forget(f)
       
   987 
       
   988     def remove(self, list, unlink=False, wlock=None):
       
   989         if unlink:
       
   990             for f in list:
       
   991                 try:
       
   992                     util.unlink(self.wjoin(f))
       
   993                 except OSError, inst:
       
   994                     if inst.errno != errno.ENOENT:
       
   995                         raise
       
   996         if not wlock:
       
   997             wlock = self.wlock()
       
   998         for f in list:
       
   999             if unlink and os.path.exists(self.wjoin(f)):
       
  1000                 self.ui.warn(_("%s still exists!\n") % f)
       
  1001             elif self.dirstate[f] == 'a':
       
  1002                 self.dirstate.forget(f)
       
  1003             elif f not in self.dirstate:
       
  1004                 self.ui.warn(_("%s not tracked!\n") % f)
       
  1005             else:
       
  1006                 self.dirstate.remove(f)
       
  1007 
       
  1008     def undelete(self, list, wlock=None):
       
  1009         p = self.dirstate.parents()[0]
       
  1010         mn = self.changelog.read(p)[0]
       
  1011         m = self.manifest.read(mn)
       
  1012         if not wlock:
       
  1013             wlock = self.wlock()
       
  1014         for f in list:
       
  1015             if self.dirstate[f] != 'r':
       
  1016                 self.ui.warn("%s not removed!\n" % f)
       
  1017             else:
       
  1018                 t = self.file(f).read(m[f])
       
  1019                 self.wwrite(f, t, m.flags(f))
       
  1020                 self.dirstate.normal(f)
       
  1021 
       
  1022     def copy(self, source, dest, wlock=None):
       
  1023         p = self.wjoin(dest)
       
  1024         if not (os.path.exists(p) or os.path.islink(p)):
       
  1025             self.ui.warn(_("%s does not exist!\n") % dest)
       
  1026         elif not (os.path.isfile(p) or os.path.islink(p)):
       
  1027             self.ui.warn(_("copy failed: %s is not a file or a "
       
  1028                            "symbolic link\n") % dest)
       
  1029         else:
       
  1030             if not wlock:
   969             if not wlock:
  1031                 wlock = self.wlock()
   970                 wlock = self.wlock()
  1032             if dest not in self.dirstate:
   971             for f in list:
  1033                 self.dirstate.add(dest)
   972                 p = self.wjoin(f)
  1034             self.dirstate.copy(source, dest)
   973                 try:
       
   974                     st = os.lstat(p)
       
   975                 except:
       
   976                     self.ui.warn(_("%s does not exist!\n") % f)
       
   977                     continue
       
   978                 if st.st_size > 10000000:
       
   979                     self.ui.warn(_("%s: files over 10MB may cause memory and"
       
   980                                    " performance problems\n"
       
   981                                    "(use 'hg revert %s' to unadd the file)\n")
       
   982                                    % (f, f))
       
   983                 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
       
   984                     self.ui.warn(_("%s not added: only files and symlinks "
       
   985                                    "supported currently\n") % f)
       
   986                 elif self.dirstate[f] in 'an':
       
   987                     self.ui.warn(_("%s already tracked!\n") % f)
       
   988                 else:
       
   989                     self.dirstate.add(f)
       
   990         finally:
       
   991             del wlock
       
   992 
       
   993     def forget(self, list, wlock=None):
       
   994         try:
       
   995             if not wlock:
       
   996                 wlock = self.wlock()
       
   997             for f in list:
       
   998                 if self.dirstate[f] != 'a':
       
   999                     self.ui.warn(_("%s not added!\n") % f)
       
  1000                 else:
       
  1001                     self.dirstate.forget(f)
       
  1002         finally:
       
  1003             del wlock
       
  1004 
       
  1005     def remove(self, list, unlink=False, wlock=None):
       
  1006         try:
       
  1007             if unlink:
       
  1008                 for f in list:
       
  1009                     try:
       
  1010                         util.unlink(self.wjoin(f))
       
  1011                     except OSError, inst:
       
  1012                         if inst.errno != errno.ENOENT:
       
  1013                             raise
       
  1014             if not wlock:
       
  1015                 wlock = self.wlock()
       
  1016             for f in list:
       
  1017                 if unlink and os.path.exists(self.wjoin(f)):
       
  1018                     self.ui.warn(_("%s still exists!\n") % f)
       
  1019                 elif self.dirstate[f] == 'a':
       
  1020                     self.dirstate.forget(f)
       
  1021                 elif f not in self.dirstate:
       
  1022                     self.ui.warn(_("%s not tracked!\n") % f)
       
  1023                 else:
       
  1024                     self.dirstate.remove(f)
       
  1025         finally:
       
  1026             del wlock
       
  1027 
       
  1028     def undelete(self, list, wlock=None):
       
  1029         try:
       
  1030             p = self.dirstate.parents()[0]
       
  1031             mn = self.changelog.read(p)[0]
       
  1032             m = self.manifest.read(mn)
       
  1033             if not wlock:
       
  1034                 wlock = self.wlock()
       
  1035             for f in list:
       
  1036                 if self.dirstate[f] != 'r':
       
  1037                     self.ui.warn("%s not removed!\n" % f)
       
  1038                 else:
       
  1039                     t = self.file(f).read(m[f])
       
  1040                     self.wwrite(f, t, m.flags(f))
       
  1041                     self.dirstate.normal(f)
       
  1042         finally:
       
  1043             del wlock
       
  1044 
       
  1045     def copy(self, source, dest, wlock=None):
       
  1046         try:
       
  1047             p = self.wjoin(dest)
       
  1048             if not (os.path.exists(p) or os.path.islink(p)):
       
  1049                 self.ui.warn(_("%s does not exist!\n") % dest)
       
  1050             elif not (os.path.isfile(p) or os.path.islink(p)):
       
  1051                 self.ui.warn(_("copy failed: %s is not a file or a "
       
  1052                                "symbolic link\n") % dest)
       
  1053             else:
       
  1054                 if not wlock:
       
  1055                     wlock = self.wlock()
       
  1056                 if dest not in self.dirstate:
       
  1057                     self.dirstate.add(dest)
       
  1058                 self.dirstate.copy(source, dest)
       
  1059         finally:
       
  1060             del wlock
  1035 
  1061 
  1036     def heads(self, start=None):
  1062     def heads(self, start=None):
  1037         heads = self.changelog.heads(start)
  1063         heads = self.changelog.heads(start)
  1038         # sort the output in rev descending order
  1064         # sort the output in rev descending order
  1039         heads = [(-self.changelog.rev(h), h) for h in heads]
  1065         heads = [(-self.changelog.rev(h), h) for h in heads]
  1307             return subset, updated_heads.keys()
  1333             return subset, updated_heads.keys()
  1308         else:
  1334         else:
  1309             return subset
  1335             return subset
  1310 
  1336 
  1311     def pull(self, remote, heads=None, force=False, lock=None):
  1337     def pull(self, remote, heads=None, force=False, lock=None):
  1312         mylock = False
       
  1313         if not lock:
       
  1314             lock = self.lock()
       
  1315             mylock = True
       
  1316 
       
  1317         try:
  1338         try:
       
  1339             if not lock:
       
  1340                 lock = self.lock()
  1318             fetch = self.findincoming(remote, force=force)
  1341             fetch = self.findincoming(remote, force=force)
  1319             if fetch == [nullid]:
  1342             if fetch == [nullid]:
  1320                 self.ui.status(_("requesting all changes\n"))
  1343                 self.ui.status(_("requesting all changes\n"))
  1321 
  1344 
  1322             if not fetch:
  1345             if not fetch:
  1329                 if 'changegroupsubset' not in remote.capabilities:
  1352                 if 'changegroupsubset' not in remote.capabilities:
  1330                     raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
  1353                     raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
  1331                 cg = remote.changegroupsubset(fetch, heads, 'pull')
  1354                 cg = remote.changegroupsubset(fetch, heads, 'pull')
  1332             return self.addchangegroup(cg, 'pull', remote.url())
  1355             return self.addchangegroup(cg, 'pull', remote.url())
  1333         finally:
  1356         finally:
  1334             if mylock:
  1357             del lock
  1335                 lock.release()
       
  1336 
  1358 
  1337     def push(self, remote, force=False, revs=None):
  1359     def push(self, remote, force=False, revs=None):
  1338         # there are two ways to push to remote repo:
  1360         # there are two ways to push to remote repo:
  1339         #
  1361         #
  1340         # addchangegroup assumes local user can lock remote
  1362         # addchangegroup assumes local user can lock remote
  1403             cg = self.changegroupsubset(update, revs, 'push')
  1425             cg = self.changegroupsubset(update, revs, 'push')
  1404         return cg, remote_heads
  1426         return cg, remote_heads
  1405 
  1427 
  1406     def push_addchangegroup(self, remote, force, revs):
  1428     def push_addchangegroup(self, remote, force, revs):
  1407         lock = remote.lock()
  1429         lock = remote.lock()
  1408 
  1430         try:
  1409         ret = self.prepush(remote, force, revs)
  1431             ret = self.prepush(remote, force, revs)
  1410         if ret[0] is not None:
  1432             if ret[0] is not None:
  1411             cg, remote_heads = ret
  1433                 cg, remote_heads = ret
  1412             return remote.addchangegroup(cg, 'push', self.url())
  1434                 return remote.addchangegroup(cg, 'push', self.url())
  1413         return ret[1]
  1435             return ret[1]
       
  1436         finally:
       
  1437             del lock
  1414 
  1438 
  1415     def push_unbundle(self, remote, force, revs):
  1439     def push_unbundle(self, remote, force, revs):
  1416         # local repo finds heads on server, finds out what revs it
  1440         # local repo finds heads on server, finds out what revs it
  1417         # must push.  once revs transferred, if server finds it has
  1441         # must push.  once revs transferred, if server finds it has
  1418         # different heads (someone else won commit/push race), server
  1442         # different heads (someone else won commit/push race), server
  1792 
  1816 
  1793         self.hook('prechangegroup', throw=True, source=srctype, url=url)
  1817         self.hook('prechangegroup', throw=True, source=srctype, url=url)
  1794 
  1818 
  1795         changesets = files = revisions = 0
  1819         changesets = files = revisions = 0
  1796 
  1820 
  1797         tr = self.transaction()
       
  1798 
       
  1799         # write changelog data to temp files so concurrent readers will not see
  1821         # write changelog data to temp files so concurrent readers will not see
  1800         # inconsistent view
  1822         # inconsistent view
  1801         cl = self.changelog
  1823         cl = self.changelog
  1802         cl.delayupdate()
  1824         cl.delayupdate()
  1803         oldheads = len(cl.heads())
  1825         oldheads = len(cl.heads())
  1804 
  1826 
  1805         # pull off the changeset group
  1827         tr = self.transaction()
  1806         self.ui.status(_("adding changesets\n"))
  1828         try:
  1807         cor = cl.count() - 1
  1829             # pull off the changeset group
  1808         chunkiter = changegroup.chunkiter(source)
  1830             self.ui.status(_("adding changesets\n"))
  1809         if cl.addgroup(chunkiter, csmap, tr, 1) is None:
  1831             cor = cl.count() - 1
  1810             raise util.Abort(_("received changelog group is empty"))
       
  1811         cnr = cl.count() - 1
       
  1812         changesets = cnr - cor
       
  1813 
       
  1814         # pull off the manifest group
       
  1815         self.ui.status(_("adding manifests\n"))
       
  1816         chunkiter = changegroup.chunkiter(source)
       
  1817         # no need to check for empty manifest group here:
       
  1818         # if the result of the merge of 1 and 2 is the same in 3 and 4,
       
  1819         # no new manifest will be created and the manifest group will
       
  1820         # be empty during the pull
       
  1821         self.manifest.addgroup(chunkiter, revmap, tr)
       
  1822 
       
  1823         # process the files
       
  1824         self.ui.status(_("adding file changes\n"))
       
  1825         while 1:
       
  1826             f = changegroup.getchunk(source)
       
  1827             if not f:
       
  1828                 break
       
  1829             self.ui.debug(_("adding %s revisions\n") % f)
       
  1830             fl = self.file(f)
       
  1831             o = fl.count()
       
  1832             chunkiter = changegroup.chunkiter(source)
  1832             chunkiter = changegroup.chunkiter(source)
  1833             if fl.addgroup(chunkiter, revmap, tr) is None:
  1833             if cl.addgroup(chunkiter, csmap, tr, 1) is None:
  1834                 raise util.Abort(_("received file revlog group is empty"))
  1834                 raise util.Abort(_("received changelog group is empty"))
  1835             revisions += fl.count() - o
  1835             cnr = cl.count() - 1
  1836             files += 1
  1836             changesets = cnr - cor
  1837 
  1837 
  1838         # make changelog see real files again
  1838             # pull off the manifest group
  1839         cl.finalize(tr)
  1839             self.ui.status(_("adding manifests\n"))
  1840 
  1840             chunkiter = changegroup.chunkiter(source)
  1841         newheads = len(self.changelog.heads())
  1841             # no need to check for empty manifest group here:
  1842         heads = ""
  1842             # if the result of the merge of 1 and 2 is the same in 3 and 4,
  1843         if oldheads and newheads != oldheads:
  1843             # no new manifest will be created and the manifest group will
  1844             heads = _(" (%+d heads)") % (newheads - oldheads)
  1844             # be empty during the pull
  1845 
  1845             self.manifest.addgroup(chunkiter, revmap, tr)
  1846         self.ui.status(_("added %d changesets"
  1846 
  1847                          " with %d changes to %d files%s\n")
  1847             # process the files
  1848                          % (changesets, revisions, files, heads))
  1848             self.ui.status(_("adding file changes\n"))
  1849 
  1849             while 1:
  1850         if changesets > 0:
  1850                 f = changegroup.getchunk(source)
  1851             self.hook('pretxnchangegroup', throw=True,
  1851                 if not f:
  1852                       node=hex(self.changelog.node(cor+1)), source=srctype,
  1852                     break
  1853                       url=url)
  1853                 self.ui.debug(_("adding %s revisions\n") % f)
  1854 
  1854                 fl = self.file(f)
  1855         tr.close()
  1855                 o = fl.count()
       
  1856                 chunkiter = changegroup.chunkiter(source)
       
  1857                 if fl.addgroup(chunkiter, revmap, tr) is None:
       
  1858                     raise util.Abort(_("received file revlog group is empty"))
       
  1859                 revisions += fl.count() - o
       
  1860                 files += 1
       
  1861 
       
  1862             # make changelog see real files again
       
  1863             cl.finalize(tr)
       
  1864 
       
  1865             newheads = len(self.changelog.heads())
       
  1866             heads = ""
       
  1867             if oldheads and newheads != oldheads:
       
  1868                 heads = _(" (%+d heads)") % (newheads - oldheads)
       
  1869 
       
  1870             self.ui.status(_("added %d changesets"
       
  1871                              " with %d changes to %d files%s\n")
       
  1872                              % (changesets, revisions, files, heads))
       
  1873 
       
  1874             if changesets > 0:
       
  1875                 self.hook('pretxnchangegroup', throw=True,
       
  1876                           node=hex(self.changelog.node(cor+1)), source=srctype,
       
  1877                           url=url)
       
  1878 
       
  1879             tr.close()
       
  1880         finally:
       
  1881             del tr
  1856 
  1882 
  1857         if changesets > 0:
  1883         if changesets > 0:
  1858             self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
  1884             self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
  1859                       source=srctype, url=url)
  1885                       source=srctype, url=url)
  1860 
  1886