Commits

lunaria committed 607b98f Merge

merge upstream

  • Participants
  • Parent commits 95a23b9, 476a981

Comments (0)

Files changed (48)

 tests/.coverage*
 tests/annotated
 tests/*.err
+tests/htmlcov
 build
 contrib/hgsh/hgsh
 dist

File contrib/check-code.py

     :logfunc: function used to report error
               logfunc(filename, linenumber, linecontent, errormessage)
     :maxerr: number of error to display before arborting.
-             Set to None (default) to report all errors
+             Set to false (default) to report all errors
 
     return True if no error is found, False otherwise.
     """
         for e in errors:
             logfunc(*e)
             fc += 1
-            if maxerr is not None and fc >= maxerr:
+            if maxerr and fc >= maxerr:
                 print " (too many errors, giving up)"
                 break
 

File contrib/hg-ssh

 
 from mercurial import dispatch
 
-import sys, os
+import sys, os, shlex
 
 cwd = os.getcwd()
 allowed_paths = [os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
                  for path in sys.argv[1:]]
 orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
+try:
+    cmdargv = shlex.split(orig_cmd)
+except ValueError, e:
+    sys.stderr.write("Illegal command %r: %s\n" % (orig_cmd, e))
+    sys.exit(-1)
 
-if orig_cmd.startswith('hg -R ') and orig_cmd.endswith(' serve --stdio'):
-    path = orig_cmd[6:-14]
+if cmdargv[:2] == ['hg', '-R'] and cmdargv[3:] == ['serve', '--stdio']:
+    path = cmdargv[2]
     repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
     if repo in allowed_paths:
         dispatch.dispatch(dispatch.request(['-R', repo, 'serve', '--stdio']))

File hgext/bugzilla.py

 from mercurial.i18n import _
 from mercurial.node import short
 from mercurial import cmdutil, mail, templater, util
-import re, time, xmlrpclib
+import re, time, urlparse, xmlrpclib
 
 class bzaccess(object):
     '''Base class for access to Bugzilla.'''
 
 # Buzgilla via XMLRPC interface.
 
-class CookieSafeTransport(xmlrpclib.SafeTransport):
-    """A SafeTransport that retains cookies over its lifetime.
+class cookietransportrequest(object):
+    """A Transport request method that retains cookies over its lifetime.
 
     The regular xmlrpclib transports ignore cookies. Which causes
     a bit of a problem when you need a cookie-based login, as with
     the Bugzilla XMLRPC interface.
 
-    So this is a SafeTransport which looks for cookies being set
-    in responses and saves them to add to all future requests.
-    It appears a SafeTransport can do both HTTP and HTTPS sessions,
-    which saves us having to do a CookieTransport too.
+    So this is a helper for defining a Transport which looks for
+    cookies being set in responses and saves them to add to all future
+    requests.
     """
 
     # Inspiration drawn from
 
         return unmarshaller.close()
 
+# The explicit calls to the underlying xmlrpclib __init__() methods are
+# necessary. The xmlrpclib.Transport classes are old-style classes, and
+# it turns out their __init__() doesn't get called when doing multiple
+# inheritance with a new-style class.
+class cookietransport(cookietransportrequest, xmlrpclib.Transport):
+    def __init__(self, use_datetime=0):
+        xmlrpclib.Transport.__init__(self, use_datetime)
+
+class cookiesafetransport(cookietransportrequest, xmlrpclib.SafeTransport):
+    def __init__(self, use_datetime=0):
+        xmlrpclib.SafeTransport.__init__(self, use_datetime)
+
 class bzxmlrpc(bzaccess):
     """Support for access to Bugzilla via the Bugzilla XMLRPC API.
 
         user = self.ui.config('bugzilla', 'user', 'bugs')
         passwd = self.ui.config('bugzilla', 'password')
 
-        self.bzproxy = xmlrpclib.ServerProxy(bzweb, CookieSafeTransport())
+        self.bzproxy = xmlrpclib.ServerProxy(bzweb, self.transport(bzweb))
         self.bzproxy.User.login(dict(login=user, password=passwd))
 
+    def transport(self, uri):
+        if urlparse.urlparse(uri, "http")[0] == "https":
+            return cookiesafetransport()
+        else:
+            return cookietransport()
+
     def get_bug_comments(self, id):
         """Return a string with all comment text for a bug."""
         c = self.bzproxy.Bug.comments(dict(ids=[id]))

File hgext/keyword.py

         # not make sense
         if (fctx._filerev is None and
             (self._repo._encodefilterpats or
-             kwt.match(fctx.path()) and not 'l' in fctx.flags()) or
+             kwt.match(fctx.path()) and not 'l' in fctx.flags() or
+             self.size() - 4 == fctx.size()) or
             self.size() == fctx.size()):
             return self._filelog.cmp(self._filenode, fctx.data())
         return True

File hgext/largefiles/lfcommands.py

     toget = []
 
     for lfile in lfiles:
-        expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
+        # If we are mid-merge, then we have to trust the standin that is in the
+        # working copy to have the correct hashvalue.  This is because the
+        # original hg.merge() already updated the standin as part of the normal
+        # merge process -- we just have to udpate the largefile to match.
+        if getattr(repo, "_ismerging", False):
+            expectedhash = lfutil.readstandin(repo, lfile)
+        else:
+            expectedhash = repo[node][lfutil.standin(lfile)].data().strip()
+
         # if it exists and its hash matches, it might have been locally
         # modified before updating and the user chose 'local'.  in this case,
         # it will not be in any store, so don't look for it.

File hgext/largefiles/overrides.py

     return result
 
 def hg_merge(orig, repo, node, force=None, remind=True):
-    result = orig(repo, node, force, remind)
-    lfcommands.updatelfiles(repo.ui, repo)
+    # Mark the repo as being in the middle of a merge, so that
+    # updatelfiles() will know that it needs to trust the standins in
+    # the working copy, not in the standins in the current node
+    repo._ismerging = True
+    try:
+        result = orig(repo, node, force, remind)
+        lfcommands.updatelfiles(repo.ui, repo)
+    finally:
+        repo._ismerging = False
     return result
 
 # When we rebase a repository with remotely changed largefiles, we need to
         self.path = patchdir or curpath
         self.opener = scmutil.opener(self.path)
         self.ui = ui
-        self.applieddirty = 0
-        self.seriesdirty = 0
+        self.applieddirty = False
+        self.seriesdirty = False
         self.added = []
         self.seriespath = "series"
         self.statuspath = "status"
     @util.propertycache
     def fullseries(self):
         try:
-             return self.opener.read(self.seriespath).splitlines()
+            return self.opener.read(self.seriespath).splitlines()
         except IOError, e:
             if e.errno == errno.ENOENT:
                 return []
         for a in 'applied fullseries series seriesguards'.split():
             if a in self.__dict__:
                 delattr(self, a)
-        self.applieddirty = 0
-        self.seriesdirty = 0
+        self.applieddirty = False
+        self.seriesdirty = False
         self.guardsdirty = False
         self.activeguards = None
 
             fp.close()
         if self.applieddirty:
             writelist(map(str, self.applied), self.statuspath)
+            self.applieddirty = False
         if self.seriesdirty:
             writelist(self.fullseries, self.seriespath)
+            self.seriesdirty = False
         if self.guardsdirty:
             writelist(self.activeguards, self.guardspath)
+            self.guardsdirty = False
         if self.added:
             qrepo = self.qrepo()
             if qrepo:
             n = repo.commit('[mq]: merge marker', force=True)
             self.removeundo(repo)
             self.applied.append(statusentry(n, pname))
-            self.applieddirty = 1
+            self.applieddirty = True
 
         head = self.qparents(repo)
 
             err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
             if head:
                 self.applied.append(statusentry(head, patch))
-                self.applieddirty = 1
+                self.applieddirty = True
             if err:
                 return (err, head)
         self.savedirty()
                 finally:
                     repo.invalidate()
                     repo.dirstate.invalidate()
+                    self.invalidate()
                 raise
         finally:
             release(tr, lock, wlock)
         if numrevs:
             qfinished = self.applied[:numrevs]
             del self.applied[:numrevs]
-            self.applieddirty = 1
+            self.applieddirty = True
 
         unknown = []
 
                 raise util.Abort(''.join(msg % p for p in unknown))
 
         self.parseseries()
-        self.seriesdirty = 1
+        self.seriesdirty = True
 
     def _revpatches(self, repo, revs):
         firstrev = repo[self.applied[0].node].rev()
                     self.fullseries[insert:insert] = [patchfn]
                     self.applied.append(statusentry(n, patchfn))
                     self.parseseries()
-                    self.seriesdirty = 1
-                    self.applieddirty = 1
+                    self.seriesdirty = True
+                    self.applieddirty = True
                     if msg:
                         msg = msg + "\n\n"
                         p.write(msg)
                         for chunk in chunks:
                             p.write(chunk)
                     p.close()
-                    wlock.release()
-                    wlock = None
                     r = self.qrepo()
                     if r:
                         r[None].add([patchfn])
                 del self.fullseries[index]
                 self.fullseries.insert(start, fullpatch)
                 self.parseseries()
-                self.seriesdirty = 1
-
-            self.applieddirty = 1
+                self.seriesdirty = True
+
+            self.applieddirty = True
             if start > 0:
                 self.checktoppatch(repo)
             if not patch:
             if not force and update:
                 self.checklocalchanges(repo)
 
-            self.applieddirty = 1
+            self.applieddirty = True
             end = len(self.applied)
             rev = self.applied[start].node
             if update:
                 # assumes strip can roll itself back if interrupted
                 repo.dirstate.setparents(*cparents)
                 self.applied.pop()
-                self.applieddirty = 1
+                self.applieddirty = True
                 self.strip(repo, [top], update=False,
                            backup='strip')
             except:
         self.fullseries = series
         self.applied = applied
         self.parseseries()
-        self.seriesdirty = 1
-        self.applieddirty = 1
+        self.seriesdirty = True
+        self.applieddirty = True
         heads = repo.changelog.heads()
         if delete:
             if rev not in heads:
             self.ui.warn(_("repo commit failed\n"))
             return 1
         self.applied.append(statusentry(n, '.hg.patches.save.line'))
-        self.applieddirty = 1
+        self.applieddirty = True
         self.removeundo(repo)
 
     def fullseriesend(self):
                 self.added.append(patchname)
                 patchname = None
             self.parseseries()
-            self.applieddirty = 1
+            self.applieddirty = True
             self.seriesdirty = True
 
         for i, filename in enumerate(files):
     current patch header, separated by a line of ``* * *``.
 
     Returns 0 on success."""
-
     q = repo.mq
-
     if not files:
         raise util.Abort(_('qfold requires at least one patch name'))
     if not q.checktoppatch(repo)[0]:
         if not newpath:
             ui.warn(_("no saved queues found, please use -n\n"))
             return 1
-        mergeq = queue(ui, repo.join(""), newpath)
+        mergeq = queue(ui, repo.path, newpath)
         ui.warn(_("merging with queue at: %s\n") % mergeq.path)
     ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
                  mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
     """
     localupdate = True
     if opts.get('name'):
-        q = queue(ui, repo.join(""), repo.join(opts.get('name')))
+        q = queue(ui, repo.path, repo.join(opts.get('name')))
         ui.warn(_('using patch queue: %s\n') % q.path)
         localupdate = False
     else:
     With two arguments, renames PATCH1 to PATCH2.
 
     Returns 0 on success."""
-
     q = repo.mq
-
     if not name:
         name = patch
         patch = None
     guards = q.guard_re.findall(q.fullseries[i])
     q.fullseries[i] = name + ''.join([' #' + g for g in guards])
     q.parseseries()
-    q.seriesdirty = 1
+    q.seriesdirty = True
 
     info = q.isapplied(patch)
     if info:
         q.applied[info[0]] = statusentry(info[1], name)
-    q.applieddirty = 1
+    q.applieddirty = True
 
     destdir = os.path.dirname(absdest)
     if not os.path.isdir(destdir):
     ret = q.save(repo, msg=message)
     if ret:
         return ret
-    q.savedirty()
+    q.savedirty() # save to .hg/patches before copying
     if opts.get('copy'):
         path = q.path
         if opts.get('name'):
         ui.warn(_("copy %s to %s\n") % (path, newpath))
         util.copyfiles(path, newpath)
     if opts.get('empty'):
-        try:
-            os.unlink(q.join(q.statuspath))
-        except:
-            pass
+        del q.applied[:]
+        q.applieddirty = True
+        q.savedirty()
     return 0
 
 @command("strip",
 
     Returns 0 on success.
     '''
-
     q = repo.mq
-
     _defaultqueue = 'patches'
     _allqueues = 'patches.queues'
     _activequeue = 'patches.queue'
     class mqrepo(repo.__class__):
         @util.propertycache
         def mq(self):
-            return queue(self.ui, self.join(""))
+            return queue(self.ui, self.path)
 
         def abortifwdirpatched(self, errmsg, force=False):
             if self.mq.applied and not force:

File hgext/rebase.py

 
     lock = wlock = None
     try:
+        wlock = repo.wlock()
         lock = repo.lock()
-        wlock = repo.wlock()
 
         # Validate input and define rebasing points
         destf = opts.get('dest', None)
 #
 # clean                  — чистый (~ локальный файл, не содержащий изменений по сравнению с хранилищем)
 #     clean working copy — чистая рабочая копия (= не содержащая изменённых файлов) 
-# purge                  — зачистить (= удалить неиспользуемые файлы из рабочей копии), 
+# purge                  — зачистить (напр. удалить неиспользуемые файлы из рабочей копии), 
 #                          (не «удалить», не «очистить»)
-# strip                  — срезать (~ ревизию и всех её потомков) (thg: содрать (как одежду));
+# strip                  — срезать (~ ревизию и всех её потомков)
 #     strip revision     — срезать ревизию 
-# ??? delete                 — уничтожить (= стереть с диска, не «удалить»)
+# delete                 — удалить, уничтожить (напр. стереть с диска)
 # remove                 — изъять (из-под контроля версий, не «удалить») 
 #
 #
 #     named branch    — именованная ветка
 # diff                — различия (не «дифф»)
 # merge               — слить (сущ. «слияние»)
-# ??? fetch               — залить (сущ. «заливка» — означает слияние поверх незафиксированных изменений)
-# backout             — обратить (~ изменения, сделанные в ранней ревизии), (thg: отлить, сущ. «отливание», иногда «обратное слияние»))
+# backout             — обратить изменения (~ сделанные в ранней ревизии)
 # rollback            — откатить (~ транзакцию)
 # cancel              — отменить 
 #
 #     to bundle several changesets — укомплектовать несколько наборов изменений
 #     binary bundle                — двоичный комплект
 # ??? batch                 — порция
-# ??? patch                 — заплатка, накладывать заплатку (не «патч», не «патчить»)
-# ??? apply patch — наложить заплатку (не «применить заплатку»), (вариант: пришить заплатку),
-# ??? unapply patch — отпороть заплатку, (thg: оторвать заплатку), варианты: снять заплатку
-# ??? fold patch(es) — подшить заплатки (~ к самой верхней наложенной заплатке) 
-# ??? chunk, hunk — лоскут (= часть заплатки)
-# ??? shelf — долгий ящик (= место для откладывания изменений) ;)
-# shelve — отложить изменения (в долгий ящик) 
+# patch               — заплатка, патч, накладывать заплатку, патчить, пропатчить
+# ??? apply patch     — наложить заплатку (не «применить заплатку»)
+# ??? unapply patch   — отпороть заплатку
+# ??? fold patch(es)  — подшить заплатки (~ к самой верхней наложенной заплатке) 
+# ??? chunk, hunk     — лоскут (= часть заплатки)
+# ??? shelf           — долгий ящик (= место для откладывания изменений) ;)
+# shelve              — отложить изменения (в долгий ящик) 
 #
 #
 # Разное
 # option           — параметр (не «опция»)
 #     options      — параметры (не «настройки») 
 # settings         — настройки (не «установки»)
-# ??? hook         — перехватчик, (не «крючок», не «ловушка», не «уловка», не «зацепка»)
-#     hook script  — скрипт-перехватчик (не «скрипт ловушки»)
-#     иногда слово hook употребляется в том же смысле, что и intercepted action (например, в документации) — тогда его следует переводить как «перехватываемое действие/событие» или «точка/момент перехвата» 
+# hook             — хук, перехватчик, (не «крючок», не «ловушка», не «уловка», не «зацепка»)
+#     hook script  — хук, скрипт-перехватчик (не «скрипт ловушки»)
+#     иногда слово hook употребляется в том же смысле, что и intercepted action (например, в документации)
+#     — тогда его следует переводить как «перехватываемое действие/событие» или «точка/момент перехвата» 
 #
 # alias          — псевдоним
 # changeset      — набор изменений
 # revision       — ревизия
 # status         — состояние
-# ??? head       — голова (= ревизия, не имеющая дочерних ревизий)
-#
-# ??? tip        — оконечная ревизия
-#
-# ??? tip revision — (ревизия-)макушка, вариант: оконечная ревизия, ревизия-наконечник 
+# head           — голова (= ревизия, не имеющая дочерних ревизий)
+# tip            — наконечник, оконечная ревизия
+#   tip revision — оконечная ревизия
 #
 # changegroup    группа изменений
 # tag            метка
 # working copy   рабочая копия
 # hash           хэш
-# glob           глоб, glob
-# update         обновить
+# glob           глоб, glob, (thg: «маска файла (glob)»)
 # binary         бинарный
 # basename       ? 
 # import         импортировать
 # restricted     ограниченный
 # graft          перенести (изменения) ??? вообще пересадить
 # id             идентификатор
-# guard (в mq)   страж (необходимо пояснение) (thg: стражник)
+# guard (в mq)   страж (необходимо пояснение) (thg: «стражник» без всяких пояснений)
 # positive/negative guard положительный/отрицательный страж ??? (может разрешающий/запрещающий?)
 # apply patch    наложить патч
 # fuzz (patch)   несоответствие контекстов (в патче и целевом файле) ???
 # nested         вложенный
 # deprecated     устаревший
 # backup         резервная копия
-# finish (mq, qfinish) финализировать??? -- «завершить» // comment by Andrei Polushin
+# finish (mq, qfinish) финализировать??? (thg: завершить заплатку)
 # fold (patch)     подшить ??? вообще свернуть
 # imply          подразумевать
 # rebase         перебазировать
 # root           корень, корневая ревизия ???
 # progress bar   индикатор выполнения
-# map (file)     ??? да и вообще все с map плохо
-# abort          отмена или останов (в статусах)
+# map (file)     ??? да и вообще все с map плохо (вариант: сопоставление)
+# abort          отмена или останов (в статусах) (thg: прервать, прервано)
 # checkin        (в конфиге для encode/decode) сейчас фиксация ???
 # lookup         поиск ???
 #
 #   Сюда же относятся сообщения cannot do smth: не могу сделать что-то или что-то невозможно?
 #   Сейчас последний вариант. -- А лучше «не удаётся» // comment by Andrei Polushin
 # - внести определения согласованных терминов в hg glossary
-# - bisect - можно во многих местах употреблять термин "бисекция" вместо
-#            неуклюжего "метод деления пополам". Это устоявшийся термин.
+# - bisect - можно во многих местах употреблять термин "бисекция" (употребляется в thg)
+#            вместо неуклюжего "метод деления пополам". Это устоявшийся термин.
 # - в строке должно быть не более 75 символов!
 # - переводить ли примеры конфигов? я оставил как есть, чтобы пользователь 
 #   привыкал к англ, т.к. все настройки Mercurial на англ
 
 # BUG
 msgid "aborting hg cat-file only understands commits\n"
-msgstr "останов: hg cat-file понимает только фиксации (commits)\n"
+msgstr "прервано: hg cat-file понимает только фиксации (commits)\n"
 
 msgid "parse given revisions"
 msgstr "разбор заданных ревизий"
 "order to benefit from largefiles. This is done with the\n"
 ":hg:`lfconvert` command::"
 msgstr ""
-"Если у вас уже есть большие файлы, отслеживаемые Mrecurial без\n"
+"Если у вас уже есть большие файлы, отслеживаемые Mercurial без\n"
 "расширения largefiles, вам надо будет преобразовать свое хранилище,\n"
 "чтобы извлечь пользу из расширения largefiles. Это делается командой\n"
 ":hg:`lfconvert`::"
 "to the minimum size in megabytes to track as a largefile, or use the\n"
 "--lfsize option to the add command (also in megabytes)::"
 msgstr ""
-"В хранилищах, уже содержащих большие файлы, каждый файл больше 10 Мб\n"
+"В хранилищах, уже содержащих большие файлы, каждый файл больше 10 МБ\n"
 "будет добавлен как большой. Чтобы изменить этот порог, используйте\n"
 "параметр ``largefiles.minsize`` в вашем конфиге Mercurial, чтобы\n"
 "задать минимальный размер в мегабайтах, начиная с которого файл считается\n"
-"большим, или используйте параметр команды add --lfsize (также в Мб)::"
+"большим, или используйте параметр команды add --lfsize (также в МБ)::"
 
 msgid ""
 "  [largefiles]\n"
 
 msgid "minimum size (MB) for files to be converted as largefiles"
 msgstr ""
-"минимальный размер (Мб), начиная с которого файлы преобразуются в большие"
+"минимальный размер (МБ), начиная с которого файлы преобразуются в большие"
 
 msgid "convert from a largefiles repo to a normal repo"
 msgstr "преобразовать из хранилища largefiles в обычное хранилище"
 msgid ""
 "add all files above this size (in megabytes) as largefiles (default: 10)"
 msgstr ""
-"добавить все файлы больше этого размера (в Мб) как большие (по умолчанию: 10)"
+"добавить все файлы больше этого размера (в МБ) как большие (по умолчанию: 10)"
 
 msgid "verify largefiles"
 msgstr "проверить большие файлы"
 msgstr "%s встречается более одного раза в %s"
 
 msgid "guard cannot be an empty string"
-msgstr "страж (guard) не может быть пустой строкой"
+msgstr "страж не может быть пустой строкой"
 
 #, python-format
 msgid "guard %r starts with invalid character: %r"
-msgstr "страж (guard) %r начинается с недопустимого символа: %r"
+msgstr "страж %r начинается с недопустимого символа: %r"
 
 #, python-format
 msgid "invalid character in guard %r: %r"
-msgstr "недопустимый символ в страже (guard) %r: %r"
+msgstr "недопустимый символ в страже %r: %r"
 
 #, python-format
 msgid "guard %r too short"
-msgstr "страж (guard) %r слишком короткий"
+msgstr "страж %r слишком короткий"
 
 #, python-format
 msgid "guard %r starts with invalid char"
-msgstr "страж (guard) %r начинается с недопустимого символа"
+msgstr "страж %r начинается с недопустимого символа"
 
 #, python-format
 msgid "allowing %s - no guards in effect\n"
-msgstr "%s разрешен - страж (guard) не задействуется\n"
+msgstr "%s разрешен - страж не задействуется\n"
 
 #, python-format
 msgid "allowing %s - no matching negative guards\n"
-msgstr "%s разрешен - нет подходящих запрещающих стражей (guards)\n"
+msgstr "%s разрешен - нет подходящих запрещающих стражей\n"
 
 #, python-format
 msgid "allowing %s - guarded by %s\n"
 
 #, python-format
 msgid "skipping %s - no matching guards\n"
-msgstr "%s пропускается - нет подходящих стражей (guards)\n"
+msgstr "%s пропускается - нет подходящих стражей\n"
 
 #, python-format
 msgid "error removing undo: %s\n"
 "не удалось наложить патч, отклоненные файлы сохранены в рабочем каталоге\n"
 
 msgid "fuzz found when applying patch, stopping\n"
-msgstr "обнаружено несоответствие контекстов (fuzz), останов\n"
+msgstr "обнаружено несоответствие контекстов (fuzz), остановлено\n"
 
 #, python-format
 msgid "revision %s refers to unknown patches: %s\n"
 msgstr "защищен %s"
 
 msgid "no matching guards"
-msgstr "нет подходящих стражей (guards)"
+msgstr "нет подходящих стражей"
 
 #, python-format
 msgid "cannot push '%s' - %s\n"
 msgstr "поместить существующие ревизии под управление mq"
 
 msgid "use git extended diff format"
-msgstr "использовать расширенный формат git для diff'ов"
+msgstr "использовать расширенный формат git для различий"
 
 msgid "qpush after importing"
 msgstr "выполнить qpush после импортирования"
 "стека"
 
 msgid "list all patches and guards"
-msgstr "перечислить все патчи и стражей (guards)"
+msgstr "перечислить все патчи и стражей"
 
 msgid "drop all guards"
 msgstr "удалить всех стражей"
 msgstr "обрабатывать все файлы как текстовые"
 
 msgid "omit dates from diff headers"
-msgstr "опускать даты в заголовках diff'ов"
+msgstr "опускать даты в заголовках файлов различий"
 
 msgid "show which function each change is in"
 msgstr "для каждого изменения показать, в какой оно сделано функции"
 msgstr "[ПАРАМЕТР]... [-o OUTFILESPEC] РЕВИЗИЯ..."
 
 msgid "dump the header and diffs for one or more changesets"
-msgstr "сохранить заголовок и diff'ы для одной или нескольких ревизий"
+msgstr "вывести заголовок и различия для одной или нескольких ревизий"
 
 msgid "    Print the changeset header and diffs for one or more revisions."
 msgstr ""
 "    of files it detects as binary. With -a, export will generate a\n"
 "    diff anyway, probably with undesirable results."
 msgstr ""
-"    Без -a/--text, export не будет генерировать diff'ы для файлов,\n"
-"    которые он считает бинарными. С -а diff'ы будут сгенерированы\n"
+"    Без -a/--text, export не будет генерировать различия для файлов,\n"
+"    которые он считает бинарными. С -а различия будут сгенерированы\n"
 "    для всех файлов, часто с нежелательными результатами."
 
 msgid ""
 "    Use the -g/--git option to generate diffs in the git extended diff\n"
 "    format. See :hg:`help diffs` for more information."
 msgstr ""
-"    Используйте -g/--git чтобы сгенерировать diff'ы в расширенном\n"
+"    Используйте -g/--git чтобы сгенерировать различия в расширенном\n"
 "    формате git. Подробнее см. :hg:`help diffs`."
 
 msgid ""
 "    оно должно иметь тип text/plain или text/x-patch). Заголовки\n"
 "    электронного письма From и Subject используются по умолчанию\n"
 "    в качестве автора фиксации и сообщения фиксации. Все части\n"
-"    тела письма типа text/plain до первого diff'а добавляются\n"
+"    тела письма типа text/plain до первого различия добавляются\n"
 "    к сообщению фиксации."
 
 msgid ""
 "       will appear in files:."
 msgstr ""
 "    .. note::\n"
-"       log -p/--patch может генерировать неожиданные diff'ы для\n"
+"       log -p/--patch может генерировать неожиданные различия для\n"
 "       ревизий слияния, поскольку сравнение слитой ревизии\n"
 "       производится только с ее первым родителем. Также в список\n"
 "       попадут только файлы, отличные от ОБОИХ родителей:."
 "       relative to one merge parent."
 msgstr ""
 "    .. note::\n"
-"       состояния могут не совпадать с diff'ом, если изменились\n"
+"       состояния могут не совпадать с файлом различий, если изменились\n"
 "       права доступа или произошло слияние. Стандартный формат diff\n"
 "       не показывает изменения прав доступа и показывает только\n"
 "       изменения относительно одной родительской ревизии."
 "%s: up to %d MB of RAM may be required to manage this file\n"
 "(use 'hg revert %s' to cancel the pending addition)\n"
 msgstr ""
-"%s: может потребовать до %d Мб памяти для обработки этого файла\n"
+"%s: может потребовать до %d МБ памяти для обработки этого файла\n"
 "(используйте 'hg revert %s' чтобы отменить запланированное добавление)\n"
 
 #, python-format
 
 #, python-format
 msgid "abort: %s\n"
-msgstr "останов: %s\n"
+msgstr "прервано: %s\n"
 
 #, python-format
 msgid "(%s)\n"
 
 #, python-format
 msgid "abort: %s: %s\n"
-msgstr "останов: %s: %s\n"
+msgstr "прервано: %s: %s\n"
 
 #, python-format
 msgid "abort: could not lock %s: %s\n"
-msgstr "останов: не удается заблокировать %s: %s\n"
+msgstr "прервано: не удается заблокировать %s: %s\n"
 
 #, python-format
 msgid "hg %s: %s\n"
 msgstr "hg: %s\n"
 
 msgid "abort: remote error:\n"
-msgstr "останов: ошибка на отдаленной стороне:\n"
+msgstr "прервано: ошибка на отдаленной стороне:\n"
 
 #, python-format
 msgid "abort: %s!\n"
-msgstr "останов: %s!\n"
+msgstr "прервано: %s!\n"
 
 #, python-format
 msgid "abort: %s"
-msgstr "останов: %s"
+msgstr "прервано: %s"
 
 msgid " empty string\n"
 msgstr " пустая строка\n"
 
 #, python-format
 msgid "abort: error: %s\n"
-msgstr "останов: ошибка: %s\n"
+msgstr "прервано: ошибка: %s\n"
 
 msgid "broken pipe\n"
-msgstr "обрыв канала (broken pipe)\n"
+msgstr "обрыв канала ввода-вывода\n"
 
 msgid "interrupted!\n"
 msgstr "прервано!\n"
 "broken pipe\n"
 msgstr ""
 "\n"
-"обрыв канала (broken pipe)\n"
+"обрыв канала ввода-вывода\n"
 
 msgid "abort: out of memory\n"
-msgstr "останов: недостаточно памяти\n"
+msgstr "прервано: недостаточно памяти\n"
 
 msgid "** unknown exception encountered, please report by visiting\n"
 msgstr "** неизвестное исключение, пожалуйте, сообщите об этом по адресу\n"
 msgstr "Задание набора файлов"
 
 msgid "Diff Formats"
-msgstr "Форматы diff'ов"
+msgstr "Форматы файлов различий"
 
 msgid "Merge Tools"
 msgstr "Инструменты для слияния"
 "    Use git extended diff format."
 msgstr ""
 "``git``\n"
-спользовать расширенный формат git для diff'ов"
+"    Использовать расширенный формат git для файлов различий."
 
 msgid ""
 "``nodates``\n"
 "    Don't include dates in diff headers."
 msgstr ""
 "``nodates``\n"
-"    Не добавлять даты в заголовки diff'ов."
+"    Не добавлять даты в заголовки файлов различий."
 
 msgid ""
 "``showfunc``\n"
 "    Mercurial всегда печатает трассировку вызовов при неизвестном\n"
 "    исключении. Если этот параметр установлен в True, трассировка\n"
 "    будет печататься при любых исключениях, даже обрабатываемых\n"
-"    Mrecurial (таких, как IOError или MemoryError).\n"
+"    Mercurial (таких, как IOError или MemoryError).\n"
 "    По умолчанию False (отключено)."
 
 msgid ""
 msgstr ""
 "Mercurial также поддерживает расширенный формат diff VCS git, который\n"
 "исправляет эти недостатки. Этот формат не используется по умолчанию,\n"
-"потому что многие распространенные изменения его не понимают."
+"потому что многие распространенные инструменты его не понимают."
 
 msgid ""
 "This means that when generating diffs from a Mercurial repository\n"
 "pull) are not affected by this, because they use an internal binary\n"
 "format for communicating changes."
 msgstr ""
-"Это значит, что при генерировании diff'ов из хранилища Mercurial\n"
+"Это значит, что при генерировании различий из хранилища Mercurial\n"
 "(например, с помощью :hg:`export`), нужно быть осторожным с такими\n"
 "вещами как копирования и переименования и другими перечисленными выше,\n"
-"потому что при применении стандартного diff'а к другому хранилищу\n"
+"потому что при применении стандартного файла различий к другому хранилищу\n"
 "эта информация теряется. На внутренние операции Mercurial (как push\n"
 "или pull) это не влияет, потому что они используют внутренний бинарный\n"
 "формат для обмена изменениями."
 "section of your configuration file. You do not need to set this option\n"
 "when importing diffs in this format or using them in the mq extension.\n"
 msgstr ""
-"Чтобы генерировать diff'ы в расширенном формате git, используйте опцию\n"
+"Чтобы генерировать различия в расширенном формате git, используйте опцию\n"
 "--git, которая доступна для многих команд, или установите 'git = True'\n"
 "в секции [diff] в вашем конфиге. Эту опцию не обязательно указывать\n"
-"при импорте diff'ов в этом формате или использовании расширения mq.\n"
+"при импорте различий в этом формате или использовании расширения mq.\n"
 
 msgid ""
 "HG\n"
 "    attributes, none of which can be represented/handled by classic\n"
 "    \"diff\" and \"patch\"."
 msgstr ""
-"Дифф (diff)\n"
+"Различия, файл различий (diff)\n"
 "    Отличия содержимого и атрибутов файлов между двумя ревизиями\n"
 "    или между некоей ревизией и текущим рабочим каталогом. Отличия\n"
 "    обычно представлены в стандартной форме, называемой \"диффом\"\n"
-"    или \"патчем\". Формат диффов git используется, если изменения\n"
+"    или \"патчем\". Формат различий git используется, если изменения\n"
 "    должны включать копирования, переименования или изменения\n"
 "    атрибутов файлов, которые не могут быть представлены или\n"
 "    обработаны классическими инструментами \"diff\" и \"patch\"."
 
 msgid "    Example: \"Did you see my correction in the diff?\""
-msgstr "    Пример: \"Ты видел мои исправления в диффе (патче)?\""
+msgstr "    Пример: \"Я прислал тебе файл различий с моими исправлениями.\""
 
 msgid ""
 "    (Verb) Diffing two changesets is the action of creating a diff or\n"
 msgid ""
 "%d files updated, %d files merged, %d files removed, %d files unresolved\n"
 msgstr ""
-"%d файлов обновлено, %d слито, %d изъято из-под контроля, %d c конфликтами\n"
+"%d файлов обновлено, %d слито, %d удалено, %d c конфликтами\n"
 
 msgid "use 'hg resolve' to retry unresolved file merges\n"
 msgstr ""
 
 #, python-format
 msgid "added %d changesets with %d changes to %d files%s\n"
-msgstr "добавлено %d набор(ов) изменений с %d изменениями в %d файле(-ах) %s\n"
+msgstr "добавлено %d наборов изменений с %d изменениями в %d файлах%s\n"
 
 msgid "Unexpected response from remote server:"
-msgstr "Неожиданный ответ от удаленного сервера:"
+msgstr "Неожиданный ответ от отдалённого сервера:"
 
 msgid "operation forbidden by server"
 msgstr "операция запрещена на сервере"
 
 #, python-format
 msgid "%.0f GB"
-msgstr "%.0f Гб"
+msgstr "%.0f ГБ"
 
 #, python-format
 msgid "%.1f GB"
-msgstr "%.1f Гб"
+msgstr "%.1f ГБ"
 
 #, python-format
 msgid "%.2f GB"
-msgstr "%.2f Гб"
+msgstr "%.2f ГБ"
 
 #, python-format
 msgid "%.0f MB"
-msgstr "%.0f Мб"
+msgstr "%.0f МБ"
 
 #, python-format
 msgid "%.1f MB"
-msgstr "%.1f Мб"
+msgstr "%.1f МБ"
 
 #, python-format
 msgid "%.2f MB"
-msgstr "%.2f Мб"
+msgstr "%.2f МБ"
 
 #, python-format
 msgid "%.0f KB"
-msgstr "%.0f Кб"
+msgstr "%.0f КБ"
 
 #, python-format
 msgid "%.1f KB"
-msgstr "%.1f Кб"
+msgstr "%.1f КБ"
 
 #, python-format
 msgid "%.2f KB"
-msgstr "%.2f Кб"
+msgstr "%.2f КБ"
 
 #, python-format
 msgid "%.0f bytes"

File mercurial/bookmarks.py

             raise util.Abort(_("bookmark '%s' contains illegal "
                 "character" % mark))
 
-    wlock = repo.wlock()
+    lock = repo.lock()
     try:
 
         file = repo.opener('bookmarks', 'w', atomictemp=True)
             pass
 
     finally:
-        wlock.release()
+        lock.release()
 
 def setcurrent(repo, mark):
     '''Set the name of the bookmark that we are currently on
         raise util.Abort(_("bookmark '%s' contains illegal "
             "character" % mark))
 
-    wlock = repo.wlock()
+    lock = repo.lock()
     try:
         file = repo.opener('bookmarks.current', 'w', atomictemp=True)
         file.write(encoding.fromlocal(mark))
         file.close()
     finally:
-        wlock.release()
+        lock.release()
     repo._bookmarkcurrent = mark
 
 def updatecurrentbookmark(repo, oldnode, curbranch):
     return d
 
 def pushbookmark(repo, key, old, new):
-    w = repo.wlock()
+    lock = repo.lock()
     try:
         marks = repo._bookmarks
         if hex(marks.get(key, '')) != old:
         write(repo)
         return True
     finally:
-        w.release()
+        lock.release()
 
 def updatefromremote(ui, repo, remote, path):
     ui.debug("checking for updated bookmarks\n")

File mercurial/commands.py

 import minirst, revset, fileset
 import dagparser, context, simplemerge
 import random, setdiscovery, treediscovery, dagutil
-import phases as phasesmod
+import phases
 
 table = {}
 
 
     tags = []
 
-    tr = repo.transaction("builddag")
+    lock = tr = None
     try:
+        lock = repo.lock()
+        tr = repo.transaction("builddag")
 
         at = -1
         atbranch = 'default'
                 atbranch = data
             ui.progress(_('building'), id, unit=_('revisions'), total=total)
         tr.close()
+
+        if tags:
+            repo.opener.write("localtags", "".join(tags))
     finally:
         ui.progress(_('building'), None)
-        tr.release()
-
-    if tags:
-        repo.opener.write("localtags", "".join(tags))
+        release(tr, lock)
 
 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
 def debugbundle(ui, bundlepath, all=None, **opts):
                 ui.write(" %s:\n      %s\n"%(commands, h[f]))
             else:
                 ui.write('%s\n' % (util.wrap(h[f], textwidth,
-                                             initindent=' %-*s   ' % (m, f),
+                                             initindent=' %-*s    ' % (m, f),
                                              hangindent=' ' * (m + 4))))
 
         if not name:
                 topics.append((sorted(names, key=len, reverse=True)[0], header))
             topics_len = max([len(s[0]) for s in topics])
             for t, desc in topics:
-                ui.write(" %-*s  %s\n" % (topics_len, t, desc))
+                ui.write(" %-*s   %s\n" % (topics_len, t, desc))
 
         optlist = []
         addglobalopts(optlist, True)
                 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
 
 @command('^phase',
-    [('p', 'public', False, _('Set changeset to public')),
-     ('d', 'draft', False, _('Set changeset to draft')),
-     ('s', 'secret', False, _('Set changeset to secret')),
+    [('p', 'public', False, _('set changeset phase to public')),
+     ('d', 'draft', False, _('set changeset phase to draft')),
+     ('s', 'secret', False, _('set changeset phase to secret')),
      ('f', 'force', False, _('allow to move boundary backward')),
-     ('r', 'rev', [], _('target revision')),
+     ('r', 'rev', [], _('target revision'), _('REV')),
     ],
-    _('[-p|-d|-s] [-f] [-C] [-r] REV'))
+    _('[-p|-d|-s] [-f] [-r] REV...'))
 def phase(ui, repo, *revs, **opts):
     """set or show the current phase name
 
     With no argument, show the phase name of specified revisions.
 
-    With one of `--public`, `--draft` or `--secret`, change the phase
-    value of the specified revisions.
+    With one of -p/--public, -d/--draft or -s/--secret, change the
+    phase value of the specified revisions.
 
     Unless -f/--force is specified, :hg:`phase` won't move changeset from a
-    lower phase to an higher phase. Phases are ordered as follows:
+    lower phase to an higher phase. Phases are ordered as follows::
 
         public < draft < secret
     """
     # search for a unique phase argument
     targetphase = None
-    for idx, name in enumerate(phasesmod.phasenames):
+    for idx, name in enumerate(phases.phasenames):
         if opts[name]:
             if targetphase is not None:
                 raise util.Abort(_('only one phase can be specified'))
             nodes = [ctx.node() for ctx in repo.set('%lr', revs)]
             if not nodes:
                 raise util.Abort(_('empty revision set'))
-            phasesmod.advanceboundary(repo, targetphase, nodes)
+            phases.advanceboundary(repo, targetphase, nodes)
             if opts['force']:
-                phasesmod.retractboundary(repo, targetphase, nodes)
+                phases.retractboundary(repo, targetphase, nodes)
         finally:
             lock.release()
 
 
     Returns 0 on success.
     """
-
-    rev_ = "."
-    names = [t.strip() for t in (name1,) + names]
-    if len(names) != len(set(names)):
-        raise util.Abort(_('tag names must be unique'))
-    for n in names:
-        if n in ['tip', '.', 'null']:
-            raise util.Abort(_("the name '%s' is reserved") % n)
-        if not n:
-            raise util.Abort(_('tag names cannot consist entirely of whitespace'))
-    if opts.get('rev') and opts.get('remove'):
-        raise util.Abort(_("--rev and --remove are incompatible"))
-    if opts.get('rev'):
-        rev_ = opts['rev']
-    message = opts.get('message')
-    if opts.get('remove'):
-        expectedtype = opts.get('local') and 'local' or 'global'
+    wlock = lock = None
+    try:
+        wlock = repo.wlock()
+        lock = repo.lock()
+        rev_ = "."
+        names = [t.strip() for t in (name1,) + names]
+        if len(names) != len(set(names)):
+            raise util.Abort(_('tag names must be unique'))
         for n in names:
-            if not repo.tagtype(n):
-                raise util.Abort(_("tag '%s' does not exist") % n)
-            if repo.tagtype(n) != expectedtype:
-                if expectedtype == 'global':
-                    raise util.Abort(_("tag '%s' is not a global tag") % n)
-                else:
-                    raise util.Abort(_("tag '%s' is not a local tag") % n)
-        rev_ = nullid
+            if n in ['tip', '.', 'null']:
+                raise util.Abort(_("the name '%s' is reserved") % n)
+            if not n:
+                raise util.Abort(_('tag names cannot consist entirely of '
+                                   'whitespace'))
+        if opts.get('rev') and opts.get('remove'):
+            raise util.Abort(_("--rev and --remove are incompatible"))
+        if opts.get('rev'):
+            rev_ = opts['rev']
+        message = opts.get('message')
+        if opts.get('remove'):
+            expectedtype = opts.get('local') and 'local' or 'global'
+            for n in names:
+                if not repo.tagtype(n):
+                    raise util.Abort(_("tag '%s' does not exist") % n)
+                if repo.tagtype(n) != expectedtype:
+                    if expectedtype == 'global':
+                        raise util.Abort(_("tag '%s' is not a global tag") % n)
+                    else:
+                        raise util.Abort(_("tag '%s' is not a local tag") % n)
+            rev_ = nullid
+            if not message:
+                # we don't translate commit messages
+                message = 'Removed tag %s' % ', '.join(names)
+        elif not opts.get('force'):
+            for n in names:
+                if n in repo.tags():
+                    raise util.Abort(_("tag '%s' already exists "
+                                       "(use -f to force)") % n)
+        if not opts.get('local'):
+            p1, p2 = repo.dirstate.parents()
+            if p2 != nullid:
+                raise util.Abort(_('uncommitted merge'))
+            bheads = repo.branchheads()
+            if not opts.get('force') and bheads and p1 not in bheads:
+                raise util.Abort(_('not at a branch head (use -f to force)'))
+        r = scmutil.revsingle(repo, rev_).node()
+
         if not message:
             # we don't translate commit messages
-            message = 'Removed tag %s' % ', '.join(names)
-    elif not opts.get('force'):
-        for n in names:
-            if n in repo.tags():
-                raise util.Abort(_("tag '%s' already exists "
-                                   "(use -f to force)") % n)
-    if not opts.get('local'):
-        p1, p2 = repo.dirstate.parents()
-        if p2 != nullid:
-            raise util.Abort(_('uncommitted merge'))
-        bheads = repo.branchheads()
-        if not opts.get('force') and bheads and p1 not in bheads:
-            raise util.Abort(_('not at a branch head (use -f to force)'))
-    r = scmutil.revsingle(repo, rev_).node()
-
-    if not message:
-        # we don't translate commit messages
-        message = ('Added tag %s for changeset %s' %
-                   (', '.join(names), short(r)))
-
-    date = opts.get('date')
-    if date:
-        date = util.parsedate(date)
-
-    if opts.get('edit'):
-        message = ui.edit(message, ui.username())
-
-    repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
+            message = ('Added tag %s for changeset %s' %
+                       (', '.join(names), short(r)))
+
+        date = opts.get('date')
+        if date:
+            date = util.parsedate(date)
+
+        if opts.get('edit'):
+            message = ui.edit(message, ui.username())
+
+        repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
+    finally:
+        release(lock, wlock)
 
 @command('tags', [], '')
 def tags(ui, repo):

File mercurial/context.py

 
         returns True if different than fctx.
         """
-        if (fctx._filerev is None and self._repo._encodefilterpats
+        if (fctx._filerev is None
+            and (self._repo._encodefilterpats
+                 # if file data starts with '\1\n', empty metadata block is
+                 # prepended, which adds 4 bytes to filelog.size().
+                 or self.size() - 4 == fctx.size())
             or self.size() == fctx.size()):
             return self._filelog.cmp(self._filenode, fctx.data())
 

File mercurial/discovery.py

     changesets need to be pushed to the remote. Return value depends
     on circumstances:
 
-    If we are not going to push anything, return a tuple (None,
-    outgoing, common) where outgoing is 0 if there are no outgoing
-    changesets and 1 if there are, but we refuse to push them
-    (e.g. would create new remote heads). The third element "common"
-    is the list of heads of the common set between local and remote.
+    If we are not going to push anything, return a tuple (None, 1,
+    common) The third element "common" is the list of heads of the
+    common set between local and remote.
 
     Otherwise, return a tuple (changegroup, remoteheads, futureheads),
     where changegroup is a readable file-like object whose read()

File mercurial/help/config.txt

 various actions such as starting or finishing a commit. Multiple
 hooks can be run for the same action by appending a suffix to the
 action. Overriding a site-wide hook can be done by changing its
-value or setting it to an empty string.
+value or setting it to an empty string.  Hooks can be prioritized
+by adding a prefix of ``priority`` to the hook name on a new line
+and setting the priority.  The default priority is 0 if
+not specified.
 
 Example ``.hg/hgrc``::
 
   incoming =
   incoming.email = /my/email/hook
   incoming.autobuild = /my/build/hook
+  # force autobuild hook to run before other incoming hooks
+  priority.incoming.autobuild = 1
 
 Most hooks are run with environment variables set that give useful
 additional information. For each hook below, the environment

File mercurial/hg.py

             if self.dir_:
                 self.rmtree(self.dir_, True)
 
-    srclock = destlock = dircleanup = None
+    srclock = destwlock = destlock = dircleanup = None
     try:
         abspath = origsource
         if islocal(origsource):
             # we need to re-init the repo after manually copying the data
             # into it
             destrepo = repository(remoteui(ui, peeropts), dest)
+            # we need full recursive locking of the new repo instance
+            destwlock = destrepo.wlock()
+            if destlock:
+                destlock.release() # a little race condition - but no deadlock
+            destlock = destrepo.lock()
             srcrepo.hook('outgoing', source='clone',
                           node=node.hex(node.nullid))
         else:
 
         return srcrepo, destrepo
     finally:
-        release(srclock, destlock)
+        release(srclock, destlock, destwlock)
         if dircleanup is not None:
             dircleanup.cleanup()
 

File mercurial/hook.py

         ui.warn(_('warning: %s hook %s\n') % (name, desc))
     return r
 
+def _allhooks(ui):
+    hooks = []
+    for name, cmd in ui.configitems('hooks'):
+        if not name.startswith('priority'):
+            priority = ui.configint('hooks', 'priority.%s' % name, 0)
+            hooks.append((-priority, len(hooks), name, cmd))
+    return [(k, v) for p, o, k, v in sorted(hooks)]
+
 _redirect = False
 def redirect(state):
     global _redirect
             pass
 
     try:
-        for hname, cmd in ui.configitems('hooks'):
+        for hname, cmd in _allhooks(ui):
             if hname.split('.')[0] != name or not cmd:
                 continue
             if util.safehasattr(cmd, '__call__'):

File mercurial/localrepo.py

     def updatebranchcache(self):
         tip = self.changelog.tip()
         if self._branchcache is not None and self._branchcachetip == tip:
-            return self._branchcache
+            return
 
         oldtip = self._branchcachetip
         self._branchcachetip = tip
 
     def known(self, nodes):
         nm = self.changelog.nodemap
-        return [(n in nm) for n in nodes]
+        result = []
+        for n in nodes:
+            r = nm.get(n)
+            resp = not (r is None or self._phaserev[r] >= phases.secret)
+            result.append(resp)
+        return result
 
     def local(self):
         return self
 
+    def cancopy(self):
+        return (repo.repository.cancopy(self)
+                and not self._phaseroots[phases.secret])
+
     def join(self, f):
         return os.path.join(self.path, f)
 
                 # if minimal phase was 0 we don't need to retract anything
                 phases.retractboundary(self, targetphase, [n])
             tr.close()
-
-            if self._branchcache:
-                self.updatebranchcache()
+            self.updatebranchcache()
             return n
         finally:
             if tr:
             if remotephases and not publishing:
                 # remote is new and unpublishing
                 subset = common + added
-                rheads, rroots = phases.analyzeremotephases(self, subset,
-                                                            remotephases)
-                for phase, boundary in enumerate(rheads):
-                    phases.advanceboundary(self, phase, boundary)
+                pheads, _dr = phases.analyzeremotephases(self, subset,
+                                                         remotephases)
+                phases.advanceboundary(self, phases.public, pheads)
+                phases.advanceboundary(self, phases.draft, common + added)
             else:
                 # Remote is old or publishing all common changesets
                 # should be seen as public
                     # don't push any phase data as there is nothing to push
                 else:
                     ana = phases.analyzeremotephases(self, fut, remotephases)
-                    rheads, rroots = ana
+                    pheads, droots = ana
                     ### Apply remote phase on local
                     if remotephases.get('publishing', False):
                         phases.advanceboundary(self, phases.public, fut)
                     else: # publish = False
-                        for phase, rpheads in enumerate(rheads):
-                            phases.advanceboundary(self, phase, rpheads)
+                        phases.advanceboundary(self, phases.public, pheads)
+                        phases.advanceboundary(self, phases.draft, fut)
                     ### Apply local phase on remote
                     #
                     # XXX If push failed we should use strict common and not
-                    # future to avoir pushing phase data on unknown changeset.
+                    # future to avoid pushing phase data on unknown changeset.
                     # This is to done later.
 
-                    # element we want to push
-                    topush = []
-
-                    # store details of known remote phase of several revision
-                    # /!\ set of index I holds rev where: I <= rev.phase()
-                    # /!\ public phase (index 0) is ignored
-                    remdetails = [set() for i in xrange(len(phases.allphases))]
-                    _revs = set()
-                    for relremphase in phases.trackedphases[::-1]:
-                        # we iterate backward because the list alway grows
-                        # when filled in this direction.
-                        _revs.update(self.revs('%ln::%ln',
-                                               rroots[relremphase], fut))
-                        remdetails[relremphase].update(_revs)
-
-                    for phase in phases.allphases[:-1]:
-                        # We don't need the last phase as we will never want to
-                        # move anything to it while moving phase backward.
-
-                        # Get the list of all revs on remote which are in a
-                        # phase higher than currently processed phase.
-                        relremrev = remdetails[phase + 1]
-
-                        if not relremrev:
-                            # no candidate to remote push anymore
-                            # break before any expensive revset
-                            break
-
-                        #dynamical inject appropriate phase symbol
-                        phasename = phases.phasenames[phase]
-                        odrevset = 'heads(%%ld and %s())' % phasename
-                        outdated =  self.set(odrevset, relremrev)
-                        for od in outdated:
-                            candstart = len(remdetails) - 1
-                            candstop = phase + 1
-                            candidateold = xrange(candstart, candstop, -1)
-                            for oldphase in candidateold:
-                                if od.rev() in remdetails[oldphase]:
-                                    break
-                            else: # last one: no need to search
-                                oldphase = phase + 1
-                            topush.append((oldphase, phase, od))
-
-                    # push every needed data
-                    for oldphase, newphase, newremotehead in topush:
+                    # Get the list of all revs draft on remote by public here.
+                    # XXX Beware that revset break if droots is not strictly
+                    # XXX root we may want to ensure it is but it is costly
+                    outdated =  self.set('heads((%ln::%ln) and public())',
+                                         droots, fut)
+                    for newremotehead in outdated:
                         r = remote.pushkey('phases',
                                            newremotehead.hex(),
-                                           str(oldphase), str(newphase))
+                                           str(phases.draft),
+                                           str(phases.public))
                         if not r:
-                            self.ui.warn(_('updating phase of %s '
-                                           'to %s from %s failed!\n')
-                                            % (newremotehead, newphase,
-                                               oldphase))
+                            self.ui.warn(_('updating %s to public failed!\n')
+                                            % newremotehead)
             finally:
                 locallock.release()
         finally:
             source.callback = pr
 
             source.changelogheader()
-            if (cl.addgroup(source, csmap, trp) is None
-                and not emptyok):
+            srccontent = cl.addgroup(source, csmap, trp)
+            if not (srccontent or emptyok):
                 raise util.Abort(_("received changelog group is empty"))
             clend = len(cl)
             changesets = clend - clstart
                 pr()
                 fl = self.file(f)
                 o = len(fl)
-                if fl.addgroup(source, revmap, trp) is None:
+                if not fl.addgroup(source, revmap, trp):
                     raise util.Abort(_("received file revlog group is empty"))
                 revisions += len(fl) - o
                 files += 1
 
             added = [cl.node(r) for r in xrange(clstart, clend)]
             publishing = self.ui.configbool('phases', 'publish', True)
-            if publishing and srctype == 'push':
+            if srctype == 'push':
                 # Old server can not push the boundary themself.
-                # This clause ensure pushed changeset are alway marked as public
-                phases.advanceboundary(self, phases.public, added)
-            elif srctype != 'strip': # strip should not touch boundary at all
+                # New server won't push the boundary if changeset already
+                # existed locally as secrete
+                #
+                # We should not use added here but the list of all change in
+                # the bundle
+                if publishing:
+                    phases.advanceboundary(self, phases.public, srccontent)
+                else:
+                    phases.advanceboundary(self, phases.draft, srccontent)
+                    phases.retractboundary(self, phases.draft, added)
+            elif srctype != 'strip':
+                # publishing only alter behavior during push
+                #
+                # strip should not touch boundary at all
                 phases.retractboundary(self, phases.draft, added)
 
             # make changelog see real files again

File mercurial/merge.py

     folded = {}
     for fn in mctx:
         folded[foldf(fn)] = fn
+
+    error = False
     for fn in wctx.unknown():
         f = foldf(fn)
         if f in folded and mctx[folded[f]].cmp(wctx[f]):
-            raise util.Abort(_("untracked file in working directory differs"
-                               " from file in requested revision: '%s'") % fn)
+            error = True
+            wctx._repo.ui.warn(_("%s: untracked file differs\n") % fn)
+    if error:
+        raise util.Abort(_("untracked files in working directory differ "
+                           "from files in requested revision"))
 
 def _checkcollision(mctx, wctx):
     "check for case folding collisions in the destination context"

File mercurial/minirst.py

         i += 1
     return blocks
 
-_fieldwidth = 12
+_fieldwidth = 14
 
 def updatefieldlists(blocks):
-    """Find key and maximum key width for field lists."""
+    """Find key for field lists."""
     i = 0
     while i < len(blocks):
         if blocks[i]['type'] != 'field':
             i += 1
             continue
 
-        keywidth = 0
         j = i
         while j < len(blocks) and blocks[j]['type'] == 'field':
             m = _fieldre.match(blocks[j]['lines'][0])
             key, rest = m.groups()
             blocks[j]['lines'][0] = rest
             blocks[j]['key'] = key
-            keywidth = max(keywidth, len(key))
             j += 1
 
-        for block in blocks[i:j]:
-            block['keywidth'] = keywidth
         i = j + 1
 
     return blocks
             m = _bulletre.match(block['lines'][0])
             subindent = indent + m.end() * ' '
     elif block['type'] == 'field':
-        keywidth = block['keywidth']
         key = block['key']
-
         subindent = indent + _fieldwidth * ' '
         if len(key) + 2 > _fieldwidth:
             # key too large, use full line width
             key = key.ljust(width)
-        elif keywidth + 2 < _fieldwidth:
-            # all keys are small, add only two spaces
-            key = key.ljust(keywidth + 2)
-            subindent = indent + (keywidth + 2) * ' '
         else:
-            # mixed sizes, use fieldwidth for this one
+            # key fits within field width
             key = key.ljust(_fieldwidth)
         block['lines'][0] = key + block['lines'][0]
     elif block['type'] == 'option':

File mercurial/phases.py

 def listphases(repo):
     """List phases root for serialisation over pushkey"""
     keys = {}
-    for phase in trackedphases:
-        for root in repo._phaseroots[phase]:
-            keys[hex(root)] = '%i' % phase
+    value = '%i' % draft
+    for root in repo._phaseroots[draft]:
+        keys[hex(root)] = value
+
     if repo.ui.configbool('phases', 'publish', True):
         # Add an extra data to let remote know we are a publishing repo.
         # Publishing repo can't just pretend they are old repo. When pushing to
     Accept unknown element input
     """
     # build list from dictionary
-    phaseroots = [[] for p in allphases]
+    draftroots = []
+    nm = repo.changelog.nodemap # to filter unknown node
     for nhex, phase in roots.iteritems():
         if nhex == 'publishing': # ignore data related to publish option
             continue
         node = bin(nhex)
         phase = int(phase)
-        if node in repo:
-            phaseroots[phase].append(node)
+        if phase == 0:
+            if node != nullid:
+                msg = _('ignoring inconsistense public root from remote: %s')
+                repo.ui.warn(msg, nhex)
+        elif phase == 1:
+            if node in nm:
+                draftroots.append(node)
+        else:
+            msg = _('ignoring unexpected root from remote: %i %s')
+            repo.ui.warn(msg, phase, nhex)
     # compute heads
-    phaseheads = [[] for p in allphases]
-    for phase in allphases[:-1]:
-        toproof = phaseroots[phase + 1]
-        revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
-                          subset, toproof, toproof, subset)
-        phaseheads[phase].extend(c.node() for c in revset)
-    return phaseheads, phaseroots
+    revset = repo.set('heads((%ln + parents(%ln)) - (%ln::%ln))',
+                      subset, draftroots, draftroots, subset)
+    publicheads = [c.node() for c in revset]
+    return publicheads, draftroots
 

File mercurial/revlog.py

         """
 
         # track the base of the current delta log
+        content = []
         node = None
 
         r = len(self)
                 deltabase = chunkdata['deltabase']
                 delta = chunkdata['delta']
 
+                content.append(node)
+
                 link = linkmapper(cs)
                 if node in self.nodemap:
                     # this can happen if two branches make the same change
                 dfh.close()
             ifh.close()
 
-        return node
+        return content
 
     def strip(self, minlink, transaction):
         """truncate the revlog on the first revision with a linkrev >= minlink

File mercurial/revset.py

                     break
     return s
 
+def _children(repo, narrow, s):
+    cs = set()
+    pr = repo.changelog.parentrevs
+    s = set(s)
+    for r in narrow:
+        for p in pr(r):
+            if p in s:
+                cs.add(r)
+    return cs
+
 def children(repo, subset, x):
     """``children(set)``
     Child changesets of changesets in set.
     """
-    cs = set()
-    cl = repo.changelog
-    s = set(getset(repo, range(len(repo)), x))
-    for r in xrange(0, len(repo)):
-        for p in cl.parentrevs(r):
-            if p in s:
-                cs.add(r)
+    s = getset(repo, range(len(repo)), x)
+    cs = _children(repo, subset, s)
     return [r for r in subset if r in cs]
 
 def closed(repo, subset, x):
     Changesets with no parent changeset in set.
     """
     s = getset(repo, subset, x)
-    cs = set(children(repo, subset, x))
+    cs = _children(repo, s, s)
     return [r for r in s if r not in cs]
 
 def secret(repo, subset, x):
     """
     return author(repo, subset, x)
 
+# for internal use
+def _list(repo, subset, x):
+    s = getstring(x, "internal error")
+    if not s:
+        return []
+    if not isinstance(subset, set):
+        subset = set(subset)
+    ls = [repo[r].rev() for r in s.split('\0')]
+    return [r for r in ls if r in subset]
+
 symbols = {
     "adds": adds,
     "all": getall,
     "tag": tag,
     "tagged": tagged,
     "user": user,
+    "_list": _list,
 }
 
 methods = {
     >>> formatspec('%d:: and not %d::', 10, 20)
     '10:: and not 20::'
     >>> formatspec('%ld or %ld', [], [1])
-    '(0-0) or 1'
+    "_list('') or 1"
     >>> formatspec('keyword(%s)', 'foo\\xe9')
     "keyword('foo\\\\xe9')"
     >>> b = lambda: 'default'
     >>> formatspec('branch(%b)', b)
     "branch('default')"
     >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
-    "root((('a' or 'b') or ('c' or 'd')))"
+    "root(_list('a\\x00b\\x00c\\x00d'))"
     '''
 
     def quote(s):
             return quote(arg.branch())
 
     def listexp(s, t):
-        "balance a list s of type t to limit parse tree depth"
         l = len(s)
         if l == 0:
-            return '(0-0)' # a minimal way to represent an empty set
-        if l == 1:
+            return "_list('')"
+        elif l == 1:
             return argtype(t, s[0])
+        elif t == 'd':
+            return "_list('%s')" % "\0".join(str(int(a)) for a in s)
+        elif t == 's':
+            return "_list('%s')" % "\0".join(s)
+        elif t == 'n':
+            return "_list('%s')" % "\0".join(nodemod.hex(a) for a in s)
+        elif t == 'b':
+            return "_list('%s')" % "\0".join(a.branch() for a in s)
+
         m = l // 2
         return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
 

File tests/run-tests.py

              " rather than capturing and diff'ing it (disables timeout)")
     parser.add_option("-f", "--first", action="store_true",
         help="exit on the first test failure")
+    parser.add_option("-H", "--htmlcov", action="store_true",
+        help="create an HTML report of the coverage of the files")
     parser.add_option("--inotify", action="store_true",
         help="enable inotify extension when running tests")
     parser.add_option("-i", "--interactive", action="store_true",
                          % hgbin)
         options.with_hg = hgbin
 
-    options.anycoverage = options.cover or options.annotate
+    options.anycoverage = options.cover or options.annotate or options.htmlcov
     if options.anycoverage:
         try:
             import coverage
         return
 
     covrun('-c')
-    omit = ','.join([BINDIR, TESTDIR])
+    omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
     covrun('-i', '-r', '"--omit=%s"' % omit) # report
+    if options.htmlcov:
+        htmldir = os.path.join(TESTDIR, 'htmlcov')
+        covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
     if options.annotate:
         adir = os.path.join(TESTDIR, 'annotated')
         if not os.path.isdir(adir):

File tests/test-alias.t

   
   basic commands:
   
-   add        add the specified files on the next commit
-   annotate   show changeset information by line for each file
-   clone      make a copy of an existing repository
-   commit     commit the specified files or all outstanding changes
-   diff       diff repository (or selected files)
-   export     dump the header and diffs for one or more changesets
-   forget     forget the specified files on the next commit
-   init       create a new repository in the given directory
-   log        show revision history of entire repository or files
-   merge      merge working directory with another revision
-   phase      set or show the current phase name
-   pull       pull changes from the specified source
-   push       push changes to the specified destination
-   remove     remove the specified files on the next commit
-   serve      start stand-alone webserver
-   status     show changed files in the working directory
-   summary    summarize working directory state
-   update     update working directory (or switch revisions)
+   add         add the specified files on the next commit
+   annotate    show changeset information by line for each file
+   clone       make a copy of an existing repository
+   commit      commit the specified files or all outstanding changes
+   diff        diff repository (or selected files)
+   export      dump the header and diffs for one or more changesets
+   forget      forget the specified files on the next commit
+   init        create a new repository in the given directory
+   log         show revision history of entire repository or files
+   merge       merge working directory with another revision
+   phase       set or show the current phase name
+   pull        pull changes from the specified source
+   push        push changes to the specified destination
+   remove      remove the specified files on the next commit
+   serve       start stand-alone webserver
+   status      show changed files in the working directory
+   summary     summarize working directory state
+   update      update working directory (or switch revisions)
   
   use "hg help" for the full list of commands or "hg -v" for details
   [255]
   
   basic commands:
   
-   add        add the specified files on the next commit
-   annotate   show changeset information by line for each file
-   clone      make a copy of an existing repository
-   commit     commit the specified files or all outstanding changes
-   diff       diff repository (or selected files)
-   export     dump the header and diffs for one or more changesets
-   forget     forget the specified files on the next commit
-   init       create a new repository in the given directory
-   log        show revision history of entire repository or files
-   merge      merge working directory with another revision
-   phase      set or show the current phase name
-   pull       pull changes from the specified source
-   push       push changes to the specified destination
-   remove     remove the specified files on the next commit
-   serve      start stand-alone webserver
-   status     show changed files in the working directory
-   summary    summarize working directory state
-   update     update working directory (or switch revisions)
+   add         add the specified files on the next commit
+   annotate    show changeset information by line for each file
+   clone       make a copy of an existing repository
+   commit      commit the specified files or all outstanding changes
+   diff        diff repository (or selected files)
+   export      dump the header and diffs for one or more changesets
+   forget      forget the specified files on the next commit
+   init        create a new repository in the given directory
+   log         show revision history of entire repository or files
+   merge       merge working directory with another revision
+   phase       set or show the current phase name
+   pull        pull changes from the specified source
+   push        push changes to the specified destination
+   remove      remove the specified files on the next commit
+   serve       start stand-alone webserver
+   status      show changed files in the working directory
+   summary     summarize working directory state
+   update      update working directory (or switch revisions)
   
   use "hg help" for the full list of commands or "hg -v" for details
   [255]
   
   basic commands:
   
-   add        add the specified files on the next commit
-   annotate   show changeset information by line for each file
-   clone      make a copy of an existing repository
-   commit     commit the specified files or all outstanding changes
-   diff       diff repository (or selected files)
-   export     dump the header and diffs for one or more changesets
-   forget     forget the specified files on the next commit
-   init       create a new repository in the given directory
-   log        show revision history of entire repository or files
-   merge      merge working directory with another revision
-   phase      set or show the current phase name
-   pull       pull changes from the specified source
-   push       push changes to the specified destination
-   remove     remove the specified files on the next commit
-   serve      start stand-alone webserver
-   status     show changed files in the working directory
-   summary    summarize working directory state
-   update     update working directory (or switch revisions)
+   add         add the specified files on the next commit
+   annotate    show changeset information by line for each file
+   clone       make a copy of an existing repository
+   commit      commit the specified files or all outstanding changes
+   diff        diff repository (or selected files)
+   export      dump the header and diffs for one or more changesets