changeset 20153:35bfe5405fb4 stable

merge with i18n
author Matt Mackall <mpm@selenic.com>
date Sun, 01 Dec 2013 13:45:00 -0600
parents 84939b728749 (diff) 65bc57780406 (current diff)
children 970394b6bd97
files
diffstat 13 files changed, 271 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/sample.hgrc	Sat Nov 30 21:50:40 2013 -0200
+++ b/contrib/sample.hgrc	Sun Dec 01 13:45:00 2013 -0600
@@ -43,20 +43,15 @@
 
 # hgext.gpg =
 
-### graphlog - ASCII graph log
-### hg help glog
-
-# hgext.graphlog =
-
 ### hgk - GUI repository browser
 ### hg help view
 
 # hgext.hgk =
 
-### mq - Mercurial patch queues
-### hg help mq
+### strip - Remove changesets and their descendents from history
+### hg help strip
 
-# hgext.mq =
+# hgext.strip =
 
 ### notify - Template driven e-mail notifications
 ### hg help notify
--- a/contrib/win32/mercurial.ini	Sat Nov 30 21:50:40 2013 -0200
+++ b/contrib/win32/mercurial.ini	Sun Dec 01 13:45:00 2013 -0600
@@ -46,7 +46,6 @@
 ;extdiff =
 ;fetch =
 ;gpg =
-;graphlog = 
 ;hgcia =
 ;hgk =
 ;highlight = 
--- a/hgext/largefiles/overrides.py	Sat Nov 30 21:50:40 2013 -0200
+++ b/hgext/largefiles/overrides.py	Sun Dec 01 13:45:00 2013 -0600
@@ -380,7 +380,7 @@
     for action in actions:
         f, m, args, msg = action
 
-        splitstandin = lfutil.splitstandin(f)
+        splitstandin = f and lfutil.splitstandin(f)
         if (m == "g" and splitstandin is not None and
             splitstandin in p1 and splitstandin not in removes):
             # Case 1: normal file in the working copy, largefile in
--- a/hgext/shelve.py	Sat Nov 30 21:50:40 2013 -0200
+++ b/hgext/shelve.py	Sun Dec 01 13:45:00 2013 -0600
@@ -91,7 +91,6 @@
             pendingctx = fp.readline().strip()
             parents = [bin(h) for h in fp.readline().split()]
             stripnodes = [bin(h) for h in fp.readline().split()]
-            unknownfiles = fp.readline()[:-1].split('\0')
         finally:
             fp.close()
 
@@ -101,13 +100,11 @@
         obj.pendingctx = repo[bin(pendingctx)]
         obj.parents = parents
         obj.stripnodes = stripnodes
-        obj.unknownfiles = unknownfiles
 
         return obj
 
     @classmethod
-    def save(cls, repo, name, originalwctx, pendingctx, stripnodes,
-             unknownfiles):
+    def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
         fp = repo.opener(cls._filename, 'wb')
         fp.write('%i\n' % cls._version)
         fp.write('%s\n' % name)
@@ -115,7 +112,6 @@
         fp.write('%s\n' % hex(pendingctx.node()))
         fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
         fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
-        fp.write('%s\n' % '\0'.join(unknownfiles))
         fp.close()
 
     @classmethod
@@ -379,7 +375,7 @@
 
         lock = repo.lock()
 
-        mergefiles(ui, repo, state.wctx, state.pendingctx, state.unknownfiles)
+        mergefiles(ui, repo, state.wctx, state.pendingctx)
 
         repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
         shelvedstate.clear(repo)
@@ -387,9 +383,9 @@
     finally:
         lockmod.release(lock, wlock)
 
-def mergefiles(ui, repo, wctx, shelvectx, unknownfiles):
+def mergefiles(ui, repo, wctx, shelvectx):
     """updates to wctx and merges the changes from shelvectx into the
-    dirstate. drops any files in unknownfiles from the dirstate."""
+    dirstate."""
     oldquiet = ui.quiet
     try:
         ui.quiet = True
@@ -397,17 +393,18 @@
         files = []
         files.extend(shelvectx.files())
         files.extend(shelvectx.parents()[0].files())
+
+        # revert will overwrite unknown files, so move them out of the way
+        m, a, r, d, u = repo.status(unknown=True)[:5]
+        for file in u:
+            if file in files:
+                util.rename(file, file + ".orig")
         cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
                        *pathtofiles(repo, files),
                        **{'no_backup': True})
     finally:
         ui.quiet = oldquiet
 
-    # Send untracked files back to being untracked
-    dirstate = repo.dirstate
-    for f in unknownfiles:
-        dirstate.drop(f)
-
 def unshelvecleanup(ui, repo, name, opts):
     """remove related files after an unshelve"""
     if not opts['keep']:
@@ -446,7 +443,7 @@
             # rebase was a no-op, so it produced no child commit
             shelvectx = state.pendingctx
 
-        mergefiles(ui, repo, state.wctx, shelvectx, state.unknownfiles)
+        mergefiles(ui, repo, state.wctx, shelvectx)
 
         state.stripnodes.append(shelvectx.node())
         repair.strip(ui, repo, state.stripnodes, backup='none', topic='shelve')
@@ -538,8 +535,8 @@
         # to the original wctx.
 
         # Store pending changes in a commit
-        m, a, r, d, u = repo.status(unknown=True)[:5]
-        if m or a or r or d or u:
+        m, a, r, d = repo.status()[:4]
+        if m or a or r or d:
             def commitfunc(ui, repo, message, match, opts):
                 hasmq = util.safehasattr(repo, 'mq')
                 if hasmq:
@@ -554,7 +551,6 @@
 
             tempopts = {}
             tempopts['message'] = "pending changes temporary commit"
-            tempopts['addremove'] = True
             oldquiet = ui.quiet
             try:
                 ui.quiet = True
@@ -588,7 +584,7 @@
 
                 stripnodes = [repo.changelog.node(rev)
                               for rev in xrange(oldtiprev, len(repo))]
-                shelvedstate.save(repo, basename, wctx, tmpwctx, stripnodes, u)
+                shelvedstate.save(repo, basename, wctx, tmpwctx, stripnodes)
 
                 util.rename(repo.join('rebasestate'),
                             repo.join('unshelverebasestate'))
@@ -603,7 +599,7 @@
                 # rebase was a no-op, so it produced no child commit
                 shelvectx = tmpwctx
 
-        mergefiles(ui, repo, wctx, shelvectx, u)
+        mergefiles(ui, repo, wctx, shelvectx)
         shelvedstate.clear(repo)
 
         # The transaction aborting will strip all the commits for us,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/i18n/check-translation.py	Sun Dec 01 13:45:00 2013 -0600
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+#
+# check-translation.py - check Mercurial specific translation problems
+
+import polib
+import re
+
+checkers = []
+
+def checker(level, msgidpat):
+    def decorator(func):
+        if msgidpat:
+            match = re.compile(msgidpat).search
+        else:
+            match = lambda msgid: True
+        checkers.append((func, level))
+        func.match = match
+        return func
+    return decorator
+
+def match(checker, pe):
+    """Examine whether POEntry "pe" is target of specified checker or not
+    """
+    if not checker.match(pe.msgid):
+        return
+    # examine suppression by translator comment
+    nochecker = 'no-%s-check' % checker.__name__
+    for tc in pe.tcomment.split():
+        if nochecker == tc:
+            return
+    return True
+
+####################
+
+def fatalchecker(msgidpat=None):
+    return checker('fatal', msgidpat)
+
+@fatalchecker(r'\$\$')
+def promptchoice(pe):
+    """Check translation of the string given to "ui.promptchoice()"
+
+    >>> pe = polib.POEntry(
+    ...     msgid ='prompt$$missing &sep$$missing &amp$$followed by &none',
+    ...     msgstr='prompt  missing &sep$$missing  amp$$followed by none&')
+    >>> match(promptchoice, pe)
+    True
+    >>> for e in promptchoice(pe): print e
+    number of choices differs between msgid and msgstr
+    msgstr has invalid choice missing '&'
+    msgstr has invalid '&' followed by none
+    """
+    idchoices = [c.rstrip(' ') for c in pe.msgid.split('$$')[1:]]
+    strchoices = [c.rstrip(' ') for c in pe.msgstr.split('$$')[1:]]
+
+    if len(idchoices) != len(strchoices):
+        yield "number of choices differs between msgid and msgstr"
+
+    indices = [(c, c.find('&')) for c in strchoices]
+    if [c for c, i in indices if i == -1]:
+        yield "msgstr has invalid choice missing '&'"
+    if [c for c, i in indices if len(c) == i + 1]:
+        yield "msgstr has invalid '&' followed by none"
+
+####################
+
+def warningchecker(msgidpat=None):
+    return checker('warning', msgidpat)
+
+####################
+
+def check(pofile, fatal=True, warning=False):
+    targetlevel = { 'fatal': fatal, 'warning': warning }
+    targetcheckers = [(checker, level)
+                      for checker, level in checkers
+                      if targetlevel[level]]
+    if not targetcheckers:
+        return []
+
+    detected = []
+    for pe in pofile.translated_entries():
+        errors = []
+        for checker, level in targetcheckers:
+            if match(checker, pe):
+                errors.extend((level, checker.__name__, error)
+                              for error in checker(pe))
+        if errors:
+            detected.append((pe, errors))
+    return detected
+
+########################################
+
+if __name__ == "__main__":
+    import sys
+    import optparse
+
+    optparser = optparse.OptionParser("""%prog [options] pofile ...
+
+This checks Mercurial specific translation problems in specified
+'*.po' files.
+
+Each detected problems are shown in the format below::
+
+    filename:linenum:type(checker): problem detail .....
+
+"type" is "fatal" or "warning". "checker" is the name of the function
+detecting corresponded error.
+
+Checking by checker "foo" on the specific msgstr can be suppressed by
+the "translator comment" like below. Multiple "no-xxxx-check" should
+be separated by whitespaces::
+
+    # no-foo-check
+    msgid = "....."
+    msgstr = "....."
+""")
+    optparser.add_option("", "--warning",
+                         help="show also warning level problems",
+                         action="store_true")
+    optparser.add_option("", "--doctest",
+                         help="run doctest of this tool, instead of check",
+                         action="store_true")
+    (options, args) = optparser.parse_args()
+
+    if options.doctest:
+        import doctest
+        failures, tests = doctest.testmod()
+        sys.exit(failures and 1 or 0)
+
+    # replace polib._POFileParser to show linenum of problematic msgstr
+    class ExtPOFileParser(polib._POFileParser):
+        def process(self, symbol, linenum):
+            super(ExtPOFileParser, self).process(symbol, linenum)
+            if symbol == 'MS': # msgstr
+                self.current_entry.linenum = linenum
+    polib._POFileParser = ExtPOFileParser
+
+    detected = []
+    warning = options.warning
+    for f in args:
+        detected.extend((f, pe, errors)
+                        for pe, errors in check(polib.pofile(f),
+                                                warning=warning))
+    if detected:
+        for f, pe, errors in detected:
+            for level, checker, error in errors:
+                sys.stderr.write('%s:%d:%s(%s): %s\n'
+                                 % (f, pe.linenum, level, checker, error))
+        sys.exit(1)
--- a/mercurial/commands.py	Sat Nov 30 21:50:40 2013 -0200
+++ b/mercurial/commands.py	Sun Dec 01 13:45:00 2013 -0600
@@ -538,7 +538,7 @@
 
       Some examples:
 
-      - start a bisection with known bad revision 12, and good revision 34::
+      - start a bisection with known bad revision 34, and good revision 12::
 
           hg bisect --bad 34
           hg bisect --good 12
@@ -585,7 +585,7 @@
 
           hg log -r "bisect(range)"
 
-      - with the graphlog extension, you can even get a nice graph::
+      - you can even get a nice graph::
 
           hg log --graph -r "bisect(range)"
 
--- a/mercurial/help/config.txt	Sat Nov 30 21:50:40 2013 -0200
+++ b/mercurial/help/config.txt	Sun Dec 01 13:45:00 2013 -0600
@@ -495,8 +495,8 @@
 Example for ``~/.hgrc``::
 
   [extensions]
-  # (the mq extension will get loaded from Mercurial's path)
-  mq =
+  # (the progress extension will get loaded from Mercurial's path)
+  progress =
   # (this extension will get loaded from the file specified)
   myfeature = ~/.hgext/myfeature.py
 
--- a/mercurial/help/glossary.txt	Sat Nov 30 21:50:40 2013 -0200
+++ b/mercurial/help/glossary.txt	Sun Dec 01 13:45:00 2013 -0600
@@ -173,9 +173,9 @@
     system (DVCS) can be described as a directed acyclic graph (DAG),
     consisting of nodes and edges, where nodes correspond to
     changesets and edges imply a parent -> child relation. This graph
-    can be visualized by graphical tools such as :hg:`glog`
-    (graphlog). In Mercurial, the DAG is limited by the requirement
-    for children to have at most two parents.
+    can be visualized by graphical tools such as :hg:`log --graph`. In
+    Mercurial, the DAG is limited by the requirement for children to
+    have at most two parents.
 
 Default branch
     See 'Branch, default'.
@@ -218,7 +218,7 @@
     extensions. See :hg:`help phases`.
 
 Graph
-    See DAG and :hg:`help graphlog`.
+    See DAG and :hg:`log --graph`.
 
 Head
     The term 'head' may be used to refer to both a branch head or a
--- a/mercurial/parsers.c	Sat Nov 30 21:50:40 2013 -0200
+++ b/mercurial/parsers.c	Sun Dec 01 13:45:00 2013 -0600
@@ -1713,6 +1713,15 @@
 	PyObject *data_obj, *inlined_obj;
 	Py_ssize_t size;
 
+	/* Initialize before argument-checking to avoid index_dealloc() crash. */
+	self->raw_length = 0;
+	self->added = NULL;
+	self->cache = NULL;
+	self->data = NULL;
+	self->headrevs = NULL;
+	self->nt = NULL;
+	self->offsets = NULL;
+
 	if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
 		return -1;
 	if (!PyString_Check(data_obj)) {
@@ -1723,12 +1732,7 @@
 
 	self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
 	self->data = data_obj;
-	self->cache = NULL;
 
-	self->added = NULL;
-	self->headrevs = NULL;
-	self->offsets = NULL;
-	self->nt = NULL;
 	self->ntlength = self->ntcapacity = 0;
 	self->ntdepth = self->ntsplits = 0;
 	self->ntlookups = self->ntmisses = 0;
@@ -1764,7 +1768,7 @@
 static void index_dealloc(indexObject *self)
 {
 	_index_clearcaches(self);
-	Py_DECREF(self->data);
+	Py_XDECREF(self->data);
 	Py_XDECREF(self->added);
 	PyObject_Del(self);
 }
--- a/tests/test-i18n.t	Sat Nov 30 21:50:40 2013 -0200
+++ b/tests/test-i18n.t	Sun Dec 01 13:45:00 2013 -0600
@@ -38,3 +38,10 @@
   
    pager Verwendet einen externen Pager zum Bl\xc3\xa4ttern in der Ausgabe von Befehlen (esc)
 
+Check Mercurial specific translation problems in each *.po files, and
+tool itself by doctest
+
+  $ cd "$TESTDIR"/../i18n
+  $ python check-translation.py *.po
+  $ python check-translation.py --doctest
+  $ cd $TESTTMP
--- a/tests/test-largefiles.t	Sat Nov 30 21:50:40 2013 -0200
+++ b/tests/test-largefiles.t	Sun Dec 01 13:45:00 2013 -0600
@@ -2192,6 +2192,39 @@
 
   $ cd ..
 
+merge action 'd' for 'local renamed directory to d2/g' which has no filename
+
+  $ hg init merge-action
+  $ cd merge-action
+  $ touch l
+  $ hg add --large l
+  $ mkdir d1
+  $ touch d1/f
+  $ hg ci -Aqm0
+  Invoking status precommit hook
+  A d1/f
+  A l
+  $ echo > d1/f
+  $ touch d1/g
+  $ hg ci -Aqm1
+  Invoking status precommit hook
+  M d1/f
+  A d1/g
+  $ hg up -qr0
+  $ hg mv d1 d2
+  moving d1/f to d2/f (glob)
+  $ hg ci -qm2
+  Invoking status precommit hook
+  A d2/f
+  R d1/f
+  $ hg merge
+  merging d2/f and d1/f to d2/f
+  1 files updated, 1 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  getting changed largefiles
+  0 largefiles updated, 0 removed
+  $ cd ..
+
 Check whether "largefiles" feature is supported only in repositories
 enabling largefiles extension.
 
--- a/tests/test-parseindex2.py	Sat Nov 30 21:50:40 2013 -0200
+++ b/tests/test-parseindex2.py	Sun Dec 01 13:45:00 2013 -0600
@@ -98,6 +98,14 @@
     return list(index), chunkcache
 
 def runtest() :
+    # Check that parse_index2() raises TypeError on bad arguments.
+    try:
+        parse_index2(0, True)
+    except TypeError:
+        pass
+    else:
+        print "Expected to get TypeError."
+
     py_res_1 = py_parseindex(data_inlined, True)
     c_res_1 = parse_index2(data_inlined, True)
 
--- a/tests/test-shelve.t	Sat Nov 30 21:50:40 2013 -0200
+++ b/tests/test-shelve.t	Sun Dec 01 13:45:00 2013 -0600
@@ -539,4 +539,42 @@
   adding file changes
   added 1 changesets with 1 changes to 2 files (+1 heads)
 
+unshelve should leave unknown files alone (issue4113)
+
+  $ echo e > e
+  $ hg shelve
+  shelved as default
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ hg status
+  ? e
+  $ hg unshelve
+  unshelving change 'default'
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 2 files (+1 heads)
+  $ hg status
+  A d
+  ? e
+  $ cat e
+  e
+
+unshelve should keep a copy of unknown files
+
+  $ hg add e
+  $ hg shelve
+  shelved as default
+  0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ echo z > e
+  $ hg unshelve
+  unshelving change 'default'
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 2 changes to 3 files (+1 heads)
+  $ cat e
+  e
+  $ cat e.orig
+  z
+
   $ cd ..