# HG changeset patch # User Matt Mackall # Date 1342656505 18000 # Node ID a06e2681dd1786e2354d84a5fa9c1c88dd4fa3e0 # Parent 98823bd0d697e067ae060f122a5ea7ca92e5e6a8# Parent 23b247234454c945ff0b907dd6195a385c134468 merge default into stable for 2.3 code freeze diff -r 98823bd0d697 -r a06e2681dd17 .hgignore --- a/.hgignore Thu Jul 19 00:53:27 2012 +0200 +++ b/.hgignore Wed Jul 18 19:08:25 2012 -0500 @@ -8,6 +8,7 @@ *.o *.so *.dll +*.exe *.pyd *.pyc *.pyo @@ -31,7 +32,6 @@ patches mercurial/__version__.py mercurial.egg-info -Output/Mercurial-*.exe .DS_Store tags cscope.* @@ -53,5 +53,4 @@ # hackable windows distribution additions ^hg-python -^hg.exe$ ^hg.py$ diff -r 98823bd0d697 -r a06e2681dd17 Makefile --- a/Makefile Thu Jul 19 00:53:27 2012 +0200 +++ b/Makefile Wed Jul 18 19:08:25 2012 -0500 @@ -33,7 +33,7 @@ all: build doc local: - $(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_mo + $(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_hgexe -i build_mo $(PYTHON) hg version build: diff -r 98823bd0d697 -r a06e2681dd17 contrib/check-code.py --- a/contrib/check-code.py Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/check-code.py Wed Jul 18 19:08:25 2012 -0500 @@ -45,12 +45,10 @@ [ (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"), (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"), - (r'^function', "don't use 'function', use old style"), (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"), (r'sed.*-i', "don't use 'sed -i', use a temporary file"), - (r'echo.*\\n', "don't use 'echo \\n', use printf"), + (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"), (r'echo -n', "don't use 'echo -n', use printf"), - (r'^diff.*-\w*N', "don't use 'diff -N'"), (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"), (r'head -c', "don't use 'head -c', use 'dd'"), (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"), @@ -62,10 +60,8 @@ (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w', "use egrep for extended grep syntax"), (r'/bin/', "don't use explicit paths for tools"), - (r'\$PWD', "don't use $PWD, use `pwd`"), (r'[^\n]\Z', "no trailing newline"), (r'export.*=', "don't export and assign at once"), - (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"), (r'^source\b', "don't use 'source', use '.'"), (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"), (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"), @@ -79,7 +75,12 @@ (r'^( *)\t', "don't use tabs to indent"), ], # warnings - [] + [ + (r'^function', "don't use 'function', use old style"), + (r'^diff.*-\w*N', "don't use 'diff -N'"), + (r'\$PWD', "don't use $PWD, use `pwd`"), + (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"), + ] ] testfilters = [ @@ -91,13 +92,16 @@ utestpats = [ [ (r'^(\S| $ ).*(\S[ \t]+|^[ \t]+)\n', "trailing whitespace on non-output"), - (uprefix + r'.*\|\s*sed', "use regex test output patterns instead of sed"), + (uprefix + r'.*\|\s*sed[^|>\n]*\n', + "use regex test output patterns instead of sed"), (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"), (uprefix + r'.*(? for continued lines"), + (r'^ saved backup bundle to \$TESTTMP.*\.hg$', + "use (glob) to match Windows paths too"), ], # warnings [] @@ -106,9 +110,9 @@ for i in [0, 1]: for p, m in testpats[i]: if p.startswith(r'^'): - p = r"^ \$ (%s)" % p[1:] + p = r"^ [$>] (%s)" % p[1:] else: - p = r"^ \$ .*(%s)" % p + p = r"^ [$>] .*(%s)" % p utestpats[i].append((p, m)) utestfilters = [ @@ -133,11 +137,12 @@ (r'^\s+\w+=\w+[^,)\n]$', "missing whitespace in assignment"), (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n' r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Py2.4'), - (r'.{85}', "line too long"), + (r'.{81}', "line too long"), (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'), (r'[^\n]\Z', "no trailing newline"), (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), -# (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"), +# (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', +# "don't use underbars in identifiers"), (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ', "don't use camelcase in identifiers"), (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+', @@ -167,13 +172,13 @@ "gratuitous whitespace after Python keyword"), (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"), # (r'\s\s=', "gratuitous whitespace before ="), - (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', + (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S', "missing whitespace around operator"), - (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s', + (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s', "missing whitespace around operator"), - (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S', + (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S', "missing whitespace around operator"), - (r'[^^+=*/!<>&| -](\s=|=\s)[^= ]', + (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]', "wrong whitespace around ="), (r'raise Exception', "don't raise generic exceptions"), (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"), @@ -199,11 +204,11 @@ "always assign an opened file to a variable, and close it afterwards"), (r'(?i)descendent', "the proper spelling is descendAnt"), (r'\.debug\(\_', "don't mark debug messages for translation"), + (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"), + (r'^\s*except\s*:', "warning: naked except clause", r'#.*re-raises'), ], # warnings [ - (r'.{81}', "warning: line over 80 characters"), - (r'^\s*except:$', "warning: naked except clause"), (r'ui\.(status|progress|write|note|warn)\([\'\"]x', "warning: unwrapped ui message"), ] @@ -222,7 +227,7 @@ (r'^ ', "don't use spaces to indent"), (r'\S\t', "don't use tabs except for indent"), (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"), - (r'.{85}', "line too long"), + (r'.{81}', "line too long"), (r'(while|if|do|for)\(', "use space after while/if/do/for"), (r'return\(', "return is not a function"), (r' ;', "no space before ;"), @@ -352,7 +357,13 @@ prelines = None errors = [] - for p, msg in pats: + for pat in pats: + if len(pat) == 3: + p, msg, ignore = pat + else: + p, msg = pat + ignore = None + # fix-up regexes for multiline searches po = p # \s doesn't match \n @@ -383,6 +394,8 @@ print "Skipping %s for %s:%s (check-code -ignore)" % ( name, f, n) continue + elif ignore and re.search(ignore, l, re.MULTILINE): + continue bd = "" if blame: bd = 'working directory' diff -r 98823bd0d697 -r a06e2681dd17 contrib/debugcmdserver.py --- a/contrib/debugcmdserver.py Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/debugcmdserver.py Wed Jul 18 19:08:25 2012 -0500 @@ -24,7 +24,7 @@ def read(size): data = sys.stdin.read(size) if not data: - raise EOFError() + raise EOFError sys.stdout.write(data) sys.stdout.flush() return data diff -r 98823bd0d697 -r a06e2681dd17 contrib/hg-ssh --- a/contrib/hg-ssh Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/hg-ssh Wed Jul 18 19:08:25 2012 -0500 @@ -24,6 +24,9 @@ You can use pattern matching of your normal shell, e.g.: command="cd repos && hg-ssh user/thomas/* projects/{mercurial,foo}" + +You can also add a --read-only flag to allow read-only access to a key, e.g.: +command="hg-ssh --read-only repos/*" """ # enable importing on demand to reduce startup time @@ -33,25 +36,51 @@ 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 "%s": %s\n' % (orig_cmd, e)) - sys.exit(255) +def main(): + cwd = os.getcwd() + readonly = False + args = sys.argv[1:] + while len(args): + if args[0] == '--read-only': + readonly = True + args.pop(0) + else: + break + allowed_paths = [os.path.normpath(os.path.join(cwd, + os.path.expanduser(path))) + for path in args] + orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?') + try: + cmdargv = shlex.split(orig_cmd) + except ValueError, e: + sys.stderr.write('Illegal command "%s": %s\n' % (orig_cmd, e)) + sys.exit(255) -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'])) + 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: + cmd = ['-R', repo, 'serve', '--stdio'] + if readonly: + cmd += [ + '--config', + 'hooks.prechangegroup.hg-ssh=python:__main__.rejectpush', + '--config', + 'hooks.prepushkey.hg-ssh=python:__main__.rejectpush' + ] + dispatch.dispatch(dispatch.request(cmd)) + else: + sys.stderr.write('Illegal repository "%s"\n' % repo) + sys.exit(255) else: - sys.stderr.write('Illegal repository "%s"\n' % repo) + sys.stderr.write('Illegal command "%s"\n' % orig_cmd) sys.exit(255) -else: - sys.stderr.write('Illegal command "%s"\n' % orig_cmd) - sys.exit(255) +def rejectpush(ui, **kwargs): + ui.warn("Permission denied\n") + # mercurial hooks use unix process conventions for hook return values + # so a truthy return means failure + return True + +if __name__ == '__main__': + main() diff -r 98823bd0d697 -r a06e2681dd17 contrib/perf.py --- a/contrib/perf.py Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/perf.py Wed Jul 18 19:08:25 2012 -0500 @@ -33,20 +33,34 @@ try: m = scmutil.match(repo[None], pats, {}) timer(lambda: len(list(repo.dirstate.walk(m, [], True, False)))) - except: + except Exception: try: m = scmutil.match(repo[None], pats, {}) timer(lambda: len([b for a, b, c in repo.dirstate.statwalk([], m)])) - except: + except Exception: timer(lambda: len(list(cmdutil.walk(repo, pats, {})))) def perfstatus(ui, repo, *pats): #m = match.always(repo.root, repo.getcwd()) - #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False)))) + #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, + # False)))) timer(lambda: sum(map(len, repo.status()))) +def clearcaches(cl): + # behave somewhat consistently across internal API changes + if util.safehasattr(cl, 'clearcaches'): + cl.clearcaches() + elif util.safehasattr(cl, '_nodecache'): + from mercurial.node import nullid, nullrev + cl._nodecache = {nullid: nullrev} + cl._nodepos = None + def perfheads(ui, repo): - timer(lambda: len(repo.changelog.headrevs())) + cl = repo.changelog + def d(): + len(cl.headrevs()) + clearcaches(cl) + timer(d) def perftags(ui, repo): import mercurial.changelog, mercurial.manifest @@ -57,6 +71,13 @@ return len(repo.tags()) timer(t) +def perfancestors(ui, repo): + heads = repo.changelog.headrevs() + def d(): + for a in repo.changelog.ancestors(heads): + pass + timer(d) + def perfdirstate(ui, repo): "a" in repo.dirstate def d(): @@ -71,6 +92,14 @@ del repo.dirstate._dirs timer(d) +def perfdirstatewrite(ui, repo): + ds = repo.dirstate + "a" in ds + def d(): + ds._dirty = True + ds.write() + timer(d) + def perfmanifest(ui, repo): def d(): t = repo.manifest.tip() @@ -111,6 +140,10 @@ def perflookup(ui, repo, rev): timer(lambda: len(repo.lookup(rev))) +def perfrevrange(ui, repo, *specs): + revrange = scmutil.revrange + timer(lambda: len(revrange(repo, specs))) + def perfnodelookup(ui, repo, rev): import mercurial.revlog mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg @@ -125,20 +158,9 @@ mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg n = repo[rev].node() cl = mercurial.revlog.revlog(repo.sopener, "00changelog.i") - # behave somewhat consistently across internal API changes - if util.safehasattr(cl, 'clearcaches'): - clearcaches = cl.clearcaches - elif util.safehasattr(cl, '_nodecache'): - from mercurial.node import nullid, nullrev - def clearcaches(): - cl._nodecache = {nullid: nullrev} - cl._nodepos = None - else: - def clearcaches(): - pass def d(): cl.rev(n) - clearcaches() + clearcaches(cl) timer(d) def perflog(ui, repo, **opts): @@ -155,7 +177,7 @@ ui.popbuffer() def perfcca(ui, repo): - timer(lambda: scmutil.casecollisionauditor(ui, False, repo[None])) + timer(lambda: scmutil.casecollisionauditor(ui, False, repo.dirstate)) def perffncacheload(ui, repo): from mercurial import scmutil, store @@ -205,6 +227,7 @@ 'perffncacheload': (perffncacheload, []), 'perffncachewrite': (perffncachewrite, []), 'perflookup': (perflookup, []), + 'perfrevrange': (perfrevrange, []), 'perfnodelookup': (perfnodelookup, []), 'perfparents': (perfparents, []), 'perfstartup': (perfstartup, []), @@ -215,8 +238,10 @@ 'perfindex': (perfindex, []), 'perfheads': (perfheads, []), 'perftags': (perftags, []), + 'perfancestors': (perfancestors, []), 'perfdirstate': (perfdirstate, []), 'perfdirstatedirs': (perfdirstate, []), + 'perfdirstatewrite': (perfdirstatewrite, []), 'perflog': (perflog, [('', 'rename', False, 'ask log to follow renames')]), 'perftemplating': (perftemplating, []), diff -r 98823bd0d697 -r a06e2681dd17 contrib/setup3k.py --- a/contrib/setup3k.py Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/setup3k.py Wed Jul 18 19:08:25 2012 -0500 @@ -26,22 +26,22 @@ try: import hashlib sha = hashlib.sha1() -except: +except ImportError: try: import sha - except: + except ImportError: raise SystemExit( "Couldn't import standard hashlib (incomplete Python install).") try: import zlib -except: +except ImportError: raise SystemExit( "Couldn't import standard zlib (incomplete Python install).") try: import bz2 -except: +except ImportError: raise SystemExit( "Couldn't import standard bz2 (incomplete Python install).") @@ -84,7 +84,7 @@ os.dup2(devnull.fileno(), sys.stderr.fileno()) objects = cc.compile([fname], output_dir=tmpdir) cc.link_executable(objects, os.path.join(tmpdir, "a.out")) - except: + except Exception: return False return True finally: diff -r 98823bd0d697 -r a06e2681dd17 contrib/shrink-revlog.py --- a/contrib/shrink-revlog.py Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/shrink-revlog.py Wed Jul 18 19:08:25 2012 -0500 @@ -240,7 +240,7 @@ writerevs(ui, r1, r2, order, tr) report(ui, r1, r2) tr.close() - except: + except: # re-raises # Abort transaction first, so we truncate the files before # deleting them. tr.abort() diff -r 98823bd0d697 -r a06e2681dd17 contrib/zsh_completion --- a/contrib/zsh_completion Thu Jul 19 00:53:27 2012 +0200 +++ b/contrib/zsh_completion Wed Jul 18 19:08:25 2012 -0500 @@ -514,6 +514,7 @@ '(--logfile -l)'{-l+,--logfile}'[read commit message from ]:log file:_files -g \*.txt' \ '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \ '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ + '--amend[amend the parent of the working dir]' \ '*:file:_hg_files' } @@ -552,6 +553,20 @@ '*:revision:_hg_labels' } +_hg_cmd_graft() { + _arguments -s -w : $_hg_global_opts \ + '(--continue -c)'{-c,--continue}'[resume interrupted graft]' \ + '(--edit -e)'{-e,--edit}'[invoke editor on commit messages]' \ + '--log[append graft info to log message]' \ + '(--currentdate -D)'{-D,--currentdate}'[record the current date as commit date]' \ + '(--currentuser -U)'{-U,--currentuser}'[record the current user as committer]' \ + '(--date -d)'{-d,--date}'[record the specified date as commit date]' \ + '(--user -u)'{-u,--user}'[record the specified user as committer]' \ + '(--tool -t)'{-t,--tool}'[specify merge tool]' \ + '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]' \ + '*:revision:_hg_labels' +} + _hg_cmd_grep() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts \ '(--print0 -0)'{-0,--print0}'[end filenames with NUL]' \ @@ -590,6 +605,7 @@ '(--strip -p)'{-p+,--strip}'[directory strip option for patch (default: 1)]:count:' \ '(--message -m)'{-m+,--message}'[use as commit message]:text:' \ '(--force -f)'{-f,--force}'[skip check for outstanding uncommitted changes]' \ + '--bypass[apply patch without touching the working directory]' \ '*:patch:_files' } @@ -635,6 +651,7 @@ _hg_cmd_manifest() { _arguments -s -w : $_hg_global_opts \ + '--all[list files from all revisions]' \ ':revision:_hg_labels' } @@ -643,6 +660,7 @@ '(--force -f)'{-f,--force}'[force a merge with outstanding changes]' \ '(--rev -r 1)'{-r,--rev}'[revision to merge]:revision:_hg_mergerevs' \ '(--preview -P)'{-P,--preview}'[review revisions to merge (no merge is performed)]' \ + '(--tool -t)'{-t,--tool}'[specify merge tool]' \ ':revision:_hg_mergerevs' } @@ -667,6 +685,16 @@ ':path:_hg_paths' } +_hg_cmd_phase() { + _arguments -s -w : $_hg_global_opts \ + '(--public -p)'{-p,--public}'[set changeset phase to public]' \ + '(--draft -d)'{-d,--draft}'[set changeset phase to draft]' \ + '(--secret -s)'{-s,--secret}'[set changeset phase to secret]' \ + '(--force -f)'{-f,--force}'[allow to move boundary backward]' \ + '(--rev -r)'{-r+,--rev}'[target revision]:revision:_hg_labels' \ + ':revision:_hg_labels' +} + _hg_cmd_pull() { _arguments -s -w : $_hg_global_opts $_hg_remote_opts \ '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \ @@ -720,7 +748,7 @@ _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \ '(--all -a :)'{-a,--all}'[revert all changes when no arguments given]' \ '(--rev -r)'{-r+,--rev}'[revision to revert to]:revision:_hg_labels' \ - '--no-backup[do not save backup copies of files]' \ + '(--no-backup -C)'{-C,--no-backup}'[do not save backup copies of files]' \ '*:file:->diff_files' if [[ $state == 'diff_files' ]] @@ -949,6 +977,7 @@ '(--merge -m)'{-m+,--merge}'[merge from another queue]:' \ '(--name -n)'{-n+,--name}'[merge queue name]:' \ '(--force -f)'{-f,--force}'[apply if the patch has rejects]' \ + '(--exact -e)'{-e,--exact}'[apply the target patch to its recorded parent]' \ '--move[reorder patch series and apply only the patch]' \ ':patch:_hg_qunapplied' } @@ -1001,6 +1030,7 @@ _arguments -s -w : $_hg_global_opts $_hg_remote_opts \ '(--git -g)'{-g,--git}'[use git extended diff format]' \ '--plain[omit hg patch header]' \ + '--body[send patches as inline message text (default)]' \ '(--outgoing -o)'{-o,--outgoing}'[send changes not found in the target repository]' \ '(--bundle -b)'{-b,--bundle}'[send changes not in target as a binary bundle]' \ '--bundlename[name of the bundle attachment file (default: bundle)]:' \ @@ -1026,4 +1056,22 @@ ':revision:_hg_revrange' } +# Rebase +_hg_cmd_rebase() { + _arguments -s -w : $_hg_global_opts \ + '*'{-r,--rev}'[rebase these revisions]:revision:_hg_revrange' \ + '(--source -s)'{-s,--source}'[rebase from the specified changeset]:revision:_hg_labels' \ + '(--base -b)'{-b,--base}'[rebase from the base of the specified changeset]:revision:_hg_labels' \ + '(--dest -d)'{-d,--dest}'[rebase onto the specified changeset]' \ + '--collapse[collapse the rebased changeset]' \ + '(--message -m)'{-m+,--message}'[use as collapse commit message]:text:' \ + '(--edit -e)'{-e,--edit}'[invoke editor on commit messages]' \ + '(--logfile -l)'{-l+,--logfile}'[read collapse commit message from ]:log file:_files -g \*.txt' \ + '--keep[keep original changeset]' \ + '--keepbranches[keep original branch name]' \ + '(--tool -t)'{-t,--tool}'[specify merge tool]' \ + '(--continue -c)'{-c,--continue}'[continue an interrupted rebase]' \ + '(--abort -a)'{-a,--abort}'[abort an interrupted rebase]' \ +} + _hg "$@" diff -r 98823bd0d697 -r a06e2681dd17 doc/gendoc.py --- a/doc/gendoc.py Thu Jul 19 00:53:27 2012 +0200 +++ b/doc/gendoc.py Wed Jul 18 19:08:25 2012 -0500 @@ -102,9 +102,9 @@ ui.write("\n") section(ui, _("Extensions")) - ui.write(_("This section contains help for extensions that are distributed " - "together with Mercurial. Help for other extensions is available " - "in the help system.")) + ui.write(_("This section contains help for extensions that are " + "distributed together with Mercurial. Help for other " + "extensions is available in the help system.")) ui.write("\n\n" ".. contents::\n" " :class: htmlonly\n" diff -r 98823bd0d697 -r a06e2681dd17 doc/hgmanpage.py --- a/doc/hgmanpage.py Thu Jul 19 00:53:27 2012 +0200 +++ b/doc/hgmanpage.py Wed Jul 18 19:08:25 2012 -0500 @@ -582,7 +582,7 @@ self._docinfo[name], self.defs['indent'][1], self.defs['indent'][1])) - elif not name in skip: + elif name not in skip: if name in self._docinfo_names: label = self._docinfo_names[name] else: diff -r 98823bd0d697 -r a06e2681dd17 hgext/acl.py --- a/hgext/acl.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/acl.py Wed Jul 18 19:08:25 2012 -0500 @@ -46,6 +46,9 @@ - a comma-separated list containing users and groups, or - an asterisk, to match anyone; +You can add the "!" prefix to a user or group name to invert the sense +of the match. + Path-based Access Control ......................... @@ -146,12 +149,54 @@ .hgtags = release_engineer +Examples using the "!" prefix +............................. + +Suppose there's a branch that only a given user (or group) should be able to +push to, and you don't want to restrict access to any other branch that may +be created. + +The "!" prefix allows you to prevent anyone except a given user or group to +push changesets in a given branch or path. + +In the examples below, we will: +1) Deny access to branch "ring" to anyone but user "gollum" +2) Deny access to branch "lake" to anyone but members of the group "hobbit" +3) Deny access to a file to anyone but user "gollum" + +:: + + [acl.allow.branches] + # Empty + + [acl.deny.branches] + + # 1) only 'gollum' can commit to branch 'ring'; + # 'gollum' and anyone else can still commit to any other branch. + ring = !gollum + + # 2) only members of the group 'hobbit' can commit to branch 'lake'; + # 'hobbit' members and anyone else can still commit to any other branch. + lake = !@hobbit + + # You can also deny access based on file paths: + + [acl.allow] + # Empty + + [acl.deny] + # 3) only 'gollum' can change the file below; + # 'gollum' and anyone else can still change any other file. + /misty/mountains/cave/ring = !gollum + ''' from mercurial.i18n import _ from mercurial import util, match import getpass, urllib +testedwith = 'internal' + def _getusers(ui, group): # First, try to use group definition from section [acl.groups] @@ -172,7 +217,21 @@ return True for ug in usersorgroups.replace(',', ' ').split(): - if user == ug or ug.find('@') == 0 and user in _getusers(ui, ug[1:]): + + if ug.startswith('!'): + # Test for excluded user or group. Format: + # if ug is a user name: !username + # if ug is a group name: !@groupname + ug = ug[1:] + if not ug.startswith('@') and user != ug \ + or ug.startswith('@') and user not in _getusers(ui, ug[1:]): + return True + + # Test for user or group. Format: + # if ug is a user name: username + # if ug is a group name: @groupname + elif user == ug \ + or ug.startswith('@') and user in _getusers(ui, ug[1:]): return True return False @@ -188,15 +247,20 @@ ui.debug('acl: %s enabled, %d entries for user %s\n' % (key, len(pats), user)) + # Branch-based ACL if not repo: if pats: - return lambda b: '*' in pats or b in pats - return lambda b: False + # If there's an asterisk (meaning "any branch"), always return True; + # Otherwise, test if b is in pats + if '*' in pats: + return util.always + return lambda b: b in pats + return util.never + # Path-based ACL if pats: return match.match(repo.root, '', pats) - return match.exact(repo.root, '', []) - + return util.never def hook(ui, repo, hooktype, node=None, source=None, **kwargs): if hooktype not in ['pretxnchangegroup', 'pretxncommit']: diff -r 98823bd0d697 -r a06e2681dd17 hgext/bugzilla.py --- a/hgext/bugzilla.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/bugzilla.py Wed Jul 18 19:08:25 2012 -0500 @@ -282,6 +282,8 @@ from mercurial import cmdutil, mail, templater, util import re, time, urlparse, xmlrpclib +testedwith = 'internal' + class bzaccess(object): '''Base class for access to Bugzilla.''' @@ -416,7 +418,8 @@ for id in bugs.keys(): self.ui.status(_(' bug %s\n') % id) cmdfmt = self.ui.config('bugzilla', 'notify', self.default_notify) - bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla') + bzdir = self.ui.config('bugzilla', 'bzdir', + '/var/www/html/bugzilla') try: # Backwards-compatible with old notify string, which # took one string. This will throw with a new format @@ -468,8 +471,8 @@ userid = self.get_user_id(defaultuser) user = defaultuser except KeyError: - raise util.Abort(_('cannot find bugzilla user id for %s or %s') % - (user, defaultuser)) + raise util.Abort(_('cannot find bugzilla user id for %s or %s') + % (user, defaultuser)) return (user, userid) def updatebug(self, bugid, newstate, text, committer): @@ -910,4 +913,3 @@ bz.notify(bugs, util.email(ctx.user())) except Exception, e: raise util.Abort(_('Bugzilla error: %s') % e) - diff -r 98823bd0d697 -r a06e2681dd17 hgext/children.py --- a/hgext/children.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/children.py Wed Jul 18 19:08:25 2012 -0500 @@ -8,12 +8,17 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -'''command to display child changesets''' +'''command to display child changesets (DEPRECATED) + +This extension is deprecated. You should use :hg:`log -r +"children(REV)"` instead. +''' from mercurial import cmdutil from mercurial.commands import templateopts from mercurial.i18n import _ +testedwith = 'internal' def children(ui, repo, file_=None, **opts): """show the children of the given or working directory revision diff -r 98823bd0d697 -r a06e2681dd17 hgext/churn.py --- a/hgext/churn.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/churn.py Wed Jul 18 19:08:25 2012 -0500 @@ -13,6 +13,8 @@ import os import time, datetime +testedwith = 'internal' + def maketemplater(ui, repo, tmpl): tmpl = templater.parsestring(tmpl, quoted=False) try: @@ -67,7 +69,7 @@ else: parents = ctx.parents() if len(parents) > 1: - ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,)) + ui.note(_('revision %d is a merge, ignoring...\n') % (rev,)) return ctx1 = parents[0] diff -r 98823bd0d697 -r a06e2681dd17 hgext/color.py --- a/hgext/color.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/color.py Wed Jul 18 19:08:25 2012 -0500 @@ -105,6 +105,8 @@ from mercurial import commands, dispatch, extensions, ui as uimod, util from mercurial.i18n import _ +testedwith = 'internal' + # start and stop parameters for effects _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'bold': 1, diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/__init__.py --- a/hgext/convert/__init__.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/__init__.py Wed Jul 18 19:08:25 2012 -0500 @@ -13,6 +13,8 @@ from mercurial import commands, templatekw from mercurial.i18n import _ +testedwith = 'internal' + # Commands definition was moved elsewhere to ease demandload job. def convert(ui, src, dest=None, revmapfile=None, **opts): @@ -328,7 +330,8 @@ ('', 'root', '', _('specify cvsroot')), # Options specific to builtin cvsps ('', 'parents', '', _('show parent changesets')), - ('', 'ancestors', '', _('show current changeset in ancestor branches')), + ('', 'ancestors', '', + _('show current changeset in ancestor branches')), # Options that are ignored for compatibility with cvsps-2.1 ('A', 'cvs-direct', None, _('ignored for compatibility')), ], diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/bzr.py --- a/hgext/convert/bzr.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/bzr.py Wed Jul 18 19:08:25 2012 -0500 @@ -72,7 +72,7 @@ self.ui.warn(_('warning: lightweight checkouts may cause ' 'conversion failures, try with a regular ' 'branch instead.\n')) - except: + except Exception: self.ui.note(_('bzr source type could not be determined\n')) def before(self): diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/common.py --- a/hgext/convert/common.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/common.py Wed Jul 18 19:08:25 2012 -0500 @@ -76,7 +76,7 @@ def getheads(self): """Return a list of this repository's heads""" - raise NotImplementedError() + raise NotImplementedError def getfile(self, name, rev): """Return a pair (data, mode) where data is the file content @@ -84,7 +84,7 @@ identifier returned by a previous call to getchanges(). Raise IOError to indicate that name was deleted in rev. """ - raise NotImplementedError() + raise NotImplementedError def getchanges(self, version): """Returns a tuple of (files, copies). @@ -95,18 +95,18 @@ copies is a dictionary of dest: source """ - raise NotImplementedError() + raise NotImplementedError def getcommit(self, version): """Return the commit object for version""" - raise NotImplementedError() + raise NotImplementedError def gettags(self): """Return the tags as a dictionary of name: revision Tag names must be UTF-8 strings. """ - raise NotImplementedError() + raise NotImplementedError def recode(self, s, encoding=None): if not encoding: @@ -116,10 +116,10 @@ return s.encode("utf-8") try: return s.decode(encoding).encode("utf-8") - except: + except UnicodeError: try: return s.decode("latin-1").encode("utf-8") - except: + except UnicodeError: return s.decode(encoding, "replace").encode("utf-8") def getchangedfiles(self, rev, i): @@ -133,7 +133,7 @@ This function is only needed to support --filemap """ - raise NotImplementedError() + raise NotImplementedError def converted(self, rev, sinkrev): '''Notify the source that a revision has been converted.''' @@ -175,13 +175,13 @@ def getheads(self): """Return a list of this repository's heads""" - raise NotImplementedError() + raise NotImplementedError def revmapfile(self): """Path to a file that will contain lines source_rev_id sink_rev_id mapping equivalent revision identifiers for each system.""" - raise NotImplementedError() + raise NotImplementedError def authorfile(self): """Path to a file that will contain lines @@ -203,7 +203,7 @@ a particular revision (or even what that revision would be) before it receives the file data. """ - raise NotImplementedError() + raise NotImplementedError def puttags(self, tags): """Put tags into sink. @@ -212,7 +212,7 @@ Return a pair (tag_revision, tag_parent_revision), or (None, None) if nothing was changed. """ - raise NotImplementedError() + raise NotImplementedError def setbranch(self, branch, pbranches): """Set the current branch name. Called before the first putcommit @@ -247,7 +247,7 @@ def hascommit(self, rev): """Return True if the sink contains rev""" - raise NotImplementedError() + raise NotImplementedError class commandline(object): def __init__(self, ui, command): @@ -333,7 +333,7 @@ argmax = 4096 try: argmax = os.sysconf("SC_ARG_MAX") - except: + except (AttributeError, ValueError): pass # Windows shells impose their own limits on command line length, diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/convcmd.py --- a/hgext/convert/convcmd.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/convcmd.py Wed Jul 18 19:08:25 2012 -0500 @@ -190,7 +190,7 @@ children.setdefault(n, []) hasparent = False for p in parents[n]: - if not p in self.map: + if p not in self.map: visit.append(p) hasparent = True children.setdefault(p, []).append(n) @@ -280,7 +280,7 @@ def writeauthormap(self): authorfile = self.authorfile if authorfile: - self.ui.status(_('Writing author map file %s\n') % authorfile) + self.ui.status(_('writing author map file %s\n') % authorfile) ofile = open(authorfile, 'w+') for author in self.authors: ofile.write("%s=%s\n" % (author, self.authors[author])) @@ -297,7 +297,7 @@ try: srcauthor, dstauthor = line.split('=', 1) except ValueError: - msg = _('Ignoring bad line in author map file %s: %s\n') + msg = _('ignoring bad line in author map file %s: %s\n') self.ui.warn(msg % (authorfile, line.rstrip())) continue @@ -462,7 +462,7 @@ if not revmapfile: try: revmapfile = destc.revmapfile() - except: + except Exception: revmapfile = os.path.join(destc, "map") c = converter(ui, srcc, destc, revmapfile, opts) diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/cvs.py --- a/hgext/convert/cvs.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/cvs.py Wed Jul 18 19:08:25 2012 -0500 @@ -121,12 +121,13 @@ pf = open(cvspass) for line in pf.read().splitlines(): part1, part2 = line.split(' ', 1) + # /1 :pserver:user@example.com:2401/cvsroot/foo + # Ah 1: + # This could be expensive, avoid unnecessary calls. + if self._cachedcommit(p1).branch == branch: + hasbranchparent = True + mparents.append((p1, mp1, i, isancestor)) + knownparents.add(mp1) + # Discard parents ancestors of other parents if there is a + # non-ancestor one on the same branch than current revision. + if hasbranchparent: + mparents = [p for p in mparents if not p[3]] + wp = None + if mparents: + wp = max(p[2] for p in mparents) + mparents = [p[1] for p in mparents] + elif parents: wp = 0 self.origparents[rev] = parents @@ -319,7 +330,6 @@ if 'close' in self.commits[rev].extra: # A branch closing revision is only useful if one of its # parents belong to the branch being closed - branch = self.commits[rev].branch pbranches = [self._cachedcommit(p).branch for p in mparents] if branch in pbranches: closed = True @@ -347,13 +357,11 @@ # able to get the files later on in getfile, we hide the # original filename in the rev part of the return value. changes, copies = self.base.getchanges(rev) - newnames = {} files = {} for f, r in changes: newf = self.filemapper(f) if newf and (newf != f or newf not in files): files[newf] = (f, r) - newnames[f] = newf files = sorted(files.items()) ncopies = {} diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/git.py --- a/hgext/convert/git.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/git.py Wed Jul 18 19:08:25 2012 -0500 @@ -69,7 +69,7 @@ def catfile(self, rev, type): if rev == hex(nullid): - raise IOError() + raise IOError data, ret = self.gitread("git cat-file %s %s" % (type, rev)) if ret: raise util.Abort(_('cannot read %r object at %s') % (type, rev)) @@ -181,8 +181,8 @@ m, f = l[:-1].split("\t") changes.append(f) else: - fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --' - % (version, version, i + 1)) + fh = self.gitopen('git diff-tree --name-only --root -r %s ' + '"%s^%s" --' % (version, version, i + 1)) changes = [f.rstrip('\n') for f in fh] if fh.close(): raise util.Abort(_('cannot read changes in %s') % version) @@ -211,7 +211,7 @@ continue name = '%s%s' % (reftype, name[prefixlen:]) bookmarks[name] = rev - except: + except Exception: pass return bookmarks diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/hg.py --- a/hgext/convert/hg.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/hg.py Wed Jul 18 19:08:25 2012 -0500 @@ -95,7 +95,7 @@ self.after() try: self.repo = hg.repository(self.ui, branchpath) - except: + except Exception: self.repo = hg.repository(self.ui, branchpath, create=True) self.before() @@ -105,7 +105,7 @@ for b in pbranches: try: self.repo.lookup(b[0]) - except: + except Exception: missings.setdefault(b[1], []).append(b[0]) if missings: @@ -192,7 +192,7 @@ try: oldlines = sorted(parentctx['.hgtags'].data().splitlines(True)) - except: + except Exception: oldlines = [] newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags]) @@ -224,7 +224,7 @@ bookmarks.write(self.repo) def hascommit(self, rev): - if not rev in self.repo and self.clonebranches: + if rev not in self.repo and self.clonebranches: raise util.Abort(_('revision %s not found in destination ' 'repository (lookups with clonebranches=true ' 'are not implemented)') % rev) @@ -241,7 +241,7 @@ # try to provoke an exception if this isn't really a hg # repo, but some other bogus compatible-looking url if not self.repo.local(): - raise error.RepoError() + raise error.RepoError except error.RepoError: ui.traceback() raise NoRepo(_("%s is not a local Mercurial repository") % path) @@ -259,7 +259,7 @@ % startnode) startrev = self.repo.changelog.rev(startnode) children = {startnode: 1} - for rev in self.repo.changelog.descendants(startrev): + for rev in self.repo.changelog.descendants([startrev]): children[self.repo.changelog.node(rev)] = 1 self.keep = children.__contains__ else: @@ -294,7 +294,8 @@ if not parents: files = sorted(ctx.manifest()) # getcopies() is not needed for roots, but it is a simple way to - # detect missing revlogs and abort on errors or populate self.ignored + # detect missing revlogs and abort on errors or populate + # self.ignored self.getcopies(ctx, parents, files) return [(f, rev) for f in files if f not in self.ignored], {} if self._changescache and self._changescache[0] == rev: diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/monotone.py --- a/hgext/convert/monotone.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/monotone.py Wed Jul 18 19:08:25 2012 -0500 @@ -30,7 +30,7 @@ f = file(path, 'rb') header = f.read(16) f.close() - except: + except IOError: header = '' if header != 'SQLite format 3\x00': raise norepo @@ -283,11 +283,11 @@ def getfile(self, name, rev): if not self.mtnisfile(name, rev): - raise IOError() # file was deleted or renamed + raise IOError # file was deleted or renamed try: data = self.mtnrun("get_file_of", name, r=rev) - except: - raise IOError() # file was deleted or renamed + except Exception: + raise IOError # file was deleted or renamed self.mtnloadmanifest(rev) node, attr = self.files.get(name, (None, "")) return data, attr @@ -317,7 +317,7 @@ def getchangedfiles(self, rev, i): # This function is only needed to support --filemap # ... and we don't support that - raise NotImplementedError() + raise NotImplementedError def before(self): # Check if we have a new enough version to use automate stdio diff -r 98823bd0d697 -r a06e2681dd17 hgext/convert/subversion.py --- a/hgext/convert/subversion.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/convert/subversion.py Wed Jul 18 19:08:25 2012 -0500 @@ -85,8 +85,8 @@ self.copyfrom_rev = p.copyfrom_rev self.action = p.action -def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True, - strict_node_history=False): +def get_log_child(fp, url, paths, start, end, limit=0, + discover_changed_paths=True, strict_node_history=False): protocol = -1 def receiver(orig_paths, revnum, author, date, message, pool): if orig_paths is not None: @@ -120,6 +120,10 @@ """Fetch SVN log in a subprocess and channel them back to parent to avoid memory collection issues. """ + if svn is None: + raise util.Abort(_('debugsvnlog could not load Subversion python ' + 'bindings')) + util.setbinary(sys.stdin) util.setbinary(sys.stdout) args = decodeargs(sys.stdin.read()) @@ -139,7 +143,7 @@ ' hg executable is in PATH')) try: orig_paths, revnum, author, date, message = entry - except: + except (TypeError, ValueError): if entry is None: break raise util.Abort(_("log stream exception '%s'") % entry) @@ -176,7 +180,7 @@ 'know better.\n')) return True data = inst.fp.read() - except: + except Exception: # Could be urllib2.URLError if the URL is invalid or anything else. return False return '' in data @@ -189,6 +193,9 @@ try: proto, path = url.split('://', 1) if proto == 'file': + if (os.name == 'nt' and path[:1] == '/' and path[1:2].isalpha() + and path[2:6].lower() == '%3a/'): + path = path[:2] + ':/' + path[6:] path = urllib.url2pathname(path) except ValueError: proto = 'file' @@ -227,7 +234,7 @@ raise NoRepo(_("%s does not look like a Subversion repository") % url) if svn is None: - raise MissingTool(_('Could not load Subversion python bindings')) + raise MissingTool(_('could not load Subversion python bindings')) try: version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR @@ -276,7 +283,8 @@ except ValueError: raise util.Abort(_('svn: revision %s is not an integer') % rev) - self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/') + self.trunkname = self.ui.config('convert', 'svn.trunk', + 'trunk').strip('/') self.startrev = self.ui.config('convert', 'svn.startrev', default=0) try: self.startrev = int(self.startrev) @@ -862,13 +870,14 @@ pass except SubversionException, (inst, num): if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION: - raise util.Abort(_('svn: branch has no revision %s') % to_revnum) + raise util.Abort(_('svn: branch has no revision %s') + % to_revnum) raise def getfile(self, file, rev): # TODO: ra.get_file transmits the whole file instead of diffs. if file in self.removed: - raise IOError() + raise IOError mode = '' try: new_module, revnum = revsplit(rev)[1:] @@ -889,7 +898,7 @@ notfound = (svn.core.SVN_ERR_FS_NOT_FOUND, svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND) if e.apr_err in notfound: # File not found - raise IOError() + raise IOError raise if mode == 'l': link_prefix = "link " @@ -949,8 +958,8 @@ if not p.startswith('/'): p = self.module + '/' + p relpaths.append(p.strip('/')) - args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths, - strict_node_history] + args = [self.baseurl, relpaths, start, end, limit, + discover_changed_paths, strict_node_history] arg = encodeargs(args) hgexe = util.hgexecutable() cmd = '%s debugsvnlog' % util.shellquote(hgexe) @@ -1089,20 +1098,13 @@ self.wopener.write(filename, data) if self.is_exec: - was_exec = self.is_exec(self.wjoin(filename)) - else: - # On filesystems not supporting execute-bit, there is no way - # to know if it is set but asking subversion. Setting it - # systematically is just as expensive and much simpler. - was_exec = 'x' not in flags - - util.setflags(self.wjoin(filename), False, 'x' in flags) - if was_exec: - if 'x' not in flags: - self.delexec.append(filename) - else: - if 'x' in flags: - self.setexec.append(filename) + if self.is_exec(self.wjoin(filename)): + if 'x' not in flags: + self.delexec.append(filename) + else: + if 'x' in flags: + self.setexec.append(filename) + util.setflags(self.wjoin(filename), False, 'x' in flags) def _copyfile(self, source, dest): # SVN's copy command pukes if the destination file exists, but diff -r 98823bd0d697 -r a06e2681dd17 hgext/eol.py --- a/hgext/eol.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/eol.py Wed Jul 18 19:08:25 2012 -0500 @@ -94,6 +94,8 @@ from mercurial import util, config, extensions, match, error import re, os +testedwith = 'internal' + # Matches a lone LF, i.e., one that is not part of CRLF. singlelf = re.compile('(^|[^\r])\n') # Matches a single EOL which can either be a CRLF where repeated CR @@ -111,7 +113,8 @@ return s if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s): return s - if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n': + if (ui.configbool('eol', 'fix-trailing-newline', False) + and s and s[-1] != '\n'): s = s + '\n' return eolre.sub('\n', s) @@ -121,7 +124,8 @@ return s if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s): return s - if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n': + if (ui.configbool('eol', 'fix-trailing-newline', False) + and s and s[-1] != '\n'): s = s + '\n' return eolre.sub('\r\n', s) diff -r 98823bd0d697 -r a06e2681dd17 hgext/extdiff.py --- a/hgext/extdiff.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/extdiff.py Wed Jul 18 19:08:25 2012 -0500 @@ -66,6 +66,8 @@ from mercurial import scmutil, scmutil, util, commands, encoding import os, shlex, shutil, tempfile, re +testedwith = 'internal' + def snapshot(ui, repo, files, node, tmproot): '''snapshot files as of some revision if not using snapshot, -I/-X does not work and recursive diff @@ -88,7 +90,7 @@ ctx = repo[node] for fn in files: wfn = util.pconvert(fn) - if not wfn in ctx: + if wfn not in ctx: # File doesn't exist; could be a bogus modify continue ui.note(' %s\n' % wfn) diff -r 98823bd0d697 -r a06e2681dd17 hgext/fetch.py --- a/hgext/fetch.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/fetch.py Wed Jul 18 19:08:25 2012 -0500 @@ -5,13 +5,15 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -'''pull, update and merge in one command''' +'''pull, update and merge in one command (DEPRECATED)''' from mercurial.i18n import _ from mercurial.node import nullid, short from mercurial import commands, cmdutil, hg, util, error from mercurial.lock import release +testedwith = 'internal' + def fetch(ui, repo, source='default', **opts): '''pull changes from a remote repository, merge new changes if needed. @@ -38,7 +40,10 @@ parent, p2 = repo.dirstate.parents() branch = repo.dirstate.branch() - branchnode = repo.branchtags().get(branch) + try: + branchnode = repo.branchtip(branch) + except error.RepoLookupError: + branchnode = None if parent != branchnode: raise util.Abort(_('working dir not at branch tip ' '(use "hg update" to check out branch tip)')) @@ -70,7 +75,7 @@ try: revs = [other.lookup(rev) for rev in opts['rev']] except error.CapabilityError: - err = _("Other repository doesn't support revision lookup, " + err = _("other repository doesn't support revision lookup, " "so a rev cannot be specified.") raise util.Abort(err) diff -r 98823bd0d697 -r a06e2681dd17 hgext/gpg.py --- a/hgext/gpg.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/gpg.py Wed Jul 18 19:08:25 2012 -0500 @@ -12,6 +12,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' class gpg(object): def __init__(self, path, key=None): @@ -43,7 +44,7 @@ try: if f: os.unlink(f) - except: + except OSError: pass keys = [] key, fingerprint = None, None @@ -163,7 +164,7 @@ r = "%5d:%s" % (rev, hgnode.hex(repo.changelog.node(rev))) ui.write("%-30s %s\n" % (keystr(ui, k), r)) -@command("sigcheck", [], _('hg sigcheck REVISION')) +@command("sigcheck", [], _('hg sigcheck REV')) def check(ui, repo, rev): """verify all the signatures there may be for a particular revision""" mygpg = newgpg(ui) @@ -179,7 +180,7 @@ keys.extend(k) if not keys: - ui.write(_("No valid signature for %s\n") % hgnode.short(rev)) + ui.write(_("no valid signature for %s\n") % hgnode.short(rev)) return # print summary @@ -205,7 +206,7 @@ ('m', 'message', '', _('commit message'), _('TEXT')), ] + commands.commitopts2, - _('hg sign [OPTION]... [REVISION]...')) + _('hg sign [OPTION]... [REV]...')) def sign(ui, repo, *revs, **opts): """add a signature for the current or given revision @@ -236,7 +237,7 @@ for n in nodes: hexnode = hgnode.hex(n) - ui.write(_("Signing %d:%s\n") % (repo.changelog.rev(n), + ui.write(_("signing %d:%s\n") % (repo.changelog.rev(n), hgnode.short(n))) # build data data = node2txt(repo, n, sigver) @@ -286,4 +287,3 @@ return "%s\n" % hgnode.hex(node) else: raise util.Abort(_("unknown signature version")) - diff -r 98823bd0d697 -r a06e2681dd17 hgext/graphlog.py --- a/hgext/graphlog.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/graphlog.py Wed Jul 18 19:08:25 2012 -0500 @@ -12,441 +12,12 @@ revision graph is also shown. ''' -from mercurial.cmdutil import show_changeset from mercurial.i18n import _ -from mercurial.node import nullrev -from mercurial import cmdutil, commands, extensions, scmutil -from mercurial import hg, util, graphmod, templatekw, revset +from mercurial import cmdutil, commands cmdtable = {} command = cmdutil.command(cmdtable) - -ASCIIDATA = 'ASC' - -def asciiedges(type, char, lines, seen, rev, parents): - """adds edge info to changelog DAG walk suitable for ascii()""" - if rev not in seen: - seen.append(rev) - nodeidx = seen.index(rev) - - knownparents = [] - newparents = [] - for parent in parents: - if parent in seen: - knownparents.append(parent) - else: - newparents.append(parent) - - ncols = len(seen) - nextseen = seen[:] - nextseen[nodeidx:nodeidx + 1] = newparents - edges = [(nodeidx, nextseen.index(p)) for p in knownparents] - - while len(newparents) > 2: - # ascii() only knows how to add or remove a single column between two - # calls. Nodes with more than two parents break this constraint so we - # introduce intermediate expansion lines to grow the active node list - # slowly. - edges.append((nodeidx, nodeidx)) - edges.append((nodeidx, nodeidx + 1)) - nmorecols = 1 - yield (type, char, lines, (nodeidx, edges, ncols, nmorecols)) - char = '\\' - lines = [] - nodeidx += 1 - ncols += 1 - edges = [] - del newparents[0] - - if len(newparents) > 0: - edges.append((nodeidx, nodeidx)) - if len(newparents) > 1: - edges.append((nodeidx, nodeidx + 1)) - nmorecols = len(nextseen) - ncols - seen[:] = nextseen - yield (type, char, lines, (nodeidx, edges, ncols, nmorecols)) - -def fix_long_right_edges(edges): - for (i, (start, end)) in enumerate(edges): - if end > start: - edges[i] = (start, end + 1) - -def get_nodeline_edges_tail( - node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): - if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: - # Still going in the same non-vertical direction. - if n_columns_diff == -1: - start = max(node_index + 1, p_node_index) - tail = ["|", " "] * (start - node_index - 1) - tail.extend(["/", " "] * (n_columns - start)) - return tail - else: - return ["\\", " "] * (n_columns - node_index - 1) - else: - return ["|", " "] * (n_columns - node_index - 1) - -def draw_edges(edges, nodeline, interline): - for (start, end) in edges: - if start == end + 1: - interline[2 * end + 1] = "/" - elif start == end - 1: - interline[2 * start + 1] = "\\" - elif start == end: - interline[2 * start] = "|" - else: - if 2 * end >= len(nodeline): - continue - nodeline[2 * end] = "+" - if start > end: - (start, end) = (end, start) - for i in range(2 * start + 1, 2 * end): - if nodeline[i] != "+": - nodeline[i] = "-" - -def get_padding_line(ni, n_columns, edges): - line = [] - line.extend(["|", " "] * ni) - if (ni, ni - 1) in edges or (ni, ni) in edges: - # (ni, ni - 1) (ni, ni) - # | | | | | | | | - # +---o | | o---+ - # | | c | | c | | - # | |/ / | |/ / - # | | | | | | - c = "|" - else: - c = " " - line.extend([c, " "]) - line.extend(["|", " "] * (n_columns - ni - 1)) - return line - -def asciistate(): - """returns the initial value for the "state" argument to ascii()""" - return [0, 0] - -def ascii(ui, state, type, char, text, coldata): - """prints an ASCII graph of the DAG - - takes the following arguments (one call per node in the graph): - - - ui to write to - - Somewhere to keep the needed state in (init to asciistate()) - - Column of the current node in the set of ongoing edges. - - Type indicator of node data == ASCIIDATA. - - Payload: (char, lines): - - Character to use as node's symbol. - - List of lines to display as the node's text. - - Edges; a list of (col, next_col) indicating the edges between - the current node and its parents. - - Number of columns (ongoing edges) in the current revision. - - The difference between the number of columns (ongoing edges) - in the next revision and the number of columns (ongoing edges) - in the current revision. That is: -1 means one column removed; - 0 means no columns added or removed; 1 means one column added. - """ - - idx, edges, ncols, coldiff = coldata - assert -2 < coldiff < 2 - if coldiff == -1: - # Transform - # - # | | | | | | - # o | | into o---+ - # |X / |/ / - # | | | | - fix_long_right_edges(edges) - - # add_padding_line says whether to rewrite - # - # | | | | | | | | - # | o---+ into | o---+ - # | / / | | | # <--- padding line - # o | | | / / - # o | | - add_padding_line = (len(text) > 2 and coldiff == -1 and - [x for (x, y) in edges if x + 1 < y]) - - # fix_nodeline_tail says whether to rewrite - # - # | | o | | | | o | | - # | | |/ / | | |/ / - # | o | | into | o / / # <--- fixed nodeline tail - # | |/ / | |/ / - # o | | o | | - fix_nodeline_tail = len(text) <= 2 and not add_padding_line - - # nodeline is the line containing the node character (typically o) - nodeline = ["|", " "] * idx - nodeline.extend([char, " "]) - - nodeline.extend( - get_nodeline_edges_tail(idx, state[1], ncols, coldiff, - state[0], fix_nodeline_tail)) - - # shift_interline is the line containing the non-vertical - # edges between this entry and the next - shift_interline = ["|", " "] * idx - if coldiff == -1: - n_spaces = 1 - edge_ch = "/" - elif coldiff == 0: - n_spaces = 2 - edge_ch = "|" - else: - n_spaces = 3 - edge_ch = "\\" - shift_interline.extend(n_spaces * [" "]) - shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) - - # draw edges from the current node to its parents - draw_edges(edges, nodeline, shift_interline) - - # lines is the list of all graph lines to print - lines = [nodeline] - if add_padding_line: - lines.append(get_padding_line(idx, ncols, edges)) - lines.append(shift_interline) - - # make sure that there are as many graph lines as there are - # log strings - while len(text) < len(lines): - text.append("") - if len(lines) < len(text): - extra_interline = ["|", " "] * (ncols + coldiff) - while len(lines) < len(text): - lines.append(extra_interline) - - # print lines - indentation_level = max(ncols, ncols + coldiff) - for (line, logstr) in zip(lines, text): - ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) - ui.write(ln.rstrip() + '\n') - - # ... and start over - state[0] = coldiff - state[1] = idx - -def get_revs(repo, rev_opt): - if rev_opt: - revs = scmutil.revrange(repo, rev_opt) - if len(revs) == 0: - return (nullrev, nullrev) - return (max(revs), min(revs)) - else: - return (len(repo) - 1, 0) - -def check_unsupported_flags(pats, opts): - for op in ["newest_first"]: - if op in opts and opts[op]: - raise util.Abort(_("-G/--graph option is incompatible with --%s") - % op.replace("_", "-")) - -def _makefilematcher(repo, pats, followfirst): - # When displaying a revision with --patch --follow FILE, we have - # to know which file of the revision must be diffed. With - # --follow, we want the names of the ancestors of FILE in the - # revision, stored in "fcache". "fcache" is populated by - # reproducing the graph traversal already done by --follow revset - # and relating linkrevs to file names (which is not "correct" but - # good enough). - fcache = {} - fcacheready = [False] - pctx = repo['.'] - wctx = repo[None] - - def populate(): - for fn in pats: - for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)): - for c in i: - fcache.setdefault(c.linkrev(), set()).add(c.path()) - - def filematcher(rev): - if not fcacheready[0]: - # Lazy initialization - fcacheready[0] = True - populate() - return scmutil.match(wctx, fcache.get(rev, []), default='path') - - return filematcher - -def _makelogrevset(repo, pats, opts, revs): - """Return (expr, filematcher) where expr is a revset string built - from log options and file patterns or None. If --stat or --patch - are not passed filematcher is None. Otherwise it is a callable - taking a revision number and returning a match objects filtering - the files to be detailed when displaying the revision. - """ - opt2revset = { - 'no_merges': ('not merge()', None), - 'only_merges': ('merge()', None), - '_ancestors': ('ancestors(%(val)s)', None), - '_fancestors': ('_firstancestors(%(val)s)', None), - '_descendants': ('descendants(%(val)s)', None), - '_fdescendants': ('_firstdescendants(%(val)s)', None), - '_matchfiles': ('_matchfiles(%(val)s)', None), - 'date': ('date(%(val)r)', None), - 'branch': ('branch(%(val)r)', ' or '), - '_patslog': ('filelog(%(val)r)', ' or '), - '_patsfollow': ('follow(%(val)r)', ' or '), - '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '), - 'keyword': ('keyword(%(val)r)', ' or '), - 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '), - 'user': ('user(%(val)r)', ' or '), - } - - opts = dict(opts) - # follow or not follow? - follow = opts.get('follow') or opts.get('follow_first') - followfirst = opts.get('follow_first') and 1 or 0 - # --follow with FILE behaviour depends on revs... - startrev = revs[0] - followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0 - - # branch and only_branch are really aliases and must be handled at - # the same time - opts['branch'] = opts.get('branch', []) + opts.get('only_branch', []) - opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']] - # pats/include/exclude are passed to match.match() directly in - # _matchfile() revset but walkchangerevs() builds its matcher with - # scmutil.match(). The difference is input pats are globbed on - # platforms without shell expansion (windows). - pctx = repo[None] - match, pats = scmutil.matchandpats(pctx, pats, opts) - slowpath = match.anypats() or (match.files() and opts.get('removed')) - if not slowpath: - for f in match.files(): - if follow and f not in pctx: - raise util.Abort(_('cannot follow file not in parent ' - 'revision: "%s"') % f) - filelog = repo.file(f) - if not len(filelog): - # A zero count may be a directory or deleted file, so - # try to find matching entries on the slow path. - if follow: - raise util.Abort( - _('cannot follow nonexistent file: "%s"') % f) - slowpath = True - if slowpath: - # See cmdutil.walkchangerevs() slow path. - # - if follow: - raise util.Abort(_('can only follow copies/renames for explicit ' - 'filenames')) - # pats/include/exclude cannot be represented as separate - # revset expressions as their filtering logic applies at file - # level. For instance "-I a -X a" matches a revision touching - # "a" and "b" while "file(a) and not file(b)" does - # not. Besides, filesets are evaluated against the working - # directory. - matchargs = ['r:', 'd:relpath'] - for p in pats: - matchargs.append('p:' + p) - for p in opts.get('include', []): - matchargs.append('i:' + p) - for p in opts.get('exclude', []): - matchargs.append('x:' + p) - matchargs = ','.join(('%r' % p) for p in matchargs) - opts['_matchfiles'] = matchargs - else: - if follow: - fpats = ('_patsfollow', '_patsfollowfirst') - fnopats = (('_ancestors', '_fancestors'), - ('_descendants', '_fdescendants')) - if pats: - # follow() revset inteprets its file argument as a - # manifest entry, so use match.files(), not pats. - opts[fpats[followfirst]] = list(match.files()) - else: - opts[fnopats[followdescendants][followfirst]] = str(startrev) - else: - opts['_patslog'] = list(pats) - - filematcher = None - if opts.get('patch') or opts.get('stat'): - if follow: - filematcher = _makefilematcher(repo, pats, followfirst) - else: - filematcher = lambda rev: match - - expr = [] - for op, val in opts.iteritems(): - if not val: - continue - if op not in opt2revset: - continue - revop, andor = opt2revset[op] - if '%(val)' not in revop: - expr.append(revop) - else: - if not isinstance(val, list): - e = revop % {'val': val} - else: - e = '(' + andor.join((revop % {'val': v}) for v in val) + ')' - expr.append(e) - - if expr: - expr = '(' + ' and '.join(expr) + ')' - else: - expr = None - return expr, filematcher - -def getlogrevs(repo, pats, opts): - """Return (revs, expr, filematcher) where revs is a list of - revision numbers, expr is a revset string built from log options - and file patterns or None, and used to filter 'revs'. If --stat or - --patch are not passed filematcher is None. Otherwise it is a - callable taking a revision number and returning a match objects - filtering the files to be detailed when displaying the revision. - """ - if not len(repo): - return [], None, None - # Default --rev value depends on --follow but --follow behaviour - # depends on revisions resolved from --rev... - follow = opts.get('follow') or opts.get('follow_first') - if opts.get('rev'): - revs = scmutil.revrange(repo, opts['rev']) - else: - if follow and len(repo) > 0: - revs = scmutil.revrange(repo, ['.:0']) - else: - revs = range(len(repo) - 1, -1, -1) - if not revs: - return [], None, None - expr, filematcher = _makelogrevset(repo, pats, opts, revs) - if expr: - # Evaluate revisions in changelog order for performance - # reasons but preserve the original sequence order in the - # filtered result. - matched = set(revset.match(repo.ui, expr)(repo, sorted(revs))) - revs = [r for r in revs if r in matched] - if not opts.get('hidden'): - # --hidden is still experimental and not worth a dedicated revset - # yet. Fortunately, filtering revision number is fast. - revs = [r for r in revs if r not in repo.changelog.hiddenrevs] - return revs, expr, filematcher - -def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None, - filematcher=None): - seen, state = [], asciistate() - for rev, type, ctx, parents in dag: - char = ctx.node() in showparents and '@' or 'o' - copies = None - if getrenamed and ctx.rev(): - copies = [] - for fn in ctx.files(): - rename = getrenamed(fn, ctx.rev()) - if rename: - copies.append((fn, rename[0])) - revmatchfn = None - if filematcher is not None: - revmatchfn = filematcher(ctx.rev()) - displayer.show(ctx, copies=copies, matchfn=revmatchfn) - lines = displayer.hunk.pop(rev).split('\n')[:-1] - displayer.flush(rev) - edges = edgefn(type, char, lines, seen, rev, parents) - for type, char, lines, coldata in edges: - ascii(ui, state, type, char, lines, coldata) - displayer.close() +testedwith = 'internal' @command('glog', [('f', 'follow', None, @@ -480,83 +51,4 @@ Nodes printed as an @ character are parents of the working directory. """ - - revs, expr, filematcher = getlogrevs(repo, pats, opts) - revs = sorted(revs, reverse=1) - limit = cmdutil.loglimit(opts) - if limit is not None: - revs = revs[:limit] - revdag = graphmod.dagwalker(repo, revs) - - getrenamed = None - if opts.get('copies'): - endrev = None - if opts.get('rev'): - endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1 - getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) - displayer = show_changeset(ui, repo, opts, buffered=True) - showparents = [ctx.node() for ctx in repo[None].parents()] - generate(ui, revdag, displayer, showparents, asciiedges, getrenamed, - filematcher) - -def graphrevs(repo, nodes, opts): - limit = cmdutil.loglimit(opts) - nodes.reverse() - if limit is not None: - nodes = nodes[:limit] - return graphmod.nodes(repo, nodes) - -def goutgoing(ui, repo, dest=None, **opts): - """show the outgoing changesets alongside an ASCII revision graph - - Print the outgoing changesets alongside a revision graph drawn with - ASCII characters. - - Nodes printed as an @ character are parents of the working - directory. - """ - - check_unsupported_flags([], opts) - o = hg._outgoing(ui, repo, dest, opts) - if o is None: - return - - revdag = graphrevs(repo, o, opts) - displayer = show_changeset(ui, repo, opts, buffered=True) - showparents = [ctx.node() for ctx in repo[None].parents()] - generate(ui, revdag, displayer, showparents, asciiedges) - -def gincoming(ui, repo, source="default", **opts): - """show the incoming changesets alongside an ASCII revision graph - - Print the incoming changesets alongside a revision graph drawn with - ASCII characters. - - Nodes printed as an @ character are parents of the working - directory. - """ - def subreporecurse(): - return 1 - - check_unsupported_flags([], opts) - def display(other, chlist, displayer): - revdag = graphrevs(other, chlist, opts) - showparents = [ctx.node() for ctx in repo[None].parents()] - generate(ui, revdag, displayer, showparents, asciiedges) - - hg._incoming(display, subreporecurse, ui, repo, source, opts, buffered=True) - -def uisetup(ui): - '''Initialize the extension.''' - _wrapcmd('log', commands.table, graphlog) - _wrapcmd('incoming', commands.table, gincoming) - _wrapcmd('outgoing', commands.table, goutgoing) - -def _wrapcmd(cmd, table, wrapfn): - '''wrap the command''' - def graph(orig, *args, **kwargs): - if kwargs['graph']: - return wrapfn(*args, **kwargs) - return orig(*args, **kwargs) - entry = extensions.wrapcommand(table, cmd, graph) - entry[1].append(('G', 'graph', None, _("show the revision DAG"))) + return cmdutil.graphlog(ui, repo, *pats, **opts) diff -r 98823bd0d697 -r a06e2681dd17 hgext/hgcia.py --- a/hgext/hgcia.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/hgcia.py Wed Jul 18 19:08:25 2012 -0500 @@ -46,17 +46,15 @@ from mercurial import cmdutil, patch, templater, util, mail import email.Parser -import xmlrpclib +import socket, xmlrpclib from xml.sax import saxutils +testedwith = 'internal' socket_timeout = 30 # seconds -try: +if util.safehasattr(socket, 'setdefaulttimeout'): # set a timeout for the socket so you don't have to wait so looooong # when cia.vc is having problems. requires python >= 2.3: - import socket socket.setdefaulttimeout(socket_timeout) -except: - pass HGCIA_VERSION = '0.1' HGCIA_URL = 'http://hg.kublai.com/mercurial/hgcia' diff -r 98823bd0d697 -r a06e2681dd17 hgext/hgk.py --- a/hgext/hgk.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/hgk.py Wed Jul 18 19:08:25 2012 -0500 @@ -39,6 +39,8 @@ from mercurial.node import nullid, nullrev, short from mercurial.i18n import _ +testedwith = 'internal' + def difftree(ui, repo, node1=None, node2=None, *files, **opts): """diff trees from two commits""" def __difftree(repo, node1, node2, files=[]): @@ -95,7 +97,8 @@ nlprefix = '\n' + prefix if ctx is None: ctx = repo[n] - ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ?? + # use ctx.node() instead ?? + ui.write("tree %s\n" % short(ctx.changeset()[0])) for p in ctx.parents(): ui.write("parent %s\n" % p) @@ -113,7 +116,8 @@ ui.write("branch %s\n\n" % ctx.branch()) if prefix != "": - ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip())) + ui.write("%s%s\n" % (prefix, + description.replace('\n', nlprefix).strip())) else: ui.write(description + "\n") if prefix: diff -r 98823bd0d697 -r a06e2681dd17 hgext/highlight/__init__.py --- a/hgext/highlight/__init__.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/highlight/__init__.py Wed Jul 18 19:08:25 2012 -0500 @@ -24,6 +24,7 @@ import highlight from mercurial.hgweb import webcommands, webutil, common from mercurial import extensions, encoding +testedwith = 'internal' def filerevision_highlight(orig, web, tmpl, fctx): mt = ''.join(tmpl('mimetype', encoding=encoding.encoding)) @@ -51,11 +52,13 @@ pg_style = web.config('web', 'pygments_style', 'colorful') fmter = highlight.HtmlFormatter(style = pg_style) req.respond(common.HTTP_OK, 'text/css') - return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')] + return ['/* pygments_style = %s */\n\n' % pg_style, + fmter.get_style_defs('')] def extsetup(): # monkeypatch in the new version - extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight) + extensions.wrapfunction(webcommands, '_filerevision', + filerevision_highlight) extensions.wrapfunction(webcommands, 'annotate', annotate_highlight) webcommands.highlightcss = generate_css webcommands.__all__.append('highlightcss') diff -r 98823bd0d697 -r a06e2681dd17 hgext/histedit.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/histedit.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,695 @@ +# histedit.py - interactive history editing for mercurial +# +# Copyright 2009 Augie Fackler +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +"""interactive history editing + +With this extension installed, Mercurial gains one new command: histedit. Usage +is as follows, assuming the following history:: + + @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42 + | Add delta + | + o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42 + | Add gamma + | + o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42 + | Add beta + | + o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42 + Add alpha + +If you were to run ``hg histedit c561b4e977df``, you would see the following +file open in your editor:: + + pick c561b4e977df Add beta + pick 030b686bedc4 Add gamma + pick 7c2fd3b9020c Add delta + + # Edit history between 633536316234 and 7c2fd3b9020c + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +In this file, lines beginning with ``#`` are ignored. You must specify a rule +for each revision in your history. For example, if you had meant to add gamma +before beta, and then wanted to add delta in the same revision as beta, you +would reorganize the file to look like this:: + + pick 030b686bedc4 Add gamma + pick c561b4e977df Add beta + fold 7c2fd3b9020c Add delta + + # Edit history between 633536316234 and 7c2fd3b9020c + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +At which point you close the editor and ``histedit`` starts working. When you +specify a ``fold`` operation, ``histedit`` will open an editor when it folds +those revisions together, offering you a chance to clean up the commit message:: + + Add beta + *** + Add delta + +Edit the commit message to your liking, then close the editor. For +this example, let's assume that the commit message was changed to +``Add beta and delta.`` After histedit has run and had a chance to +remove any old or temporary revisions it needed, the history looks +like this:: + + @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42 + | Add beta and delta. + | + o 1 081603921c3f 2009-04-27 18:04 -0500 durin42 + | Add gamma + | + o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42 + Add alpha + +Note that ``histedit`` does *not* remove any revisions (even its own temporary +ones) until after it has completed all the editing operations, so it will +probably perform several strip operations when it's done. For the above example, +it had to run strip twice. Strip can be slow depending on a variety of factors, +so you might need to be a little patient. You can choose to keep the original +revisions by passing the ``--keep`` flag. + +The ``edit`` operation will drop you back to a command prompt, +allowing you to edit files freely, or even use ``hg record`` to commit +some changes as a separate commit. When you're done, any remaining +uncommitted changes will be committed as well. When done, run ``hg +histedit --continue`` to finish this step. You'll be prompted for a +new commit message, but the default commit message will be the +original message for the ``edit`` ed revision. + +The ``message`` operation will give you a chance to revise a commit +message without changing the contents. It's a shortcut for doing +``edit`` immediately followed by `hg histedit --continue``. + +If ``histedit`` encounters a conflict when moving a revision (while +handling ``pick`` or ``fold``), it'll stop in a similar manner to +``edit`` with the difference that it won't prompt you for a commit +message when done. If you decide at this point that you don't like how +much work it will be to rearrange history, or that you made a mistake, +you can use ``hg histedit --abort`` to abandon the new changes you +have made and return to the state before you attempted to edit your +history. + +If we clone the example repository above and add three more changes, such that +we have the following history:: + + @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan + | Add theta + | + o 5 140988835471 2009-04-27 18:04 -0500 stefan + | Add eta + | + o 4 122930637314 2009-04-27 18:04 -0500 stefan + | Add zeta + | + o 3 836302820282 2009-04-27 18:04 -0500 stefan + | Add epsilon + | + o 2 989b4d060121 2009-04-27 18:04 -0500 durin42 + | Add beta and delta. + | + o 1 081603921c3f 2009-04-27 18:04 -0500 durin42 + | Add gamma + | + o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42 + Add alpha + +If you run ``hg histedit --outgoing`` on the clone then it is the same +as running ``hg histedit 836302820282``. If you need plan to push to a +repository that Mercurial does not detect to be related to the source +repo, you can add a ``--force`` option. +""" + +try: + import cPickle as pickle +except ImportError: + import pickle +import tempfile +import os + +from mercurial import bookmarks +from mercurial import cmdutil +from mercurial import discovery +from mercurial import error +from mercurial import hg +from mercurial import node +from mercurial import patch +from mercurial import repair +from mercurial import scmutil +from mercurial import util +from mercurial.i18n import _ + +cmdtable = {} +command = cmdutil.command(cmdtable) + +testedwith = 'internal' + +editcomment = """ + +# Edit history between %s and %s +# +# Commands: +# p, pick = use commit +# e, edit = use commit, but stop for amending +# f, fold = use commit, but fold into previous commit (combines N and N-1) +# d, drop = remove commit from history +# m, mess = edit message without changing commit content +# +""" + +def between(repo, old, new, keep): + revs = [old] + current = old + while current != new: + ctx = repo[current] + if not keep and len(ctx.children()) > 1: + raise util.Abort(_('cannot edit history that would orphan nodes')) + if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid: + raise util.Abort(_("can't edit history with merges")) + if not ctx.children(): + current = new + else: + current = ctx.children()[0].node() + revs.append(current) + if len(repo[current].children()) and not keep: + raise util.Abort(_('cannot edit history that would orphan nodes')) + return revs + + +def pick(ui, repo, ctx, ha, opts): + oldctx = repo[ha] + if oldctx.parents()[0] == ctx: + ui.debug('node %s unchanged\n' % ha) + return oldctx, [], [], [] + hg.update(repo, ctx.node()) + fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') + fp = os.fdopen(fd, 'w') + diffopts = patch.diffopts(ui, opts) + diffopts.git = True + diffopts.ignorews = False + diffopts.ignorewsamount = False + diffopts.ignoreblanklines = False + gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) + for chunk in gen: + fp.write(chunk) + fp.close() + try: + files = set() + try: + patch.patch(ui, repo, patchfile, files=files, eolmode=None) + if not files: + ui.warn(_('%s: empty changeset') + % node.hex(ha)) + return ctx, [], [], [] + finally: + os.unlink(patchfile) + except Exception: + raise util.Abort(_('Fix up the change and run ' + 'hg histedit --continue')) + n = repo.commit(text=oldctx.description(), user=oldctx.user(), + date=oldctx.date(), extra=oldctx.extra()) + return repo[n], [n], [oldctx.node()], [] + + +def edit(ui, repo, ctx, ha, opts): + oldctx = repo[ha] + hg.update(repo, ctx.node()) + fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') + fp = os.fdopen(fd, 'w') + diffopts = patch.diffopts(ui, opts) + diffopts.git = True + diffopts.ignorews = False + diffopts.ignorewsamount = False + diffopts.ignoreblanklines = False + gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) + for chunk in gen: + fp.write(chunk) + fp.close() + try: + files = set() + try: + patch.patch(ui, repo, patchfile, files=files, eolmode=None) + finally: + os.unlink(patchfile) + except Exception: + pass + raise util.Abort(_('Make changes as needed, you may commit or record as ' + 'needed now.\nWhen you are finished, run hg' + ' histedit --continue to resume.')) + +def fold(ui, repo, ctx, ha, opts): + oldctx = repo[ha] + hg.update(repo, ctx.node()) + fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') + fp = os.fdopen(fd, 'w') + diffopts = patch.diffopts(ui, opts) + diffopts.git = True + diffopts.ignorews = False + diffopts.ignorewsamount = False + diffopts.ignoreblanklines = False + gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) + for chunk in gen: + fp.write(chunk) + fp.close() + try: + files = set() + try: + patch.patch(ui, repo, patchfile, files=files, eolmode=None) + if not files: + ui.warn(_('%s: empty changeset') + % node.hex(ha)) + return ctx, [], [], [] + finally: + os.unlink(patchfile) + except Exception: + raise util.Abort(_('Fix up the change and run ' + 'hg histedit --continue')) + n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(), + date=oldctx.date(), extra=oldctx.extra()) + return finishfold(ui, repo, ctx, oldctx, n, opts, []) + +def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): + parent = ctx.parents()[0].node() + hg.update(repo, parent) + fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') + fp = os.fdopen(fd, 'w') + diffopts = patch.diffopts(ui, opts) + diffopts.git = True + diffopts.ignorews = False + diffopts.ignorewsamount = False + diffopts.ignoreblanklines = False + gen = patch.diff(repo, parent, newnode, opts=diffopts) + for chunk in gen: + fp.write(chunk) + fp.close() + files = set() + try: + patch.patch(ui, repo, patchfile, files=files, eolmode=None) + finally: + os.unlink(patchfile) + newmessage = '\n***\n'.join( + [ctx.description()] + + [repo[r].description() for r in internalchanges] + + [oldctx.description()]) + # If the changesets are from the same author, keep it. + if ctx.user() == oldctx.user(): + username = ctx.user() + else: + username = ui.username() + newmessage = ui.edit(newmessage, username) + n = repo.commit(text=newmessage, user=username, + date=max(ctx.date(), oldctx.date()), extra=oldctx.extra()) + return repo[n], [n], [oldctx.node(), ctx.node()], [newnode] + +def drop(ui, repo, ctx, ha, opts): + return ctx, [], [repo[ha].node()], [] + + +def message(ui, repo, ctx, ha, opts): + oldctx = repo[ha] + hg.update(repo, ctx.node()) + fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') + fp = os.fdopen(fd, 'w') + diffopts = patch.diffopts(ui, opts) + diffopts.git = True + diffopts.ignorews = False + diffopts.ignorewsamount = False + diffopts.ignoreblanklines = False + gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) + for chunk in gen: + fp.write(chunk) + fp.close() + try: + files = set() + try: + patch.patch(ui, repo, patchfile, files=files, eolmode=None) + finally: + os.unlink(patchfile) + except Exception: + raise util.Abort(_('Fix up the change and run ' + 'hg histedit --continue')) + message = oldctx.description() + message = ui.edit(message, ui.username()) + new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(), + extra=oldctx.extra()) + newctx = repo[new] + if oldctx.node() != newctx.node(): + return newctx, [new], [oldctx.node()], [] + # We didn't make an edit, so just indicate no replaced nodes + return newctx, [new], [], [] + + +def makedesc(c): + summary = '' + if c.description(): + summary = c.description().splitlines()[0] + line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary) + return line[:80] # trim to 80 chars so it's not stupidly wide in my editor + +actiontable = {'p': pick, + 'pick': pick, + 'e': edit, + 'edit': edit, + 'f': fold, + 'fold': fold, + 'd': drop, + 'drop': drop, + 'm': message, + 'mess': message, + } + +@command('histedit', + [('', 'commands', '', + _('Read history edits from the specified file.')), + ('c', 'continue', False, _('continue an edit already in progress')), + ('k', 'keep', False, + _("don't strip old nodes after edit is complete")), + ('', 'abort', False, _('abort an edit in progress')), + ('o', 'outgoing', False, _('changesets not found in destination')), + ('f', 'force', False, + _('force outgoing even for unrelated repositories')), + ('r', 'rev', [], _('first revision to be edited'))], + _("[PARENT]")) +def histedit(ui, repo, *parent, **opts): + """interactively edit changeset history + """ + # TODO only abort if we try and histedit mq patches, not just + # blanket if mq patches are applied somewhere + mq = getattr(repo, 'mq', None) + if mq and mq.applied: + raise util.Abort(_('source has mq patches applied')) + + parent = list(parent) + opts.get('rev', []) + if opts.get('outgoing'): + if len(parent) > 1: + raise util.Abort( + _('only one repo argument allowed with --outgoing')) + elif parent: + parent = parent[0] + + dest = ui.expandpath(parent or 'default-push', parent or 'default') + dest, revs = hg.parseurl(dest, None)[:2] + ui.status(_('comparing with %s\n') % util.hidepassword(dest)) + + revs, checkout = hg.addbranchrevs(repo, repo, revs, None) + other = hg.peer(repo, opts, dest) + + if revs: + revs = [repo.lookup(rev) for rev in revs] + + parent = discovery.findcommonoutgoing( + repo, other, [], force=opts.get('force')).missing[0:1] + else: + if opts.get('force'): + raise util.Abort(_('--force only allowed with --outgoing')) + + if opts.get('continue', False): + if len(parent) != 0: + raise util.Abort(_('no arguments allowed with --continue')) + (parentctxnode, created, replaced, + tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo) + currentparent, wantnull = repo.dirstate.parents() + parentctx = repo[parentctxnode] + # discover any nodes the user has added in the interim + newchildren = [c for c in parentctx.children() + if c.node() not in existing] + action, currentnode = rules.pop(0) + while newchildren: + if action in ('f', 'fold'): + tmpnodes.extend([n.node() for n in newchildren]) + else: + created.extend([n.node() for n in newchildren]) + filtered = [] + for r in newchildren: + filtered += [c for c in r.children() if c.node not in existing] + newchildren = filtered + m, a, r, d = repo.status()[:4] + oldctx = repo[currentnode] + message = oldctx.description() + if action in ('e', 'edit', 'm', 'mess'): + message = ui.edit(message, ui.username()) + elif action in ('f', 'fold'): + message = 'fold-temp-revision %s' % currentnode + new = None + if m or a or r or d: + new = repo.commit(text=message, user=oldctx.user(), + date=oldctx.date(), extra=oldctx.extra()) + + # If we're resuming a fold and we have new changes, mark the + # replacements and finish the fold. If not, it's more like a + # drop of the changesets that disappeared, and we can skip + # this step. + if action in ('f', 'fold') and (new or newchildren): + if new: + tmpnodes.append(new) + else: + new = newchildren[-1] + (parentctx, created_, replaced_, tmpnodes_) = finishfold( + ui, repo, parentctx, oldctx, new, opts, newchildren) + replaced.extend(replaced_) + created.extend(created_) + tmpnodes.extend(tmpnodes_) + elif action not in ('d', 'drop'): + if new != oldctx.node(): + replaced.append(oldctx.node()) + if new: + if new != oldctx.node(): + created.append(new) + parentctx = repo[new] + + elif opts.get('abort', False): + if len(parent) != 0: + raise util.Abort(_('no arguments allowed with --abort')) + (parentctxnode, created, replaced, tmpnodes, + existing, rules, keep, tip, replacemap) = readstate(repo) + ui.debug('restore wc to old tip %s\n' % node.hex(tip)) + hg.clean(repo, tip) + ui.debug('should strip created nodes %s\n' % + ', '.join([node.hex(n)[:12] for n in created])) + ui.debug('should strip temp nodes %s\n' % + ', '.join([node.hex(n)[:12] for n in tmpnodes])) + for nodes in (created, tmpnodes): + for n in reversed(nodes): + try: + repair.strip(ui, repo, n) + except error.LookupError: + pass + os.unlink(os.path.join(repo.path, 'histedit-state')) + return + else: + cmdutil.bailifchanged(repo) + if os.path.exists(os.path.join(repo.path, 'histedit-state')): + raise util.Abort(_('history edit already in progress, try ' + '--continue or --abort')) + + tip, empty = repo.dirstate.parents() + + + if len(parent) != 1: + raise util.Abort(_('histedit requires exactly one parent revision')) + parent = scmutil.revsingle(repo, parent[0]).node() + + keep = opts.get('keep', False) + revs = between(repo, parent, tip, keep) + + ctxs = [repo[r] for r in revs] + existing = [r.node() for r in ctxs] + rules = opts.get('commands', '') + if not rules: + rules = '\n'.join([makedesc(c) for c in ctxs]) + rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12]) + rules = ui.edit(rules, ui.username()) + # Save edit rules in .hg/histedit-last-edit.txt in case + # the user needs to ask for help after something + # surprising happens. + f = open(repo.join('histedit-last-edit.txt'), 'w') + f.write(rules) + f.close() + else: + f = open(rules) + rules = f.read() + f.close() + rules = [l for l in (r.strip() for r in rules.splitlines()) + if l and not l[0] == '#'] + rules = verifyrules(rules, repo, ctxs) + + parentctx = repo[parent].parents()[0] + keep = opts.get('keep', False) + replaced = [] + replacemap = {} + tmpnodes = [] + created = [] + + + while rules: + writestate(repo, parentctx.node(), created, replaced, + tmpnodes, existing, rules, keep, tip, replacemap) + action, ha = rules.pop(0) + (parentctx, created_, replaced_, tmpnodes_) = actiontable[action]( + ui, repo, parentctx, ha, opts) + + if replaced_: + clen, rlen = len(created_), len(replaced_) + if clen == rlen == 1: + ui.debug('histedit: exact replacement of %s with %s\n' % ( + node.short(replaced_[0]), node.short(created_[0]))) + + replacemap[replaced_[0]] = created_[0] + elif clen > rlen: + assert rlen == 1, ('unexpected replacement of ' + '%d changes with %d changes' % (rlen, clen)) + # made more changesets than we're replacing + # TODO synthesize patch names for created patches + replacemap[replaced_[0]] = created_[-1] + ui.debug('histedit: created many, assuming %s replaced by %s' % + (node.short(replaced_[0]), node.short(created_[-1]))) + elif rlen > clen: + if not created_: + # This must be a drop. Try and put our metadata on + # the parent change. + assert rlen == 1 + r = replaced_[0] + ui.debug('histedit: %s seems replaced with nothing, ' + 'finding a parent\n' % (node.short(r))) + pctx = repo[r].parents()[0] + if pctx.node() in replacemap: + ui.debug('histedit: parent is already replaced\n') + replacemap[r] = replacemap[pctx.node()] + else: + replacemap[r] = pctx.node() + ui.debug('histedit: %s best replaced by %s\n' % ( + node.short(r), node.short(replacemap[r]))) + else: + assert len(created_) == 1 + for r in replaced_: + ui.debug('histedit: %s replaced by %s\n' % ( + node.short(r), node.short(created_[0]))) + replacemap[r] = created_[0] + else: + assert False, ( + 'Unhandled case in replacement mapping! ' + 'replacing %d changes with %d changes' % (rlen, clen)) + created.extend(created_) + replaced.extend(replaced_) + tmpnodes.extend(tmpnodes_) + + hg.update(repo, parentctx.node()) + + if not keep: + if replacemap: + ui.note(_('histedit: Should update metadata for the following ' + 'changes:\n')) + + def copybms(old, new): + if old in tmpnodes or old in created: + # can't have any metadata we'd want to update + return + while new in replacemap: + new = replacemap[new] + ui.note(_('histedit: %s to %s\n') % (node.short(old), + node.short(new))) + octx = repo[old] + marks = octx.bookmarks() + if marks: + ui.note(_('histedit: moving bookmarks %s\n') % + ', '.join(marks)) + for mark in marks: + repo._bookmarks[mark] = new + bookmarks.write(repo) + + # We assume that bookmarks on the tip should remain + # tipmost, but bookmarks on non-tip changesets should go + # to their most reasonable successor. As a result, find + # the old tip and new tip and copy those bookmarks first, + # then do the rest of the bookmark copies. + oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1] + newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1] + copybms(oldtip, newtip) + + for old, new in sorted(replacemap.iteritems()): + copybms(old, new) + # TODO update mq state + + ui.debug('should strip replaced nodes %s\n' % + ', '.join([node.hex(n)[:12] for n in replaced])) + for n in sorted(replaced, key=lambda x: repo[x].rev()): + try: + repair.strip(ui, repo, n) + except error.LookupError: + pass + + ui.debug('should strip temp nodes %s\n' % + ', '.join([node.hex(n)[:12] for n in tmpnodes])) + for n in reversed(tmpnodes): + try: + repair.strip(ui, repo, n) + except error.LookupError: + pass + os.unlink(os.path.join(repo.path, 'histedit-state')) + if os.path.exists(repo.sjoin('undo')): + os.unlink(repo.sjoin('undo')) + + +def writestate(repo, parentctxnode, created, replaced, + tmpnodes, existing, rules, keep, oldtip, replacemap): + fp = open(os.path.join(repo.path, 'histedit-state'), 'w') + pickle.dump((parentctxnode, created, replaced, + tmpnodes, existing, rules, keep, oldtip, replacemap), + fp) + fp.close() + +def readstate(repo): + """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules, + keep, oldtip, replacemap ). + """ + fp = open(os.path.join(repo.path, 'histedit-state')) + return pickle.load(fp) + + +def verifyrules(rules, repo, ctxs): + """Verify that there exists exactly one edit rule per given changeset. + + Will abort if there are to many or too few rules, a malformed rule, + or a rule on a changeset outside of the user-given range. + """ + parsed = [] + if len(rules) != len(ctxs): + raise util.Abort(_('must specify a rule for each changeset once')) + for r in rules: + if ' ' not in r: + raise util.Abort(_('malformed line "%s"') % r) + action, rest = r.split(' ', 1) + if ' ' in rest.strip(): + ha, rest = rest.split(' ', 1) + else: + ha = r.strip() + try: + if repo[ha] not in ctxs: + raise util.Abort( + _('may not use changesets other than the ones listed')) + except error.RepoError: + raise util.Abort(_('unknown changeset %s listed') % ha) + if action not in actiontable: + raise util.Abort(_('unknown action "%s"') % action) + parsed.append([action, ha]) + return parsed diff -r 98823bd0d697 -r a06e2681dd17 hgext/inotify/__init__.py --- a/hgext/inotify/__init__.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/inotify/__init__.py Wed Jul 18 19:08:25 2012 -0500 @@ -15,6 +15,8 @@ import server from client import client, QueryFailed +testedwith = 'internal' + def serve(ui, repo, **opts): '''start an inotify server for this repository''' server.start(ui, repo.dirstate, repo.root, opts) @@ -46,7 +48,8 @@ files = match.files() if '.' in files: files = [] - if self._inotifyon and not ignored and not subrepos and not self._dirty: + if (self._inotifyon and not ignored and not subrepos and + not self._dirty): cli = client(ui, repo) try: result = cli.statusquery(files, match, False, diff -r 98823bd0d697 -r a06e2681dd17 hgext/inotify/server.py --- a/hgext/inotify/server.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/inotify/server.py Wed Jul 18 19:08:25 2012 -0500 @@ -355,7 +355,7 @@ except (OSError, socket.error), inst: try: os.unlink(self.realsockpath) - except: + except OSError: pass os.rmdir(tempdir) if inst.errno == errno.EEXIST: @@ -416,7 +416,7 @@ # try to send back our version to the client # this way, the client too is informed of the mismatch sock.sendall(chr(common.version)) - except: + except socket.error: pass return diff -r 98823bd0d697 -r a06e2681dd17 hgext/interhg.py --- a/hgext/interhg.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/interhg.py Wed Jul 18 19:08:25 2012 -0500 @@ -28,6 +28,8 @@ from mercurial import templatefilters, extensions from mercurial.i18n import _ +testedwith = 'internal' + interhg_table = [] def uisetup(ui): diff -r 98823bd0d697 -r a06e2681dd17 hgext/keyword.py --- a/hgext/keyword.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/keyword.py Wed Jul 18 19:08:25 2012 -0500 @@ -1,6 +1,6 @@ # keyword.py - $Keyword$ expansion for Mercurial # -# Copyright 2007-2010 Christian Ebert +# Copyright 2007-2012 Christian Ebert # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. @@ -92,6 +92,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' # hg commands that do not act on keywords nokwcommands = ('add addremove annotate bundle export grep incoming init log' @@ -187,7 +188,7 @@ self.repo = repo self.match = match.match(repo.root, '', [], inc, exc) self.restrict = kwtools['hgcmd'] in restricted.split() - self.record = False + self.postcommit = False kwmaps = self.ui.configitems('keywordmaps') if kwmaps: # override default templates @@ -238,11 +239,11 @@ def iskwfile(self, cand, ctx): '''Returns subset of candidates which are configured for keyword expansion but are not symbolic links.''' - return [f for f in cand if self.match(f) and not 'l' in ctx.flags(f)] + return [f for f in cand if self.match(f) and 'l' not in ctx.flags(f)] def overwrite(self, ctx, candidates, lookup, expand, rekw=False): '''Overwrites selected files expanding/shrinking keywords.''' - if self.restrict or lookup or self.record: # exclude kw_copy + if self.restrict or lookup or self.postcommit: # exclude kw_copy candidates = self.iskwfile(candidates, ctx) if not candidates: return @@ -279,7 +280,7 @@ fp.close() if kwcmd: self.repo.dirstate.normal(f) - elif self.record: + elif self.postcommit: self.repo.dirstate.normallookup(f) def shrink(self, fname, text): @@ -441,7 +442,7 @@ if name.split('.', 1)[0].find('commit') > -1: repo.ui.setconfig('hooks', name, '') msg = _('hg keyword configuration and expansion example') - ui.note("hg ci -m '%s'\n" % msg) + ui.note("hg ci -m '%s'\n" % msg) # check-code-ignore repo.commit(text=msg) ui.status(_('\n\tkeywords expanded\n')) ui.write(repo.wread(fn)) @@ -504,11 +505,18 @@ showfiles += ([f for f in files if f not in kwfiles], [f for f in unknown if f not in kwunknown]) kwlabels = 'enabled deleted enabledunknown ignored ignoredunknown'.split() - kwstates = zip('K!kIi', showfiles, kwlabels) - for char, filenames, kwstate in kwstates: - fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n' + kwstates = zip(kwlabels, 'K!kIi', showfiles) + fm = ui.formatter('kwfiles', opts) + fmt = '%.0s%s\n' + if opts.get('all') or ui.verbose: + fmt = '%s %s\n' + for kwstate, char, filenames in kwstates: + label = 'kwfiles.' + kwstate for f in filenames: - ui.write(fmt % repo.pathto(f, cwd), label='kwfiles.' + kwstate) + fm.startitem() + fm.write('kwstatus path', fmt, char, + repo.pathto(f, cwd), label=label) + fm.end() @command('kwshrink', commands.walkopts, _('hg kwshrink [OPTION]... [FILE]...')) def shrink(ui, repo, *pats, **opts): @@ -582,7 +590,7 @@ def kwcommitctx(self, ctx, error=False): n = super(kwrepo, self).commitctx(ctx, error) # no lock needed, only called from repo.commit() which already locks - if not kwt.record: + if not kwt.postcommit: restrict = kwt.restrict kwt.restrict = True kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()), @@ -624,6 +632,21 @@ kwt.match = util.never return orig(web, req, tmpl) + def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts): + '''Wraps cmdutil.amend expanding keywords after amend.''' + wlock = repo.wlock() + try: + kwt.postcommit = True + newid = orig(ui, repo, commitfunc, old, extra, pats, opts) + if newid != old.node(): + ctx = repo[newid] + kwt.restrict = True + kwt.overwrite(ctx, ctx.files(), False, True) + kwt.restrict = False + return newid + finally: + wlock.release() + def kw_copy(orig, ui, repo, pats, opts, rename=False): '''Wraps cmdutil.copy so that copy/rename destinations do not contain expanded keywords. @@ -634,25 +657,29 @@ For the latter we have to follow the symlink to find out whether its target is configured for expansion and we therefore must unexpand the keywords in the destination.''' - orig(ui, repo, pats, opts, rename) - if opts.get('dry_run'): - return - wctx = repo[None] - cwd = repo.getcwd() + wlock = repo.wlock() + try: + orig(ui, repo, pats, opts, rename) + if opts.get('dry_run'): + return + wctx = repo[None] + cwd = repo.getcwd() - def haskwsource(dest): - '''Returns true if dest is a regular file and configured for - expansion or a symlink which points to a file configured for - expansion. ''' - source = repo.dirstate.copied(dest) - if 'l' in wctx.flags(source): - source = scmutil.canonpath(repo.root, cwd, - os.path.realpath(source)) - return kwt.match(source) + def haskwsource(dest): + '''Returns true if dest is a regular file and configured for + expansion or a symlink which points to a file configured for + expansion. ''' + source = repo.dirstate.copied(dest) + if 'l' in wctx.flags(source): + source = scmutil.canonpath(repo.root, cwd, + os.path.realpath(source)) + return kwt.match(source) - candidates = [f for f in repo.dirstate.copies() if - not 'l' in wctx.flags(f) and haskwsource(f)] - kwt.overwrite(wctx, candidates, False, False) + candidates = [f for f in repo.dirstate.copies() if + 'l' not in wctx.flags(f) and haskwsource(f)] + kwt.overwrite(wctx, candidates, False, False) + finally: + wlock.release() def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): '''Wraps record.dorecord expanding keywords after recording.''' @@ -660,7 +687,7 @@ try: # record returns 0 even when nothing has changed # therefore compare nodes before and after - kwt.record = True + kwt.postcommit = True ctx = repo['.'] wstatus = repo[None].status() ret = orig(ui, repo, commitfunc, *pats, **opts) @@ -680,7 +707,7 @@ # 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 'l' not in fctx.flags() or self.size() - 4 == fctx.size()) or self.size() == fctx.size()): return self._filelog.cmp(self._filenode, fctx.data()) @@ -689,6 +716,7 @@ extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp) extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) extensions.wrapfunction(patch, 'diff', kw_diff) + extensions.wrapfunction(cmdutil, 'amend', kw_amend) extensions.wrapfunction(cmdutil, 'copy', kw_copy) for c in 'annotate changeset rev filediff diff'.split(): extensions.wrapfunction(webcommands, c, kwweb_skip) diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/basestore.py --- a/hgext/largefiles/basestore.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/basestore.py Wed Jul 18 19:08:25 2012 -0500 @@ -48,8 +48,8 @@ '''Put source file into the store under /.''' raise NotImplementedError('abstract method') - def exists(self, hash): - '''Check to see if the store contains the given hash.''' + def exists(self, hashes): + '''Check to see if the store contains the given hashes.''' raise NotImplementedError('abstract method') def get(self, files): diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/lfcommands.py --- a/hgext/largefiles/lfcommands.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/lfcommands.py Wed Jul 18 19:08:25 2012 -0500 @@ -11,7 +11,8 @@ import os import shutil -from mercurial import util, match as match_, hg, node, context, error, cmdutil +from mercurial import util, match as match_, hg, node, context, error, \ + cmdutil, scmutil from mercurial.i18n import _ from mercurial.lock import release @@ -133,7 +134,7 @@ try: fctx = ctx.filectx(lfutil.standin(f)) except error.LookupError: - raise IOError() + raise IOError renamed = fctx.renamed() if renamed: renamed = lfutil.splitstandin(renamed[0]) @@ -233,7 +234,7 @@ try: fctx = ctx.filectx(srcfname) except error.LookupError: - raise IOError() + raise IOError renamed = fctx.renamed() if renamed: # standin is always a largefile because largefile-ness @@ -282,7 +283,7 @@ try: fctx = ctx.filectx(f) except error.LookupError: - raise IOError() + raise IOError renamed = fctx.renamed() if renamed: renamed = renamed[0] @@ -339,7 +340,11 @@ store = basestore._openstore(rsrc, rdst, put=True) at = 0 - files = filter(lambda h: not store.exists(h), files) + ui.debug("sending statlfile command for %d largefiles\n" % len(files)) + retval = store.exists(files) + files = filter(lambda h: not retval[h], files) + ui.debug("%d largefiles need to be uploaded\n" % len(files)) + for hash in files: ui.progress(_('uploading largefiles'), at, unit='largefile', total=len(files)) @@ -368,7 +373,7 @@ store = basestore._openstore(repo) return store.verify(revs, contents=contents) -def cachelfiles(ui, repo, node): +def cachelfiles(ui, repo, node, filelist=None): '''cachelfiles ensures that all largefiles needed by the specified revision are present in the repository's largefile cache. @@ -376,6 +381,8 @@ by this operation; missing is the list of files that were needed but could not be found.''' lfiles = lfutil.listlfiles(repo, node) + if filelist: + lfiles = set(lfiles) & set(filelist) toget = [] for lfile in lfiles: @@ -404,6 +411,23 @@ return ([], []) +def downloadlfiles(ui, repo, rev=None): + matchfn = scmutil.match(repo[None], + [repo.wjoin(lfutil.shortname)], {}) + def prepare(ctx, fns): + pass + totalsuccess = 0 + totalmissing = 0 + for ctx in cmdutil.walkchangerevs(repo, matchfn, {'rev' : rev}, + prepare): + success, missing = cachelfiles(ui, repo, ctx.node()) + totalsuccess += len(success) + totalmissing += len(missing) + ui.status(_("%d additional largefiles cached\n") % totalsuccess) + if totalmissing > 0: + ui.status(_("%d largefiles failed to download\n") % totalmissing) + return totalsuccess, totalmissing + def updatelfiles(ui, repo, filelist=None, printmessage=True): wlock = repo.wlock() try: @@ -417,7 +441,7 @@ if printmessage and lfiles: ui.status(_('getting changed largefiles\n')) printed = True - cachelfiles(ui, repo, '.') + cachelfiles(ui, repo, '.', lfiles) updated, removed = 0, 0 for i in map(lambda f: _updatelfile(repo, lfdirstate, f), lfiles): @@ -459,6 +483,10 @@ # recognition that such cache missing files are REMOVED. lfdirstate.normallookup(lfile) return None # don't try to set the mode + else: + # Synchronize largefile dirstate to the last modified time of + # the file + lfdirstate.normal(lfile) ret = 1 mode = os.stat(absstandin).st_mode if mode != os.stat(abslfile).st_mode: diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/lfutil.py --- a/hgext/largefiles/lfutil.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/lfutil.py Wed Jul 18 19:08:25 2012 -0500 @@ -115,10 +115,10 @@ def findfile(repo, hash): if instore(repo, hash): - repo.ui.note(_('Found %s in store\n') % hash) + repo.ui.note(_('found %s in store\n') % hash) return storepath(repo, hash) elif inusercache(repo.ui, hash): - repo.ui.note(_('Found %s in system cache\n') % hash) + repo.ui.note(_('found %s in system cache\n') % hash) path = storepath(repo, hash) util.makedirs(os.path.dirname(path)) link(usercachepath(repo.ui, hash), path) diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/localstore.py --- a/hgext/largefiles/localstore.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/localstore.py Wed Jul 18 19:08:25 2012 -0500 @@ -22,9 +22,9 @@ the user cache.''' def __init__(self, ui, repo, remote): - url = os.path.join(remote.path, '.hg', lfutil.longname) + url = os.path.join(remote.local().path, '.hg', lfutil.longname) super(localstore, self).__init__(ui, repo, util.expandpath(url)) - self.remote = remote + self.remote = remote.local() def put(self, source, hash): util.makedirs(os.path.dirname(lfutil.storepath(self.remote, hash))) @@ -43,7 +43,7 @@ path = lfutil.usercachepath(self.ui, hash) else: raise basestore.StoreError(filename, hash, '', - _("Can't get file locally")) + _("can't get file locally")) fd = open(path, 'rb') try: return lfutil.copyandhash(fd, tmpfile) diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/overrides.py --- a/hgext/largefiles/overrides.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/overrides.py Wed Jul 18 19:08:25 2012 -0500 @@ -169,6 +169,8 @@ # function handle this. if not getattr(repo, "_isaddremove", False): lfutil.reporemove(repo, remove, unlink=True) + else: + lfutil.reporemove(repo, remove, unlink=False) finally: wlock.release() @@ -651,6 +653,7 @@ # take some extra care so that the largefiles are correctly updated in the # working copy def overridepull(orig, ui, repo, source=None, **opts): + revsprepull = len(repo) if opts.get('rebase', False): repo._isrebasing = True try: @@ -660,7 +663,6 @@ 'the update flag\n') del opts['rebase'] cmdutil.bailifchanged(repo) - revsprepull = len(repo) origpostincoming = commands.postincoming def _dummy(*args, **kwargs): pass @@ -695,8 +697,35 @@ (cached, missing) = lfcommands.cachelfiles(ui, repo, head) numcached += len(cached) ui.status(_("%d largefiles cached\n") % numcached) + if opts.get('all_largefiles'): + revspostpull = len(repo) + revs = [] + for rev in xrange(revsprepull + 1, revspostpull): + revs.append(repo[rev].rev()) + lfcommands.downloadlfiles(ui, repo, revs) return result +def overrideclone(orig, ui, source, dest=None, **opts): + if dest is None: + dest = hg.defaultdest(source) + if opts.get('all_largefiles') and not hg.islocal(dest): + raise util.Abort(_( + '--all-largefiles is incompatible with non-local destination %s' % + dest)) + result = hg.clone(ui, opts, source, dest, + pull=opts.get('pull'), + stream=opts.get('uncompressed'), + rev=opts.get('rev'), + update=True, # required for successful walkchangerevs + branch=opts.get('branch')) + if result is None: + return True + if opts.get('all_largefiles'): + sourcerepo, destrepo = result + success, missing = lfcommands.downloadlfiles(ui, destrepo.local(), None) + return missing != 0 + return result is None + def overriderebase(orig, ui, repo, **opts): repo._isrebasing = True try: @@ -778,10 +807,56 @@ if subrepos: for subpath in ctx.substate: sub = ctx.sub(subpath) - sub.archive(repo.ui, archiver, prefix) + submatch = match_.narrowmatcher(subpath, matchfn) + sub.archive(repo.ui, archiver, prefix, submatch) archiver.done() +def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None): + rev = repo._state[1] + ctx = repo._repo[rev] + + lfcommands.cachelfiles(ui, repo._repo, ctx.node()) + + def write(name, mode, islink, getdata): + # At this point, the standin has been replaced with the largefile name, + # so the normal matcher works here without the lfutil variants. + if match and not match(f): + return + data = getdata() + + archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data) + + for f in ctx: + ff = ctx.flags(f) + getdata = ctx[f].data + if lfutil.isstandin(f): + path = lfutil.findfile(repo._repo, getdata().strip()) + if path is None: + raise util.Abort( + _('largefile %s not found in repo store or system cache') + % lfutil.splitstandin(f)) + f = lfutil.splitstandin(f) + + def getdatafn(): + fd = None + try: + fd = open(os.path.join(prefix, path), 'rb') + return fd.read() + finally: + if fd: + fd.close() + + getdata = getdatafn + + write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata) + + for subpath in ctx.substate: + sub = ctx.sub(subpath) + submatch = match_.narrowmatcher(subpath, match) + sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/', + submatch) + # If a largefile is modified, the change is not reflected in its # standin until a commit. cmdutil.bailifchanged() raises an exception # if the repo has uncommitted changes. Wrap it to also check if diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/proto.py --- a/hgext/largefiles/proto.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/proto.py Wed Jul 18 19:08:25 2012 -0500 @@ -6,7 +6,8 @@ import os import urllib2 -from mercurial import error, httprepo, util, wireproto +from mercurial import error, httppeer, util, wireproto +from mercurial.wireproto import batchable, future from mercurial.i18n import _ import lfutil @@ -81,7 +82,7 @@ # unfortunately, httprepository._callpush tries to convert its # input file-like into a bundle before sending it, so we can't use # it ... - if issubclass(self.__class__, httprepo.httprepository): + if issubclass(self.__class__, httppeer.httppeer): res = None try: res = self._call('putlfile', data=fd, sha=sha, @@ -119,15 +120,19 @@ length)) return (length, stream) + @batchable def statlfile(self, sha): + f = future() + result = {'sha': sha} + yield result, f try: - return int(self._call("statlfile", sha=sha)) + yield int(f.value) except (ValueError, urllib2.HTTPError): # If the server returns anything but an integer followed by a # newline, newline, it's not speaking our language; if we get # an HTTP error, we can't be sure the largefile is present; # either way, consider it missing. - return 2 + yield 2 repo.__class__ = lfileswirerepository diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/remotestore.py --- a/hgext/largefiles/remotestore.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/remotestore.py Wed Jul 18 19:08:25 2012 -0500 @@ -10,6 +10,7 @@ from mercurial import util from mercurial.i18n import _ +from mercurial.wireproto import remotebatch import lfutil import basestore @@ -20,8 +21,6 @@ super(remotestore, self).__init__(ui, repo, url) def put(self, source, hash): - if self._verify(hash): - return if self.sendfile(source, hash): raise util.Abort( _('remotestore: could not put %s to remote store %s') @@ -29,8 +28,8 @@ self.ui.debug( _('remotestore: put %s to remote store %s') % (source, self.url)) - def exists(self, hash): - return self._verify(hash) + def exists(self, hashes): + return self._verify(hashes) def sendfile(self, filename, hash): self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash)) @@ -74,8 +73,8 @@ infile = lfutil.limitreader(infile, length) return lfutil.copyandhash(lfutil.blockstream(infile), tmpfile) - def _verify(self, hash): - return not self._stat(hash) + def _verify(self, hashes): + return self._stat(hashes) def _verifyfile(self, cctx, cset, contents, standin, verified): filename = lfutil.splitstandin(standin) @@ -104,3 +103,8 @@ else: raise RuntimeError('verify failed: unexpected response from ' 'statlfile (%r)' % stat) + + def batch(self): + '''Support for remote batching.''' + return remotebatch(self) + diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/reposetup.py --- a/hgext/largefiles/reposetup.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/reposetup.py Wed Jul 18 19:08:25 2012 -0500 @@ -340,8 +340,9 @@ lfdirstate.normal(lfile) for lfile in lfdirstate: if lfile in modifiedfiles: - if not os.path.exists( - repo.wjoin(lfutil.standin(lfile))): + if (not os.path.exists(repo.wjoin( + lfutil.standin(lfile)))) or \ + (not os.path.exists(repo.wjoin(lfile))): lfdirstate.drop(lfile) result = orig(text=text, user=user, date=date, match=match, diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/uisetup.py --- a/hgext/largefiles/uisetup.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/uisetup.py Wed Jul 18 19:08:25 2012 -0500 @@ -9,7 +9,7 @@ '''setup for largefiles extension: uisetup''' from mercurial import archival, cmdutil, commands, extensions, filemerge, hg, \ - httprepo, localrepo, merge, sshrepo, sshserver, wireproto + httppeer, localrepo, merge, sshpeer, sshserver, wireproto from mercurial.i18n import _ from mercurial.hgweb import hgweb_mod, protocol, webcommands from mercurial.subrepo import hgsubrepo @@ -70,6 +70,15 @@ overrides.overrideupdate) entry = extensions.wrapcommand(commands.table, 'pull', overrides.overridepull) + pullopt = [('', 'all-largefiles', None, + _('download all pulled versions of largefiles'))] + entry[1].extend(pullopt) + entry = extensions.wrapcommand(commands.table, 'clone', + overrides.overrideclone) + cloneopt = [('', 'all-largefiles', None, + _('download all versions of all largefiles'))] + + entry[1].extend(cloneopt) entry = extensions.wrapcommand(commands.table, 'cat', overrides.overridecat) entry = extensions.wrapfunction(merge, '_checkunknownfile', @@ -100,6 +109,7 @@ extensions.wrapfunction(hg, 'merge', overrides.hgmerge) extensions.wrapfunction(archival, 'archive', overrides.overridearchive) + extensions.wrapfunction(hgsubrepo, 'archive', overrides.hgsubrepoarchive) extensions.wrapfunction(cmdutil, 'bailifchanged', overrides.overridebailifchanged) @@ -133,10 +143,10 @@ # can't do this in reposetup because it needs to have happened before # wirerepo.__init__ is called - proto.ssholdcallstream = sshrepo.sshrepository._callstream - proto.httpoldcallstream = httprepo.httprepository._callstream - sshrepo.sshrepository._callstream = proto.sshrepocallstream - httprepo.httprepository._callstream = proto.httprepocallstream + proto.ssholdcallstream = sshpeer.sshpeer._callstream + proto.httpoldcallstream = httppeer.httppeer._callstream + sshpeer.sshpeer._callstream = proto.sshrepocallstream + httppeer.httppeer._callstream = proto.httprepocallstream # don't die on seeing a repo with the largefiles requirement localrepo.localrepository.supported |= set(['largefiles']) diff -r 98823bd0d697 -r a06e2681dd17 hgext/largefiles/wirestore.py --- a/hgext/largefiles/wirestore.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/largefiles/wirestore.py Wed Jul 18 19:08:25 2012 -0500 @@ -14,7 +14,7 @@ if not cap: raise lfutil.storeprotonotcapable([]) storetypes = cap.split(',') - if not 'serve' in storetypes: + if 'serve' not in storetypes: raise lfutil.storeprotonotcapable(storetypes) self.remote = remote super(wirestore, self).__init__(ui, repo, remote.url()) @@ -25,5 +25,13 @@ def _get(self, hash): return self.remote.getlfile(hash) - def _stat(self, hash): - return self.remote.statlfile(hash) + def _stat(self, hashes): + batch = self.remote.batch() + futures = {} + for hash in hashes: + futures[hash] = batch.statlfile(hash) + batch.submit() + retval = {} + for hash in hashes: + retval[hash] = not futures[hash].value + return retval diff -r 98823bd0d697 -r a06e2681dd17 hgext/mq.py --- a/hgext/mq.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/mq.py Wed Jul 18 19:08:25 2012 -0500 @@ -46,6 +46,17 @@ You will by default be managing a patch queue named "patches". You can create other, independent patch queues with the :hg:`qqueue` command. + +If the working directory contains uncommitted files, qpush, qpop and +qgoto abort immediately. If -f/--force is used, the changes are +discarded. Setting:: + + [mq] + keepchanges = True + +make them behave as if --keep-changes were passed, and non-conflicting +local changes will be tolerated and preserved. If incompatible options +such as -f/--force or --exact are passed, this setting is ignored. ''' from mercurial.i18n import _ @@ -62,6 +73,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' # Patch names looks like unix-file names. # They must be joinable with queue directory and result in the patch path. @@ -280,6 +292,9 @@ if phase is not None: repo.ui.restoreconfig(backup) +class AbortNoCleanup(error.Abort): + pass + class queue(object): def __init__(self, ui, path, patchdir=None): self.basepath = path @@ -308,7 +323,7 @@ try: gitmode = ui.configbool('mq', 'git', None) if gitmode is None: - raise error.ConfigError() + raise error.ConfigError self.gitmode = gitmode and 'yes' or 'no' except error.ConfigError: self.gitmode = ui.config('mq', 'git', 'auto').lower() @@ -599,7 +614,7 @@ raise util.Abort(_("repo commit failed")) try: ph = patchheader(mergeq.join(patch), self.plainmode) - except: + except Exception: raise util.Abort(_("unable to read %s") % patch) diffopts = self.patchopts(diffopts, patch) @@ -681,7 +696,7 @@ def apply(self, repo, series, list=False, update_status=True, strict=False, patchdir=None, merge=None, all_files=None, - tobackup=None): + tobackup=None, keepchanges=False): wlock = lock = tr = None try: wlock = repo.wlock() @@ -690,11 +705,15 @@ try: ret = self._apply(repo, series, list, update_status, strict, patchdir, merge, all_files=all_files, - tobackup=tobackup) + tobackup=tobackup, keepchanges=keepchanges) tr.close() self.savedirty() return ret - except: + except AbortNoCleanup: + tr.close() + self.savedirty() + return 2, repo.dirstate.p1() + except: # re-raises try: tr.abort() finally: @@ -708,7 +727,7 @@ def _apply(self, repo, series, list=False, update_status=True, strict=False, patchdir=None, merge=None, all_files=None, - tobackup=None): + tobackup=None, keepchanges=False): """returns (error, hash) error = 1 for unable to read, 2 for patch failed, 3 for patch @@ -749,6 +768,9 @@ if tobackup: touched = patchmod.changedfiles(self.ui, repo, pf) touched = set(touched) & tobackup + if touched and keepchanges: + raise AbortNoCleanup( + _("local changes found, refresh first")) self.backup(repo, touched, copy=True) tobackup = tobackup - touched (patcherr, files, fuzz) = self.patch(repo, pf) @@ -862,7 +884,7 @@ def finish(self, repo, revs): # Manually trigger phase computation to ensure phasedefaults is # executed before we remove the patches. - repo._phaserev + repo._phasecache patches = self._revpatches(repo, sorted(revs)) qfinished = self._cleanup(patches, len(patches)) if qfinished and repo.ui.configbool('mq', 'secret', False): @@ -911,19 +933,35 @@ return top, patch return None, None - def checksubstate(self, repo): + def checksubstate(self, repo, baserev=None): '''return list of subrepos at a different revision than substate. Abort if any subrepos have uncommitted changes.''' inclsubs = [] wctx = repo[None] + if baserev: + bctx = repo[baserev] + else: + bctx = wctx.parents()[0] for s in wctx.substate: if wctx.sub(s).dirty(True): raise util.Abort( _("uncommitted changes in subrepository %s") % s) - elif wctx.sub(s).dirty(): + elif s not in bctx.substate or bctx.sub(s).dirty(): inclsubs.append(s) return inclsubs + def putsubstate2changes(self, substatestate, changes): + for files in changes[:3]: + if '.hgsubstate' in files: + return # already listed up + # not yet listed up + if substatestate in 'a?': + changes[1].append('.hgsubstate') + elif substatestate in 'r': + changes[2].append('.hgsubstate') + else: # modified + changes[0].append('.hgsubstate') + def localchangesfound(self, refresh=True): if refresh: raise util.Abort(_("local changes found, refresh first")) @@ -959,6 +997,10 @@ else: raise util.Abort(_('patch "%s" already exists') % name) + def checkkeepchanges(self, keepchanges, force): + if force and keepchanges: + raise util.Abort(_('cannot use both --force and --keep-changes')) + def new(self, repo, patchfn, *pats, **opts): """options: msg: a string or a no-argument function returning a string @@ -1038,12 +1080,7 @@ if commitfiles: parent = self.qparents(repo, n) if inclsubs: - if substatestate in 'a?': - changes[1].append('.hgsubstate') - elif substatestate in 'r': - changes[2].append('.hgsubstate') - else: # modified - changes[0].append('.hgsubstate') + self.putsubstate2changes(substatestate, changes) chunks = patchmod.diff(repo, node1=parent, node2=n, changes=changes, opts=diffopts) for chunk in chunks: @@ -1052,14 +1089,14 @@ r = self.qrepo() if r: r[None].add([patchfn]) - except: + except: # re-raises repo.rollback() raise except Exception: patchpath = self.join(patchfn) try: os.unlink(patchpath) - except: + except OSError: self.ui.warn(_('error unlinking %s\n') % patchpath) raise self.removeundo(repo) @@ -1156,8 +1193,10 @@ return self.series[i + off] raise util.Abort(_("patch %s not in series") % patch) - def push(self, repo, patch=None, force=False, list=False, - mergeq=None, all=False, move=False, exact=False, nobackup=False): + def push(self, repo, patch=None, force=False, list=False, mergeq=None, + all=False, move=False, exact=False, nobackup=False, + keepchanges=False): + self.checkkeepchanges(keepchanges, force) diffopts = self.diffopts() wlock = repo.wlock() try: @@ -1212,14 +1251,19 @@ if start == len(self.series): self.ui.warn(_('patch series already fully applied\n')) return 1 - if not force: + if not force and not keepchanges: self.checklocalchanges(repo, refresh=self.applied) if exact: + if keepchanges: + raise util.Abort( + _("cannot use --exact and --keep-changes together")) if move: - raise util.Abort(_("cannot use --exact and --move together")) + raise util.Abort(_('cannot use --exact and --move ' + 'together')) if self.applied: - raise util.Abort(_("cannot push --exact with applied patches")) + raise util.Abort(_('cannot push --exact with applied ' + 'patches')) root = self.series[start] target = patchheader(self.join(root), self.plainmode).parent if not target: @@ -1257,9 +1301,12 @@ end = self.series.index(patch, start) + 1 tobackup = set() - if not nobackup and force: + if (not nobackup and force) or keepchanges: m, a, r, d = self.checklocalchanges(repo, force=True) - tobackup.update(m + a) + if keepchanges: + tobackup.update(m + a + r + d) + else: + tobackup.update(m + a) s = self.series[start:end] all_files = set() @@ -1268,8 +1315,8 @@ ret = self.mergepatch(repo, mergeq, s, diffopts) else: ret = self.apply(repo, s, list, all_files=all_files, - tobackup=tobackup) - except: + tobackup=tobackup, keepchanges=keepchanges) + except: # re-raises self.ui.warn(_('cleaning up working directory...')) node = repo.dirstate.p1() hg.revert(repo, node, None) @@ -1299,7 +1346,8 @@ wlock.release() def pop(self, repo, patch=None, force=False, update=True, all=False, - nobackup=False): + nobackup=False, keepchanges=False): + self.checkkeepchanges(keepchanges, force) wlock = repo.wlock() try: if patch: @@ -1346,9 +1394,13 @@ tobackup = set() if update: - m, a, r, d = self.checklocalchanges(repo, force=force) - if not nobackup and force: - tobackup.update(m + a) + m, a, r, d = self.checklocalchanges( + repo, force=force or keepchanges) + if force: + if not nobackup: + tobackup.update(m + a) + elif keepchanges: + tobackup.update(m + a + r + d) self.applieddirty = True end = len(self.applied) @@ -1379,8 +1431,10 @@ if d: raise util.Abort(_("deletions found between repo revs")) - # backup local changes in --force case - self.backup(repo, set(a + m + r) & tobackup) + tobackup = set(a + m + r) & tobackup + if keepchanges and tobackup: + self.localchangesfound() + self.backup(repo, tobackup) for f in a: try: @@ -1438,10 +1492,14 @@ raise util.Abort(_("cannot refresh immutable revision"), hint=_('see "hg help phases" for details')) - inclsubs = self.checksubstate(repo) - cparents = repo.changelog.parents(top) patchparent = self.qparents(repo, top) + + inclsubs = self.checksubstate(repo, hex(patchparent)) + if inclsubs: + inclsubs.append('.hgsubstate') + substatestate = repo.dirstate['.hgsubstate'] + ph = patchheader(self.join(patchfn), self.plainmode) diffopts = self.diffopts({'git': opts.get('git')}, patchfn) if msg: @@ -1519,10 +1577,6 @@ a = list(aa) c = [filter(matchfn, l) for l in (m, a, r)] match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs)) - chunks = patchmod.diff(repo, patchparent, match=match, - changes=c, opts=diffopts) - for chunk in chunks: - patchf.write(chunk) try: if diffopts.git or diffopts.upgrade: @@ -1589,7 +1643,7 @@ self.applieddirty = True self.strip(repo, [top], update=False, backup='strip') - except: + except: # re-raises repo.dirstate.invalidate() raise @@ -1601,9 +1655,15 @@ n = newcommit(repo, oldphase, message, user, ph.date, match=match, force=True) # only write patch after a successful commit + if inclsubs: + self.putsubstate2changes(substatestate, c) + chunks = patchmod.diff(repo, patchparent, + changes=c, opts=diffopts) + for chunk in chunks: + patchf.write(chunk) patchf.close() self.applied.append(statusentry(n, patchfn)) - except: + except: # re-raises ctx = repo[cparents[0]] repo.dirstate.rebuild(ctx.node(), ctx.manifest()) self.savedirty() @@ -1730,7 +1790,7 @@ else: series.append(l) if datastart is None: - self.ui.warn(_("No saved patch data found\n")) + self.ui.warn(_("no saved patch data found\n")) return 1 self.ui.warn(_("restoring status: %s\n") % lines[0]) self.fullseries = series @@ -1757,7 +1817,7 @@ self.ui.status(_("updating queue directory\n")) r = self.qrepo() if not r: - self.ui.warn(_("Unable to load queue repository\n")) + self.ui.warn(_("unable to load queue repository\n")) return 1 hg.clean(r, qpp[0]) @@ -1842,6 +1902,8 @@ 'files')) rev = scmutil.revrange(repo, rev) rev.sort(reverse=True) + elif not files: + raise util.Abort(_('no files or revisions specified')) if (len(files) > 1 or len(rev) > 1) and patchname: raise util.Abort(_('option "-n" not valid when importing multiple ' 'patches')) @@ -1959,6 +2021,14 @@ self.removeundo(repo) return imported +def fixkeepchangesopts(ui, opts): + if (not ui.configbool('mq', 'keepchanges') or opts.get('force') + or opts.get('exact')): + return opts + opts = dict(opts) + opts['keep_changes'] = True + return opts + @command("qdelete|qremove|qrm", [('k', 'keep', None, _('keep patch file')), ('r', 'rev', [], @@ -2045,7 +2115,7 @@ _('place existing revisions under mq control'), _('REV')), ('g', 'git', None, _('use git extended diff format')), ('P', 'push', None, _('qpush after importing'))], - _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')) + _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...')) def qimport(ui, repo, *filename, **opts): """import a patch or existing changeset @@ -2140,7 +2210,8 @@ @command("qclone", [('', 'pull', None, _('use pull protocol to copy metadata')), - ('U', 'noupdate', None, _('do not update the new working directories')), + ('U', 'noupdate', None, + _('do not update the new working directories')), ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')), ('p', 'patches', '', @@ -2174,7 +2245,7 @@ # main repo (destination and sources) if dest is None: dest = hg.defaultdest(source) - sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source)) + sr = hg.peer(ui, opts, ui.expandpath(source)) # patches repo (source only) if opts.get('patches'): @@ -2182,18 +2253,19 @@ else: patchespath = patchdir(sr) try: - hg.repository(ui, patchespath) + hg.peer(ui, opts, patchespath) except error.RepoError: raise util.Abort(_('versioned patch repository not found' ' (see init --mq)')) qbase, destrev = None, None if sr.local(): - if sr.mq.applied and sr[qbase].phase() != phases.secret: - qbase = sr.mq.applied[0].node + repo = sr.local() + if repo.mq.applied and repo[qbase].phase() != phases.secret: + qbase = repo.mq.applied[0].node if not hg.islocal(dest): - heads = set(sr.heads()) - destrev = list(heads.difference(sr.heads(qbase))) - destrev.append(sr.changelog.parents(qbase)[0]) + heads = set(repo.heads()) + destrev = list(heads.difference(repo.heads(qbase))) + destrev.append(repo.changelog.parents(qbase)[0]) elif sr.capable('lookup'): try: qbase = sr.lookup('qbase') @@ -2213,13 +2285,14 @@ stream=opts.get('uncompressed')) if dr.local(): + repo = dr.local() if qbase: ui.note(_('stripping applied patches from destination ' 'repository\n')) - dr.mq.strip(dr, [qbase], update=False, backup=None) + repo.mq.strip(repo, [qbase], update=False, backup=None) if not opts.get('noupdate'): ui.note(_('updating destination repository\n')) - hg.update(dr, dr.changelog.tip()) + hg.update(repo, repo.changelog.tip()) @command("qcommit|qci", commands.table["^commit|ci"][1], @@ -2242,7 +2315,8 @@ """print the entire series file Returns 0 on success.""" - repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary')) + repo.mq.qseries(repo, missing=opts.get('missing'), + summary=opts.get('summary')) return 0 @command("qtop", seriesopts, _('hg qtop [-s]')) @@ -2461,9 +2535,10 @@ for f in files: p = q.lookup(f) if p in patches or p == parent: - ui.warn(_('Skipping already folded patch %s\n') % p) + ui.warn(_('skipping already folded patch %s\n') % p) if q.isapplied(p): - raise util.Abort(_('qfold cannot fold already applied patch %s') % p) + raise util.Abort(_('qfold cannot fold already applied patch %s') + % p) patches.append(p) for p in patches: @@ -2497,20 +2572,26 @@ wlock.release() @command("qgoto", - [('f', 'force', None, _('overwrite any local changes')), + [('', 'keep-changes', None, + _('tolerate non-conflicting local changes')), + ('f', 'force', None, _('overwrite any local changes')), ('', 'no-backup', None, _('do not save backup copies of files'))], _('hg qgoto [OPTION]... PATCH')) def goto(ui, repo, patch, **opts): '''push or pop patches until named patch is at top of stack Returns 0 on success.''' + opts = fixkeepchangesopts(ui, opts) q = repo.mq patch = q.lookup(patch) nobackup = opts.get('no_backup') + keepchanges = opts.get('keep_changes') if q.isapplied(patch): - ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup) + ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup, + keepchanges=keepchanges) else: - ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup) + ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup, + keepchanges=keepchanges) q.savedirty() return ret @@ -2566,7 +2647,8 @@ args = list(args) if opts.get('list'): if args or opts.get('none'): - raise util.Abort(_('cannot mix -l/--list with options or arguments')) + raise util.Abort(_('cannot mix -l/--list with options or ' + 'arguments')) for i in xrange(len(q.series)): status(i) return @@ -2630,8 +2712,11 @@ return newpath @command("^qpush", - [('f', 'force', None, _('apply on top of local changes')), - ('e', 'exact', None, _('apply the target patch to its recorded parent')), + [('', 'keep-changes', None, + _('tolerate non-conflicting local changes')), + ('f', 'force', None, _('apply on top of local changes')), + ('e', 'exact', None, + _('apply the target patch to its recorded parent')), ('l', 'list', None, _('list patch name in commit text')), ('a', 'all', None, _('apply all patches')), ('m', 'merge', None, _('merge from another queue (DEPRECATED)')), @@ -2644,14 +2729,17 @@ def push(ui, repo, patch=None, **opts): """push the next patch onto the stack - When -f/--force is applied, all local changes in patched files - will be lost. + By default, abort if the working directory contains uncommitted + changes. With --keep-changes, abort only if the uncommitted files + overlap with patched files. With -f/--force, backup and patch over + uncommitted changes. Return 0 on success. """ q = repo.mq mergeq = None + opts = fixkeepchangesopts(ui, opts) if opts.get('merge'): if opts.get('name'): newpath = repo.join(opts.get('name')) @@ -2664,25 +2752,34 @@ 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'), - exact=opts.get('exact'), nobackup=opts.get('no_backup')) + exact=opts.get('exact'), nobackup=opts.get('no_backup'), + keepchanges=opts.get('keep_changes')) return ret @command("^qpop", [('a', 'all', None, _('pop all patches')), ('n', 'name', '', _('queue name to pop (DEPRECATED)'), _('NAME')), + ('', 'keep-changes', None, + _('tolerate non-conflicting local changes')), ('f', 'force', None, _('forget any local changes to patched files')), ('', 'no-backup', None, _('do not save backup copies of files'))], _('hg qpop [-a] [-f] [PATCH | INDEX]')) def pop(ui, repo, patch=None, **opts): """pop the current patch off the stack - By default, pops off the top of the patch stack. If given a patch - name, keeps popping off patches until the named patch is at the - top of the stack. + Without argument, pops off the top of the patch stack. If given a + patch name, keeps popping off patches until the named patch is at + the top of the stack. + + By default, abort if the working directory contains uncommitted + changes. With --keep-changes, abort only if the uncommitted files + overlap with patched files. With -f/--force, backup and discard + changes made to such files. Return 0 on success. """ + opts = fixkeepchangesopts(ui, opts) localupdate = True if opts.get('name'): q = queue(ui, repo.path, repo.join(opts.get('name'))) @@ -2691,7 +2788,8 @@ else: q = repo.mq ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate, - all=opts.get('all'), nobackup=opts.get('no_backup')) + all=opts.get('all'), nobackup=opts.get('no_backup'), + keepchanges=opts.get('keep_changes')) q.savedirty() return ret @@ -2819,8 +2917,10 @@ ('', 'no-backup', None, _('no backups')), ('', 'nobackup', None, _('no backups (DEPRECATED)')), ('n', '', None, _('ignored (DEPRECATED)')), - ('k', 'keep', None, _("do not modify working copy during strip"))], - _('hg strip [-k] [-f] [-n] REV...')) + ('k', 'keep', None, _("do not modify working copy during strip")), + ('B', 'bookmark', '', _("remove revs only reachable from given" + " bookmark"))], + _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...')) def strip(ui, repo, *revs, **opts): """strip changesets and all their descendants from the repository @@ -2859,10 +2959,36 @@ cl = repo.changelog revs = list(revs) + opts.get('rev') revs = set(scmutil.revrange(repo, revs)) + + if opts.get('bookmark'): + mark = opts.get('bookmark') + marks = repo._bookmarks + if mark not in marks: + raise util.Abort(_("bookmark '%s' not found") % mark) + + # If the requested bookmark is not the only one pointing to a + # a revision we have to only delete the bookmark and not strip + # anything. revsets cannot detect that case. + uniquebm = True + for m, n in marks.iteritems(): + if m != mark and n == repo[mark].node(): + uniquebm = False + break + if uniquebm: + rsrevs = repo.revs("ancestors(bookmark(%s)) - " + "ancestors(head() and not bookmark(%s)) - " + "ancestors(bookmark() and not bookmark(%s))", + mark, mark, mark) + revs.update(set(rsrevs)) + if not revs: + del marks[mark] + repo._writebookmarks(mark) + ui.write(_("bookmark '%s' deleted\n") % mark) + if not revs: raise util.Abort(_('empty revision set')) - descendants = set(cl.descendants(*revs)) + descendants = set(cl.descendants(revs)) strippedrevs = revs.union(descendants) roots = revs.difference(descendants) @@ -2904,8 +3030,14 @@ finally: wlock.release() + if opts.get('bookmark'): + del marks[mark] + repo._writebookmarks(marks) + ui.write(_("bookmark '%s' deleted\n") % mark) + repo.mq.strip(repo, revs, backup=backup, update=update, force=opts.get('force')) + return 0 @command("qselect", @@ -3313,8 +3445,8 @@ tags = result[0] for patch in mqtags: if patch[1] in tags: - self.ui.warn(_('Tag %s overrides mq patch of the same name\n') - % patch[1]) + self.ui.warn(_('tag %s overrides mq patch of the same ' + 'name\n') % patch[1]) else: tags[patch[1]] = patch[0] @@ -3424,13 +3556,12 @@ applied = set([repo[r.node].rev() for r in repo.mq.applied]) return [r for r in subset if r in applied] -def extsetup(ui): - revset.symbols['mq'] = revsetmq - # tell hggettext to extract docstrings from these functions: i18nfunctions = [revsetmq] -def uisetup(ui): +def extsetup(ui): + # Ensure mq wrappers are called first, regardless of extension load order by + # NOT wrapping in uisetup() and instead deferring to init stage two here. mqopt = [('', 'mq', None, _("operate on patch repository"))] extensions.wrapcommand(commands.table, 'import', mqimport) @@ -3455,6 +3586,7 @@ if extmodule.__file__ != __file__: dotable(getattr(extmodule, 'cmdtable', {})) + revset.symbols['mq'] = revsetmq colortable = {'qguard.negative': 'red', 'qguard.positive': 'yellow', diff -r 98823bd0d697 -r a06e2681dd17 hgext/notify.py --- a/hgext/notify.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/notify.py Wed Jul 18 19:08:25 2012 -0500 @@ -132,6 +132,8 @@ from mercurial import patch, cmdutil, templater, util, mail import email.Parser, email.Errors, fnmatch, socket, time +testedwith = 'internal' + # template for single changeset can include email headers. single_template = ''' Subject: changeset in {webroot}: {desc|firstline|strip} @@ -357,8 +359,8 @@ author = repo[rev].user() else: data += ui.popbuffer() - ui.note(_('notify: suppressing notification for merge %d:%s\n') % - (rev, repo[rev].hex()[:12])) + ui.note(_('notify: suppressing notification for merge %d:%s\n') + % (rev, repo[rev].hex()[:12])) ui.pushbuffer() if count: n.diff(ctx, repo['tip']) diff -r 98823bd0d697 -r a06e2681dd17 hgext/pager.py --- a/hgext/pager.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/pager.py Wed Jul 18 19:08:25 2012 -0500 @@ -51,7 +51,9 @@ from mercurial import commands, dispatch, util, extensions from mercurial.i18n import _ -def _runpager(p): +testedwith = 'internal' + +def _runpager(ui, p): pager = subprocess.Popen(p, shell=True, bufsize=-1, close_fds=util.closefds, stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr) @@ -59,7 +61,7 @@ stdout = os.dup(sys.stdout.fileno()) stderr = os.dup(sys.stderr.fileno()) os.dup2(pager.stdin.fileno(), sys.stdout.fileno()) - if util.isatty(sys.stderr): + if ui._isatty(sys.stderr): os.dup2(pager.stdin.fileno(), sys.stderr.fileno()) @atexit.register @@ -70,7 +72,7 @@ pager.wait() def uisetup(ui): - if ui.plain() or '--debugger' in sys.argv or not util.isatty(sys.stdout): + if '--debugger' in sys.argv or not ui.formatted(): return def pagecmd(orig, ui, options, cmd, cmdfunc): @@ -87,7 +89,7 @@ ui.setconfig('ui', 'interactive', False) if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, signal.SIG_DFL) - _runpager(p) + _runpager(ui, p) return orig(ui, options, cmd, cmdfunc) extensions.wrapfunction(dispatch, '_runcommand', pagecmd) diff -r 98823bd0d697 -r a06e2681dd17 hgext/patchbomb.py --- a/hgext/patchbomb.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/patchbomb.py Wed Jul 18 19:08:25 2012 -0500 @@ -48,13 +48,14 @@ import os, errno, socket, tempfile, cStringIO import email.MIMEMultipart, email.MIMEBase import email.Utils, email.Encoders, email.Generator -from mercurial import cmdutil, commands, hg, mail, patch, util, discovery +from mercurial import cmdutil, commands, hg, mail, patch, util from mercurial import scmutil from mercurial.i18n import _ from mercurial.node import bin cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' def prompt(ui, prompt, default=None, rest=':'): if default: @@ -109,7 +110,8 @@ msg = email.MIMEMultipart.MIMEMultipart() if body: msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) - p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test')) + p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', + opts.get('test')) binnode = bin(node) # if node is mq patch, it will have the patch file's name as a tag if not patchname: @@ -119,7 +121,8 @@ patchname = patchtags[0] elif total > 1: patchname = cmdutil.makefilename(repo, '%b-%n.patch', - binnode, seqno=idx, total=total) + binnode, seqno=idx, + total=total) else: patchname = cmdutil.makefilename(repo, '%b.patch', binnode) disposition = 'inline' @@ -270,18 +273,18 @@ def getoutgoing(dest, revs): '''Return the revisions present locally but not in dest''' - dest = ui.expandpath(dest or 'default-push', dest or 'default') - dest, branches = hg.parseurl(dest) - revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) - other = hg.peer(repo, opts, dest) - ui.status(_('comparing with %s\n') % util.hidepassword(dest)) - common, _anyinc, _heads = discovery.findcommonincoming(repo, other) - nodes = revs and map(repo.lookup, revs) or revs - o = repo.changelog.findmissing(common, heads=nodes) - if not o: + url = ui.expandpath(dest or 'default-push', dest or 'default') + url = hg.parseurl(url)[0] + ui.status(_('comparing with %s\n') % util.hidepassword(url)) + + revs = [r for r in scmutil.revrange(repo, revs) if r >= 0] + if not revs: + revs = [len(repo) - 1] + revs = repo.revs('outgoing(%s) and ::%ld', dest or '', revs) + if not revs: ui.status(_("no changes found\n")) return [] - return [str(repo.changelog.rev(r)) for r in o] + return [str(r) for r in revs] def getpatches(revs): for r in scmutil.revrange(repo, revs): @@ -302,7 +305,7 @@ finally: try: os.unlink(tmpfn) - except: + except OSError: pass os.rmdir(tmpdir) @@ -358,7 +361,7 @@ def getpatchmsgs(patches, patchnames=None): msgs = [] - ui.write(_('This patch series consists of %d patches.\n\n') + ui.write(_('this patch series consists of %d patches.\n\n') % len(patches)) # build the intro message, or skip it if the user declines @@ -523,7 +526,7 @@ if replyto: m['Reply-To'] = ', '.join(replyto) if opts.get('test'): - ui.status(_('Displaying '), subj, ' ...\n') + ui.status(_('displaying '), subj, ' ...\n') ui.flush() if 'PAGER' in os.environ and not ui.plain(): fp = util.popen(os.environ['PAGER'], 'w') @@ -541,7 +544,7 @@ else: if not sendmail: sendmail = mail.connect(ui, mbox=mbox) - ui.status(_('Sending '), subj, ' ...\n') + ui.status(_('sending '), subj, ' ...\n') ui.progress(_('sending'), i, item=subj, total=len(msgs)) if not mbox: # Exim does not remove the Bcc field diff -r 98823bd0d697 -r a06e2681dd17 hgext/progress.py --- a/hgext/progress.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/progress.py Wed Jul 18 19:08:25 2012 -0500 @@ -38,14 +38,14 @@ import sys import time -from mercurial import util from mercurial.i18n import _ +testedwith = 'internal' def spacejoin(*args): return ' '.join(s for s in args if s) def shouldprint(ui): - return util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty') + return ui._isatty(sys.stderr) or ui.configbool('progress', 'assume-tty') def fmtremaining(seconds): if seconds < 60: @@ -237,7 +237,7 @@ # truncate the list of topics assuming all topics within # this one are also closed if topic in self.topics: - self.topics = self.topics[:self.topics.index(topic)] + self.topics = self.topics[:self.topics.index(topic)] else: if topic not in self.topics: self.starttimes[topic] = now diff -r 98823bd0d697 -r a06e2681dd17 hgext/purge.py --- a/hgext/purge.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/purge.py Wed Jul 18 19:08:25 2012 -0500 @@ -30,6 +30,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' @command('purge|clean', [('a', 'abort-on-err', None, _('abort if an error occurs')), @@ -100,10 +101,10 @@ status = repo.status(match=match, ignored=opts['all'], unknown=True) for f in sorted(status[4] + status[5]): - ui.note(_('Removing file %s\n') % f) + ui.note(_('removing file %s\n') % f) remove(removefile, f) for f in sorted(directories, reverse=True): if match(f) and not os.listdir(repo.wjoin(f)): - ui.note(_('Removing directory %s\n') % f) + ui.note(_('removing directory %s\n') % f) remove(os.rmdir, f) diff -r 98823bd0d697 -r a06e2681dd17 hgext/rebase.py --- a/hgext/rebase.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/rebase.py Wed Jul 18 19:08:25 2012 -0500 @@ -26,6 +26,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' @command('rebase', [('s', 'source', '', @@ -47,8 +48,7 @@ _('read collapse commit message from file'), _('FILE')), ('', 'keep', False, _('keep original changesets')), ('', 'keepbranches', False, _('keep original branch names')), - ('D', 'detach', False, _('force detaching of source from its original ' - 'branch')), + ('D', 'detach', False, _('(DEPRECATED)')), ('t', 'tool', '', _('specify merge tool')), ('c', 'continue', False, _('continue an interrupted rebase')), ('a', 'abort', False, _('abort an interrupted rebase'))] + @@ -130,7 +130,6 @@ extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion keepf = opts.get('keep', False) keepbranchesf = opts.get('keepbranches', False) - detachf = opts.get('detach', False) # keepopen is not meant for use on the command line, but by # other extensions keepopen = opts.get('keepopen', False) @@ -145,8 +144,6 @@ if collapsef: raise util.Abort( _('cannot use collapse with continue or abort')) - if detachf: - raise util.Abort(_('cannot use detach with continue or abort')) if srcf or basef or destf: raise util.Abort( _('abort and continue do not allow specifying revisions')) @@ -167,12 +164,6 @@ if revf and srcf: raise util.Abort(_('cannot specify both a ' 'revision and a source')) - if detachf: - if not (srcf or revf): - raise util.Abort( - _('detach requires a revision to be specified')) - if basef: - raise util.Abort(_('cannot specify a base with detach')) cmdutil.bailifchanged(repo) @@ -182,7 +173,7 @@ branch = repo[None].branch() dest = repo[branch] else: - dest = repo[destf] + dest = scmutil.revsingle(repo, destf) if revf: rebaseset = repo.revs('%lr', revf) @@ -201,7 +192,7 @@ root = None if not rebaseset: - repo.ui.debug('base is ancestor of destination') + repo.ui.debug('base is ancestor of destination\n') result = None elif not keepf and list(repo.revs('first(children(%ld) - %ld)', rebaseset, rebaseset)): @@ -214,7 +205,7 @@ % repo[root], hint=_('see hg help phases for details')) else: - result = buildstate(repo, dest, rebaseset, detachf) + result = buildstate(repo, dest, rebaseset, collapsef) if not result: # Empty state built, nothing to rebase @@ -223,7 +214,7 @@ else: originalwd, target, state = result if collapsef: - targetancestors = set(repo.changelog.ancestors(target)) + targetancestors = set(repo.changelog.ancestors([target])) targetancestors.add(target) external = checkexternal(repo, state, targetancestors) @@ -242,7 +233,7 @@ # Rebase if not targetancestors: - targetancestors = set(repo.changelog.ancestors(target)) + targetancestors = set(repo.changelog.ancestors([target])) targetancestors.add(target) # Keep track of the current bookmarks in order to reset them later @@ -268,7 +259,7 @@ else: try: ui.setconfig('ui', 'forcemerge', opts.get('tool', '')) - stats = rebasenode(repo, rev, p1, state) + stats = rebasenode(repo, rev, p1, state, collapsef) if stats and stats[3] > 0: raise util.Abort(_('unresolved conflicts (see hg ' 'resolve, then hg rebase --continue)')) @@ -323,7 +314,7 @@ # Remove no more useful revisions rebased = [rev for rev in state if state[rev] != nullmerge] if rebased: - if set(repo.changelog.descendants(min(rebased))) - set(state): + if set(repo.changelog.descendants([min(rebased)])) - set(state): ui.warn(_("warning: new changesets detected " "on source branch, not stripping\n")) else: @@ -391,7 +382,7 @@ repo.dirstate.invalidate() raise -def rebasenode(repo, rev, p1, state): +def rebasenode(repo, rev, p1, state, collapse): 'Rebase a single revision' # Merge phase # Update to target and merge it with local @@ -405,7 +396,9 @@ base = None if repo[rev].rev() != repo[min(state)].rev(): base = repo[rev].p1().node() - return merge.update(repo, rev, True, True, False, base) + # When collapsing in-place, the parent is the common ancestor, we + # have to allow merging with it. + return merge.update(repo, rev, True, True, False, base, collapse) def defineparents(repo, rev, target, state, targetancestors): 'Return the new parent relationship of the revision that will be rebased' @@ -579,7 +572,7 @@ descendants = set() if dstates: - descendants = set(repo.changelog.descendants(*dstates)) + descendants = set(repo.changelog.descendants(dstates)) if descendants - set(dstates): repo.ui.warn(_("warning: new changesets detected on target branch, " "can't abort\n")) @@ -596,13 +589,13 @@ repo.ui.warn(_('rebase aborted\n')) return 0 -def buildstate(repo, dest, rebaseset, detach): +def buildstate(repo, dest, rebaseset, collapse): '''Define which revisions are going to be rebased and where repo: repo dest: context rebaseset: set of rev - detach: boolean''' + ''' # This check isn't strictly necessary, since mq detects commits over an # applied patch. But it prevents messing up the working directory when @@ -611,7 +604,6 @@ [s.node for s in repo.mq.applied]): raise util.Abort(_('cannot rebase onto an applied mq patch')) - detachset = set() roots = list(repo.set('roots(%ld)', rebaseset)) if not roots: raise util.Abort(_('no matching revisions')) @@ -624,17 +616,53 @@ raise util.Abort(_('source is ancestor of destination')) if commonbase == dest: samebranch = root.branch() == dest.branch() - if samebranch and root in dest.children(): - repo.ui.debug('source is a child of destination') - return None - # rebase on ancestor, force detach - detach = True - if detach: - detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root) + if not collapse and samebranch and root in dest.children(): + repo.ui.debug('source is a child of destination\n') + return None repo.ui.debug('rebase onto %d starting from %d\n' % (dest, root)) state = dict.fromkeys(rebaseset, nullrev) - state.update(dict.fromkeys(detachset, nullmerge)) + # Rebase tries to turn into a parent of while + # preserving the number of parents of rebased changesets: + # + # - A changeset with a single parent will always be rebased as a + # changeset with a single parent. + # + # - A merge will be rebased as merge unless its parents are both + # ancestors of or are themselves in the rebased set and + # pruned while rebased. + # + # If one parent of is an ancestor of , the rebased + # version of this parent will be . This is always true with + # --base option. + # + # Otherwise, we need to *replace* the original parents with + # . This "detaches" the rebased set from its former location + # and rebases it onto . Changes introduced by ancestors of + # not common with (the detachset, marked as + # nullmerge) are "removed" from the rebased changesets. + # + # - If has a single parent, set it to . + # + # - If is a merge, we cannot decide which parent to + # replace, the rebase operation is not clearly defined. + # + # The table below sums up this behavior: + # + # +--------------------+----------------------+-------------------------+ + # | | one parent | merge | + # +--------------------+----------------------+-------------------------+ + # | parent in :: | new parent is | parents in :: are | + # | | | remapped to | + # +--------------------+----------------------+-------------------------+ + # | unrelated source | new parent is | ambiguous, abort | + # +--------------------+----------------------+-------------------------+ + # + # The actual abort is handled by `defineparents` + if len(root.parents()) <= 1: + # (strict) ancestors of not ancestors of + detachset = repo.revs('::%d - ::%d - %d', root, commonbase, root) + state.update(dict.fromkeys(detachset, nullmerge)) return repo['.'].rev(), dest.rev(), state def pullrebase(orig, ui, repo, *args, **opts): diff -r 98823bd0d697 -r a06e2681dd17 hgext/record.py --- a/hgext/record.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/record.py Wed Jul 18 19:08:25 2012 -0500 @@ -14,6 +14,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)') @@ -516,10 +517,11 @@ '(use "hg commit" instead)')) changes = repo.status(match=match)[:3] - diffopts = mdiff.diffopts(git=True, nodates=True, - ignorews=opts.get('ignore_all_space'), - ignorewsamount=opts.get('ignore_space_change'), - ignoreblanklines=opts.get('ignore_blank_lines')) + diffopts = mdiff.diffopts( + git=True, nodates=True, + ignorews=opts.get('ignore_all_space'), + ignorewsamount=opts.get('ignore_space_change'), + ignoreblanklines=opts.get('ignore_blank_lines')) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO() fp.write(''.join(chunks)) diff -r 98823bd0d697 -r a06e2681dd17 hgext/relink.py --- a/hgext/relink.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/relink.py Wed Jul 18 19:08:25 2012 -0500 @@ -11,6 +11,8 @@ from mercurial.i18n import _ import os, stat +testedwith = 'internal' + def relink(ui, repo, origin=None, **opts): """recreate hardlinks between two repositories @@ -41,8 +43,6 @@ raise util.Abort(_('hardlinks are not supported on this system')) src = hg.repository(ui, ui.expandpath(origin or 'default-relink', origin or 'default')) - if not src.local(): - raise util.Abort(_('must specify local origin repository')) ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path)) if repo.root == src.root: ui.status(_('there is nothing to relink\n')) @@ -79,7 +79,7 @@ dirnames.sort() relpath = dirpath[len(src) + seplen:] for filename in sorted(filenames): - if not filename[-2:] in ('.d', '.i'): + if filename[-2:] not in ('.d', '.i'): continue st = os.stat(os.path.join(dirpath, filename)) if not stat.S_ISREG(st.st_mode): diff -r 98823bd0d697 -r a06e2681dd17 hgext/schemes.py --- a/hgext/schemes.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/schemes.py Wed Jul 18 19:08:25 2012 -0500 @@ -44,6 +44,8 @@ from mercurial import extensions, hg, templater, util from mercurial.i18n import _ +testedwith = 'internal' + class ShortRepository(object): def __init__(self, url, scheme, templater): diff -r 98823bd0d697 -r a06e2681dd17 hgext/share.py --- a/hgext/share.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/share.py Wed Jul 18 19:08:25 2012 -0500 @@ -8,6 +8,8 @@ from mercurial.i18n import _ from mercurial import hg, commands, util +testedwith = 'internal' + def share(ui, source, dest=None, noupdate=False): """create a new shared repository diff -r 98823bd0d697 -r a06e2681dd17 hgext/transplant.py --- a/hgext/transplant.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/transplant.py Wed Jul 18 19:08:25 2012 -0500 @@ -25,6 +25,7 @@ cmdtable = {} command = cmdutil.command(cmdtable) +testedwith = 'internal' class transplantentry(object): def __init__(self, lnode, rnode): @@ -88,16 +89,21 @@ def applied(self, repo, node, parent): '''returns True if a node is already an ancestor of parent - or has already been transplanted''' + or is parent or has already been transplanted''' + if hasnode(repo, parent): + parentrev = repo.changelog.rev(parent) if hasnode(repo, node): - if node in repo.changelog.reachable(parent, stop=node): + rev = repo.changelog.rev(node) + reachable = repo.changelog.incancestors([parentrev], rev) + if rev in reachable: return True for t in self.transplants.get(node): # it might have been stripped if not hasnode(repo, t.lnode): self.transplants.remove(t) return False - if t.lnode in repo.changelog.reachable(parent, stop=t.lnode): + lnoderev = repo.changelog.rev(t.lnode) + if lnoderev in repo.changelog.incancestors([parentrev], lnoderev): return True return False @@ -124,7 +130,7 @@ continue parents = source.changelog.parents(node) - if not opts.get('filter'): + if not (opts.get('filter') or opts.get('log')): # If the changeset parent is the same as the # wdir's parent, just pull it. if parents[0] == p1: @@ -133,7 +139,7 @@ continue if pulls: if source != repo: - repo.pull(source, heads=pulls) + repo.pull(source.peer(), heads=pulls) merge.update(repo, pulls[-1], False, False, None) p1, p2 = repo.dirstate.parents() pulls = [] @@ -197,7 +203,7 @@ os.unlink(patchfile) tr.close() if pulls: - repo.pull(source, heads=pulls) + repo.pull(source.peer(), heads=pulls) merge.update(repo, pulls[-1], False, False, None) finally: self.saveseries(revmap, merges) @@ -534,7 +540,7 @@ transplanted, otherwise you will be prompted to select the changesets you want. - :hg:`transplant --branch REVISION --all` will transplant the + :hg:`transplant --branch REV --all` will transplant the selected branch (up to the named revision) onto your current working directory. @@ -608,9 +614,9 @@ sourcerepo = opts.get('source') if sourcerepo: - source = hg.peer(ui, opts, ui.expandpath(sourcerepo)) - branches = map(source.lookup, opts.get('branch', ())) - source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, source, + peer = hg.peer(ui, opts, ui.expandpath(sourcerepo)) + branches = map(peer.lookup, opts.get('branch', ())) + source, csets, cleanupfn = bundlerepo.getremotechanges(ui, repo, peer, onlyheads=branches, force=True) else: source = repo diff -r 98823bd0d697 -r a06e2681dd17 hgext/win32mbcs.py --- a/hgext/win32mbcs.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/win32mbcs.py Wed Jul 18 19:08:25 2012 -0500 @@ -48,6 +48,7 @@ import os, sys from mercurial.i18n import _ from mercurial import util, encoding +testedwith = 'internal' _encoding = None # see extsetup @@ -164,4 +165,3 @@ if '--debug' in sys.argv: ui.write("[win32mbcs] activated with encoding: %s\n" % _encoding) - diff -r 98823bd0d697 -r a06e2681dd17 hgext/win32text.py --- a/hgext/win32text.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/win32text.py Wed Jul 18 19:08:25 2012 -0500 @@ -46,6 +46,8 @@ from mercurial import util import re +testedwith = 'internal' + # regexp for single LF without CR preceding. re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE) @@ -128,7 +130,7 @@ data = c[f].data() if not util.binary(data) and newline in data: if not halt: - ui.warn(_('Attempt to commit or push text file(s) ' + ui.warn(_('attempt to commit or push text file(s) ' 'using %s line endings\n') % newlinestr[newline]) ui.warn(_('in %s: %s\n') % (short(c.node()), f)) diff -r 98823bd0d697 -r a06e2681dd17 hgext/zeroconf/__init__.py --- a/hgext/zeroconf/__init__.py Thu Jul 19 00:53:27 2012 +0200 +++ b/hgext/zeroconf/__init__.py Wed Jul 18 19:08:25 2012 -0500 @@ -32,6 +32,8 @@ from mercurial.hgweb import hgweb_mod from mercurial.hgweb import hgwebdir_mod +testedwith = 'internal' + # publish server = None @@ -44,7 +46,7 @@ s.connect(('1.0.0.1', 0)) ip = s.getsockname()[0] return ip - except: + except socket.error: pass # Generic method, sometimes gives useless results @@ -61,7 +63,7 @@ s.connect(('1.0.0.1', 1)) ip = s.getsockname()[0] return ip - except: + except socket.error: pass return dumbip @@ -119,7 +121,8 @@ name = os.path.basename(repo) path = (prefix + repo).strip('/') desc = u.config('web', 'description', name) - publish(name, desc, path, util.getport(u.config("web", "port", 8000))) + publish(name, desc, path, + util.getport(u.config("web", "port", 8000))) # listen diff -r 98823bd0d697 -r a06e2681dd17 i18n/de.po diff -r 98823bd0d697 -r a06e2681dd17 i18n/ru.po --- a/i18n/ru.po Thu Jul 19 00:53:27 2012 +0200 +++ b/i18n/ru.po Wed Jul 18 19:08:25 2012 -0500 @@ -172,7 +172,7 @@ msgstr "" "Project-Id-Version: Mercurial\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-06-05 10:52+0400\n" +"POT-Creation-Date: 2012-06-15 10:29+0400\n" "PO-Revision-Date: 2011-05-12 23:48+0400\n" "Last-Translator: Alexander Sauta \n" "Language-Team: Russian\n" @@ -11591,8 +11591,8 @@ msgid "show normal and closed branch heads" msgstr "показать обычные и закрытые головы веток" -msgid "[-ac] [-r STARTREV] [REV]..." -msgstr "[-ac] [-r НАЧРЕВИЗИЯ] [РЕВИЗИЯ]..." +msgid "[-ct] [-r STARTREV] [REV]..." +msgstr "[-ct] [-r НАЧРЕВИЗИЯ] [РЕВИЗИЯ]..." msgid "show current repository heads or show branch heads" msgstr "показать головы текущего хранилища или головы веток" @@ -13894,16 +13894,17 @@ " bookmarks`)." msgid "" -" If the changeset is not a descendant of the working directory's\n" -" parent, the update is aborted. With the -c/--check option, the\n" -" working directory is checked for uncommitted changes; if none are\n" -" found, the working directory is updated to the specified\n" +" If the changeset is not a descendant or ancestor of the working\n" +" directory's parent, the update is aborted. With the -c/--check\n" +" option, the working directory is checked for uncommitted changes; if\n" +" none are found, the working directory is updated to the specified\n" " changeset." msgstr "" -" Если РЕВИЗИЯ - не потомок родителя рабочего каталога, update\n" -" завершается с ошибкой. С опцией -c/--check рабочий каталог\n" -" проверяется на наличие незафиксированных изменений; если таковых\n" -" нет, рабочий каталог обновляется до указанной ревизии." +" Если ревизия не является потомком или предком родительской ревизии\n" +" рабочего каталога, выполнение update прерывается. С параметром -c/--" +"check\n" +" рабочий каталог проверяется на наличие незафиксированных изменений;\n" +" если таковых нет, рабочий каталог обновляется до указанной ревизии." msgid "" " Update sets the working directory's parent revison to the specified\n" diff -r 98823bd0d697 -r a06e2681dd17 mercurial/archival.py --- a/mercurial/archival.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/archival.py Wed Jul 18 19:08:25 2012 -0500 @@ -7,6 +7,7 @@ from i18n import _ from node import hex +import match as matchmod import cmdutil import scmutil, util, encoding import cStringIO, os, tarfile, time, zipfile @@ -284,6 +285,7 @@ if subrepos: for subpath in ctx.substate: sub = ctx.sub(subpath) - sub.archive(repo.ui, archiver, prefix) + submatch = matchmod.narrowmatcher(subpath, matchfn) + sub.archive(repo.ui, archiver, prefix, submatch) archiver.done() diff -r 98823bd0d697 -r a06e2681dd17 mercurial/base85.c --- a/mercurial/base85.c Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/base85.c Wed Jul 18 19:08:25 2012 -0500 @@ -9,6 +9,7 @@ Largely based on git's implementation */ +#define PY_SSIZE_T_CLEAN #include #include "util.h" @@ -33,7 +34,7 @@ const unsigned char *text; PyObject *out; char *dst; - int len, olen, i; + Py_ssize_t len, olen, i; unsigned int acc, val, ch; int pad = 0; @@ -81,7 +82,8 @@ PyObject *out; const char *text; char *dst; - int len, i, j, olen, c, cap; + Py_ssize_t len, i, j, olen, cap; + int c; unsigned int acc; if (!PyArg_ParseTuple(args, "s#", &text, &len)) @@ -109,7 +111,8 @@ if (c < 0) return PyErr_Format( PyExc_ValueError, - "bad base85 character at position %d", i); + "bad base85 character at position %d", + (int)i); acc = acc * 85 + c; } if (i++ < len) @@ -118,13 +121,15 @@ if (c < 0) return PyErr_Format( PyExc_ValueError, - "bad base85 character at position %d", i); + "bad base85 character at position %d", + (int)i); /* overflow detection: 0xffffffff == "|NsC0", * "|NsC" == 0x03030303 */ if (acc > 0x03030303 || (acc *= 85) > 0xffffffff - c) return PyErr_Format( PyExc_ValueError, - "bad base85 sequence at position %d", i); + "bad base85 sequence at position %d", + (int)i); acc += c; } diff -r 98823bd0d697 -r a06e2681dd17 mercurial/bdiff.c --- a/mercurial/bdiff.c Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/bdiff.c Wed Jul 18 19:08:25 2012 -0500 @@ -9,6 +9,7 @@ Based roughly on Python difflib */ +#define PY_SSIZE_T_CLEAN #include #include #include @@ -17,7 +18,8 @@ #include "util.h" struct line { - int hash, len, n, e; + int hash, n, e; + Py_ssize_t len; const char *l; }; @@ -31,7 +33,7 @@ struct hunk *next; }; -static int splitlines(const char *a, int len, struct line **lr) +static int splitlines(const char *a, Py_ssize_t len, struct line **lr) { unsigned hash; int i; @@ -338,7 +340,8 @@ PyObject *result = NULL; struct line *al, *bl; struct hunk l, *h; - int an, bn, len = 0, la, lb, count; + int an, bn, count; + Py_ssize_t len = 0, la, lb; PyThreadState *_save; if (!PyArg_ParseTuple(args, "s#s#:bdiff", &sa, &la, &sb, &lb)) @@ -378,9 +381,18 @@ for (h = l.next; h; h = h->next) { if (h->a1 != la || h->b1 != lb) { len = bl[h->b1].l - bl[lb].l; - putbe32(al[la].l - al->l, rb); - putbe32(al[h->a1].l - al->l, rb + 4); - putbe32(len, rb + 8); + +#define checkputbe32(__x, __c) \ + if (__x > UINT_MAX) { \ + PyErr_SetString(PyExc_ValueError, \ + "bdiff: value too large for putbe32"); \ + goto nomem; \ + } \ + putbe32((uint32_t)(__x), __c); + + checkputbe32(al[la].l - al->l, rb); + checkputbe32(al[h->a1].l - al->l, rb + 4); + checkputbe32(len, rb + 8); memcpy(rb + 12, bl[lb].l, len); rb += 12 + len; } @@ -407,7 +419,7 @@ PyObject *s, *result = NULL; char allws, c; const char *r; - int i, rlen, wlen = 0; + Py_ssize_t i, rlen, wlen = 0; char *w; if (!PyArg_ParseTuple(args, "Sb:fixws", &s, &allws)) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/bookmarks.py --- a/mercurial/bookmarks.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/bookmarks.py Wed Jul 18 19:08:25 2012 -0500 @@ -7,7 +7,7 @@ from mercurial.i18n import _ from mercurial.node import hex -from mercurial import encoding, util +from mercurial import encoding, error, util import errno, os def valid(mark): @@ -140,8 +140,8 @@ def updatecurrentbookmark(repo, oldnode, curbranch): try: - return update(repo, oldnode, repo.branchtags()[curbranch]) - except KeyError: + return update(repo, oldnode, repo.branchtip(curbranch)) + except error.RepoLookupError: if curbranch == "default": # no default branch! return update(repo, oldnode, repo.lookup("tip")) else: @@ -150,13 +150,20 @@ def update(repo, parents, node): marks = repo._bookmarks update = False - mark = repo._bookmarkcurrent - if mark and marks[mark] in parents: - old = repo[marks[mark]] - new = repo[node] - if new in old.descendants(): - marks[mark] = new.node() - update = True + cur = repo._bookmarkcurrent + if not cur: + return False + + toupdate = [b for b in marks if b.split('@', 1)[0] == cur.split('@', 1)[0]] + for mark in toupdate: + if mark and marks[mark] in parents: + old = repo[marks[mark]] + new = repo[node] + if new in old.descendants() and mark == cur: + marks[cur] = new.node() + update = True + if mark != cur: + del marks[mark] if update: repo._writebookmarks(marks) return update @@ -221,6 +228,11 @@ repo._bookmarks[n] = cr.node() changed = True ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n)) + elif rb[k] in repo: + # add remote bookmarks for changes we already have + repo._bookmarks[k] = repo[rb[k]].node() + changed = True + ui.status(_("adding remote bookmark %s\n") % k) if changed: write(repo) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/bundlerepo.py Wed Jul 18 19:08:25 2012 -0500 @@ -54,7 +54,7 @@ continue for p in (p1, p2): - if not p in self.nodemap: + if p not in self.nodemap: raise error.LookupError(p, self.indexfile, _("unknown parent")) # start, size, full unc. size, base (unused), link, p1, p2, node @@ -167,6 +167,10 @@ def _file(self, f): self._repo.file(f) +class bundlepeer(localrepo.localpeer): + def canpush(self): + return False + class bundlerepository(localrepo.localrepository): def __init__(self, ui, path, bundlename): self._tempparent = None @@ -272,6 +276,9 @@ def cancopy(self): return False + def peer(self): + return bundlepeer(self) + def getcwd(self): return os.getcwd() # always outside the repo @@ -323,13 +330,16 @@ Returns a tuple (local, csets, cleanupfn): - "local" is a local repo from which to obtain the actual incoming changesets; it - is a bundlerepo for the obtained bundle when the original "other" is remote. + "local" is a local repo from which to obtain the actual incoming + changesets; it is a bundlerepo for the obtained bundle when the + original "other" is remote. "csets" lists the incoming changeset node ids. - "cleanupfn" must be called without arguments when you're done processing the - changes; it closes both the original "other" and the one returned here. + "cleanupfn" must be called without arguments when you're done processing + the changes; it closes both the original "other" and the one returned + here. ''' - tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force) + tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, + force=force) common, incoming, rheads = tmp if not incoming: try: @@ -341,8 +351,8 @@ bundle = None bundlerepo = None - localrepo = other - if bundlename or not other.local(): + localrepo = other.local() + if bundlename or not localrepo: # create a bundle (uncompressed if other repo is not local) if other.capable('getbundle'): @@ -353,12 +363,12 @@ rheads = None else: cg = other.changegroupsubset(incoming, rheads, 'incoming') - bundletype = other.local() and "HG10BZ" or "HG10UN" + bundletype = localrepo and "HG10BZ" or "HG10UN" fname = bundle = changegroup.writebundle(cg, bundlename, bundletype) # keep written bundle? if bundlename: bundle = None - if not other.local(): + if not localrepo: # use the created uncompressed bundlerepo localrepo = bundlerepo = bundlerepository(ui, repo.root, fname) # this repo contains local and other now, so filter out local again diff -r 98823bd0d697 -r a06e2681dd17 mercurial/changelog.py --- a/mercurial/changelog.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/changelog.py Wed Jul 18 19:08:25 2012 -0500 @@ -120,8 +120,6 @@ self._realopener = opener self._delayed = False self._divert = False - # hiddenrevs: revs that should be hidden by command and tools - self.hiddenrevs = set() def delayupdate(self): "delay visibility of index updates to other readers" diff -r 98823bd0d697 -r a06e2681dd17 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/cmdutil.py Wed Jul 18 19:08:25 2012 -0500 @@ -10,7 +10,7 @@ import os, sys, errno, re, tempfile import util, scmutil, templater, patch, error, templatekw, revlog, copies import match as matchmod -import subrepo, context, repair, bookmarks +import subrepo, context, repair, bookmarks, graphmod, revset def parsealiases(cmd): return cmd.lstrip("^").split("|") @@ -952,12 +952,26 @@ for ctx in walkchangerevs(repo, m, {'rev': None}, prep): rev = ctx.rev() if rev in results: - ui.status(_("Found revision %s from %s\n") % + ui.status(_("found revision %s from %s\n") % (rev, util.datestr(results[rev]))) return str(rev) raise util.Abort(_("revision matching date not found")) +def increasingwindows(start, end, windowsize=8, sizelimit=512): + if start < end: + while start < end: + yield start, min(windowsize, end - start) + start += windowsize + if windowsize < sizelimit: + windowsize *= 2 + else: + while start > end: + yield start, min(windowsize, start - end - 1) + start -= windowsize + if windowsize < sizelimit: + windowsize *= 2 + def walkchangerevs(repo, match, opts, prepare): '''Iterate over files and the revs in which they changed. @@ -973,20 +987,6 @@ yielding each context, the iterator will first call the prepare function on each context in the window in forward order.''' - def increasing_windows(start, end, windowsize=8, sizelimit=512): - if start < end: - while start < end: - yield start, min(windowsize, end - start) - start += windowsize - if windowsize < sizelimit: - windowsize *= 2 - else: - while start > end: - yield start, min(windowsize, start - end - 1) - start -= windowsize - if windowsize < sizelimit: - windowsize *= 2 - follow = opts.get('follow') or opts.get('follow_first') if not len(repo): @@ -1176,7 +1176,7 @@ def want(rev): return rev in wanted - for i, window in increasing_windows(0, len(revs)): + for i, window in increasingwindows(0, len(revs)): nrevs = [rev for rev in revs[i:i + window] if want(rev)] for rev in sorted(nrevs): fns = fncache.get(rev) @@ -1192,6 +1192,277 @@ yield change(rev) return iterate() +def _makegraphfilematcher(repo, pats, followfirst): + # When displaying a revision with --patch --follow FILE, we have + # to know which file of the revision must be diffed. With + # --follow, we want the names of the ancestors of FILE in the + # revision, stored in "fcache". "fcache" is populated by + # reproducing the graph traversal already done by --follow revset + # and relating linkrevs to file names (which is not "correct" but + # good enough). + fcache = {} + fcacheready = [False] + pctx = repo['.'] + wctx = repo[None] + + def populate(): + for fn in pats: + for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)): + for c in i: + fcache.setdefault(c.linkrev(), set()).add(c.path()) + + def filematcher(rev): + if not fcacheready[0]: + # Lazy initialization + fcacheready[0] = True + populate() + return scmutil.match(wctx, fcache.get(rev, []), default='path') + + return filematcher + +def _makegraphlogrevset(repo, pats, opts, revs): + """Return (expr, filematcher) where expr is a revset string built + from log options and file patterns or None. If --stat or --patch + are not passed filematcher is None. Otherwise it is a callable + taking a revision number and returning a match objects filtering + the files to be detailed when displaying the revision. + """ + opt2revset = { + 'no_merges': ('not merge()', None), + 'only_merges': ('merge()', None), + '_ancestors': ('ancestors(%(val)s)', None), + '_fancestors': ('_firstancestors(%(val)s)', None), + '_descendants': ('descendants(%(val)s)', None), + '_fdescendants': ('_firstdescendants(%(val)s)', None), + '_matchfiles': ('_matchfiles(%(val)s)', None), + 'date': ('date(%(val)r)', None), + 'branch': ('branch(%(val)r)', ' or '), + '_patslog': ('filelog(%(val)r)', ' or '), + '_patsfollow': ('follow(%(val)r)', ' or '), + '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '), + 'keyword': ('keyword(%(val)r)', ' or '), + 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '), + 'user': ('user(%(val)r)', ' or '), + } + + opts = dict(opts) + # follow or not follow? + follow = opts.get('follow') or opts.get('follow_first') + followfirst = opts.get('follow_first') and 1 or 0 + # --follow with FILE behaviour depends on revs... + startrev = revs[0] + followdescendants = (len(revs) > 1 and revs[0] < revs[1]) and 1 or 0 + + # branch and only_branch are really aliases and must be handled at + # the same time + opts['branch'] = opts.get('branch', []) + opts.get('only_branch', []) + opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']] + # pats/include/exclude are passed to match.match() directly in + # _matchfile() revset but walkchangerevs() builds its matcher with + # scmutil.match(). The difference is input pats are globbed on + # platforms without shell expansion (windows). + pctx = repo[None] + match, pats = scmutil.matchandpats(pctx, pats, opts) + slowpath = match.anypats() or (match.files() and opts.get('removed')) + if not slowpath: + for f in match.files(): + if follow and f not in pctx: + raise util.Abort(_('cannot follow file not in parent ' + 'revision: "%s"') % f) + filelog = repo.file(f) + if not len(filelog): + # A zero count may be a directory or deleted file, so + # try to find matching entries on the slow path. + if follow: + raise util.Abort( + _('cannot follow nonexistent file: "%s"') % f) + slowpath = True + if slowpath: + # See walkchangerevs() slow path. + # + if follow: + raise util.Abort(_('can only follow copies/renames for explicit ' + 'filenames')) + # pats/include/exclude cannot be represented as separate + # revset expressions as their filtering logic applies at file + # level. For instance "-I a -X a" matches a revision touching + # "a" and "b" while "file(a) and not file(b)" does + # not. Besides, filesets are evaluated against the working + # directory. + matchargs = ['r:', 'd:relpath'] + for p in pats: + matchargs.append('p:' + p) + for p in opts.get('include', []): + matchargs.append('i:' + p) + for p in opts.get('exclude', []): + matchargs.append('x:' + p) + matchargs = ','.join(('%r' % p) for p in matchargs) + opts['_matchfiles'] = matchargs + else: + if follow: + fpats = ('_patsfollow', '_patsfollowfirst') + fnopats = (('_ancestors', '_fancestors'), + ('_descendants', '_fdescendants')) + if pats: + # follow() revset inteprets its file argument as a + # manifest entry, so use match.files(), not pats. + opts[fpats[followfirst]] = list(match.files()) + else: + opts[fnopats[followdescendants][followfirst]] = str(startrev) + else: + opts['_patslog'] = list(pats) + + filematcher = None + if opts.get('patch') or opts.get('stat'): + if follow: + filematcher = _makegraphfilematcher(repo, pats, followfirst) + else: + filematcher = lambda rev: match + + expr = [] + for op, val in opts.iteritems(): + if not val: + continue + if op not in opt2revset: + continue + revop, andor = opt2revset[op] + if '%(val)' not in revop: + expr.append(revop) + else: + if not isinstance(val, list): + e = revop % {'val': val} + else: + e = '(' + andor.join((revop % {'val': v}) for v in val) + ')' + expr.append(e) + + if expr: + expr = '(' + ' and '.join(expr) + ')' + else: + expr = None + return expr, filematcher + +def getgraphlogrevs(repo, pats, opts): + """Return (revs, expr, filematcher) where revs is an iterable of + revision numbers, expr is a revset string built from log options + and file patterns or None, and used to filter 'revs'. If --stat or + --patch are not passed filematcher is None. Otherwise it is a + callable taking a revision number and returning a match objects + filtering the files to be detailed when displaying the revision. + """ + def increasingrevs(repo, revs, matcher): + # The sorted input rev sequence is chopped in sub-sequences + # which are sorted in ascending order and passed to the + # matcher. The filtered revs are sorted again as they were in + # the original sub-sequence. This achieve several things: + # + # - getlogrevs() now returns a generator which behaviour is + # adapted to log need. First results come fast, last ones + # are batched for performances. + # + # - revset matchers often operate faster on revision in + # changelog order, because most filters deal with the + # changelog. + # + # - revset matchers can reorder revisions. "A or B" typically + # returns returns the revision matching A then the revision + # matching B. We want to hide this internal implementation + # detail from the caller, and sorting the filtered revision + # again achieves this. + for i, window in increasingwindows(0, len(revs), windowsize=1): + orevs = revs[i:i + window] + nrevs = set(matcher(repo, sorted(orevs))) + for rev in orevs: + if rev in nrevs: + yield rev + + if not len(repo): + return iter([]), None, None + # Default --rev value depends on --follow but --follow behaviour + # depends on revisions resolved from --rev... + follow = opts.get('follow') or opts.get('follow_first') + if opts.get('rev'): + revs = scmutil.revrange(repo, opts['rev']) + else: + if follow and len(repo) > 0: + revs = scmutil.revrange(repo, ['.:0']) + else: + revs = range(len(repo) - 1, -1, -1) + if not revs: + return iter([]), None, None + expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs) + if expr: + matcher = revset.match(repo.ui, expr) + revs = increasingrevs(repo, revs, matcher) + if not opts.get('hidden'): + # --hidden is still experimental and not worth a dedicated revset + # yet. Fortunately, filtering revision number is fast. + revs = (r for r in revs if r not in repo.hiddenrevs) + else: + revs = iter(revs) + return revs, expr, filematcher + +def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None, + filematcher=None): + seen, state = [], graphmod.asciistate() + for rev, type, ctx, parents in dag: + char = 'o' + if ctx.node() in showparents: + char = '@' + elif ctx.obsolete(): + char = 'x' + copies = None + if getrenamed and ctx.rev(): + copies = [] + for fn in ctx.files(): + rename = getrenamed(fn, ctx.rev()) + if rename: + copies.append((fn, rename[0])) + revmatchfn = None + if filematcher is not None: + revmatchfn = filematcher(ctx.rev()) + displayer.show(ctx, copies=copies, matchfn=revmatchfn) + lines = displayer.hunk.pop(rev).split('\n') + if not lines[-1]: + del lines[-1] + displayer.flush(rev) + edges = edgefn(type, char, lines, seen, rev, parents) + for type, char, lines, coldata in edges: + graphmod.ascii(ui, state, type, char, lines, coldata) + displayer.close() + +def graphlog(ui, repo, *pats, **opts): + # Parameters are identical to log command ones + revs, expr, filematcher = getgraphlogrevs(repo, pats, opts) + revs = sorted(revs, reverse=1) + limit = loglimit(opts) + if limit is not None: + revs = revs[:limit] + revdag = graphmod.dagwalker(repo, revs) + + getrenamed = None + if opts.get('copies'): + endrev = None + if opts.get('rev'): + endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1 + getrenamed = templatekw.getrenamedfn(repo, endrev=endrev) + displayer = show_changeset(ui, repo, opts, buffered=True) + showparents = [ctx.node() for ctx in repo[None].parents()] + displaygraph(ui, revdag, displayer, showparents, + graphmod.asciiedges, getrenamed, filematcher) + +def checkunsupportedgraphflags(pats, opts): + for op in ["newest_first"]: + if op in opts and opts[op]: + raise util.Abort(_("-G/--graph option is incompatible with --%s") + % op.replace("_", "-")) + +def graphrevs(repo, nodes, opts): + limit = loglimit(opts) + nodes.reverse() + if limit is not None: + nodes = nodes[:limit] + return graphmod.nodes(repo, nodes) + def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly): join = lambda f: os.path.join(prefix, f) bad = [] @@ -1202,7 +1473,7 @@ cca = None abort, warn = scmutil.checkportabilityalert(ui) if abort or warn: - cca = scmutil.casecollisionauditor(ui, abort, wctx) + cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate) for f in repo.walk(match): exact = match.exact(f) if exact or not explicitonly and f not in repo.dirstate: @@ -1349,7 +1620,7 @@ a = ctx.filectx(f) if f in base.manifest(): b = base.filectx(f) - return (a.data() == b.data() + return (not a.cmp(b) and a.flags() == b.flags()) else: return False @@ -1367,7 +1638,7 @@ copied=copied.get(path)) return mctx except KeyError: - raise IOError() + raise IOError else: ui.note(_('copying changeset %s to %s\n') % (old, base)) @@ -1376,7 +1647,7 @@ try: return old.filectx(path) except KeyError: - raise IOError() + raise IOError # See if we got a message from -m or -l, if not, open the editor # with the message of the changeset to amend @@ -1493,7 +1764,7 @@ def badfn(path, msg): if path in names: return - if path in repo[node].substate: + if path in ctx.substate: return path_ = path + '/' for f in names: @@ -1501,14 +1772,14 @@ return ui.warn("%s: %s\n" % (m.rel(path), msg)) - m = scmutil.match(repo[node], pats, opts) + m = scmutil.match(ctx, pats, opts) m.bad = badfn - for abs in repo[node].walk(m): + for abs in ctx.walk(m): if abs not in names: names[abs] = m.rel(abs), m.exact(abs) # get the list of subrepos that must be reverted - targetsubs = [s for s in repo[node].substate if m(s)] + targetsubs = [s for s in ctx.substate if m(s)] m = scmutil.matchfiles(repo, names) changes = repo.status(match=m)[:4] modified, added, removed, deleted = map(set, changes) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/commands.py --- a/mercurial/commands.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/commands.py Wed Jul 18 19:08:25 2012 -0500 @@ -15,9 +15,9 @@ import sshserver, hgweb, hgweb.server, commandserver import merge as mergemod import minirst, revset, fileset -import dagparser, context, simplemerge -import random, setdiscovery, treediscovery, dagutil, pvec -import phases +import dagparser, context, simplemerge, graphmod +import random, setdiscovery, treediscovery, dagutil, pvec, localrepo +import phases, obsolete table = {} @@ -98,6 +98,7 @@ _('limit number of changes displayed'), _('NUM')), ('M', 'no-merges', None, _('do not show merges')), ('', 'stat', None, _('output diffstat-style summary of changes')), + ('G', 'graph', None, _("show the revision DAG")), ] + templateopts diffopts = [ @@ -520,10 +521,12 @@ revision as good or bad without checking it out first. If you supply a command, it will be used for automatic bisection. - Its exit status will be used to mark revisions as good or bad: - status 0 means good, 125 means to skip the revision, 127 - (command not found) will abort the bisection, and any other - non-zero exit status means the revision is bad. + The environment variable HG_NODE will contain the ID of the + changeset being tested. The exit status of the command will be + used to mark revisions as good or bad: status 0 means good, 125 + means to skip the revision, 127 (command not found) will abort the + bisection, and any other non-zero exit status means the revision + is bad. .. container:: verbose @@ -563,6 +566,11 @@ hg log -r "bisect(pruned)" + - see the changeset currently being bisected (especially useful + if running with -U/--noupdate):: + + hg log -r "bisect(current)" + - see all changesets that took part in the current bisection:: hg log -r "bisect(range)" @@ -647,10 +655,22 @@ if command: changesets = 1 try: + node = state['current'][0] + except LookupError: + if noupdate: + raise util.Abort(_('current bisect revision is unknown - ' + 'start a new bisect to fix')) + node, p2 = repo.dirstate.parents() + if p2 != nullid: + raise util.Abort(_('current bisect revision is a merge')) + try: while changesets: # update state + state['current'] = [node] hbisect.save_state(repo, state) - status = util.system(command, out=ui.fout) + status = util.system(command, + environ={'HG_NODE': hex(node)}, + out=ui.fout) if status == 125: transition = "skip" elif status == 0: @@ -662,17 +682,20 @@ raise util.Abort(_("%s killed") % command) else: transition = "bad" - ctx = scmutil.revsingle(repo, rev) + ctx = scmutil.revsingle(repo, rev, node) rev = None # clear for future iterations state[transition].append(ctx.node()) - ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition)) + ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition)) check_state(state, interactive=False) # bisect nodes, changesets, good = hbisect.bisect(repo.changelog, state) # update to next check - cmdutil.bailifchanged(repo) - hg.clean(repo, nodes[0], show_stats=False) + node = nodes[0] + if not noupdate: + cmdutil.bailifchanged(repo) + hg.clean(repo, node, show_stats=False) finally: + state['current'] = [node] hbisect.save_state(repo, state) print_result(nodes, good) return @@ -704,6 +727,8 @@ if extendnode is not None: ui.write(_("Extending search to changeset %d:%s\n" % (extendnode.rev(), extendnode))) + state['current'] = [extendnode.node()] + hbisect.save_state(repo, state) if noupdate: return cmdutil.bailifchanged(repo) @@ -723,6 +748,8 @@ ui.write(_("Testing changeset %d:%s " "(%d changesets remaining, ~%d tests)\n") % (rev, short(node), changesets, tests)) + state['current'] = [node] + hbisect.save_state(repo, state) if not noupdate: cmdutil.bailifchanged(repo) return hg.clean(repo, node) @@ -801,7 +828,7 @@ if mark in marks and not force: raise util.Abort(_("bookmark '%s' already exists " "(use -f to force)") % mark) - if ((mark in repo.branchtags() or mark == repo.dirstate.branch()) + if ((mark in repo.branchmap() or mark == repo.dirstate.branch()) and not force): raise util.Abort( _("a bookmark cannot have the name of an existing branch")) @@ -877,7 +904,7 @@ repo.dirstate.setbranch(label) ui.status(_('reset working directory to branch %s\n') % label) elif label: - if not opts.get('force') and label in repo.branchtags(): + if not opts.get('force') and label in repo.branchmap(): if label not in [p.branch() for p in repo.parents()]: raise util.Abort(_('a branch of the same name already' ' exists'), @@ -910,37 +937,45 @@ """ hexfunc = ui.debugflag and hex or short - activebranches = [repo[n].branch() for n in repo.heads()] - def testactive(tag, node): - realhead = tag in activebranches - open = node in repo.branchheads(tag, closed=False) - return realhead and open - branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag) - for tag, node in repo.branchtags().items()], - reverse=True) - - for isactive, node, tag in branches: + + activebranches = set([repo[n].branch() for n in repo.heads()]) + branches = [] + for tag, heads in repo.branchmap().iteritems(): + for h in reversed(heads): + ctx = repo[h] + isopen = not ctx.closesbranch() + if isopen: + tip = ctx + break + else: + tip = repo[heads[-1]] + isactive = tag in activebranches and isopen + branches.append((tip, isactive, isopen)) + branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]), + reverse=True) + + for ctx, isactive, isopen in branches: if (not active) or isactive: + if isactive: + label = 'branches.active' + notice = '' + elif not isopen: + if not closed: + continue + label = 'branches.closed' + notice = _(' (closed)') + else: + label = 'branches.inactive' + notice = _(' (inactive)') + if ctx.branch() == repo.dirstate.branch(): + label = 'branches.current' + rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch())) + rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())), + 'log.changeset') + tag = ui.label(ctx.branch(), label) if ui.quiet: ui.write("%s\n" % tag) else: - hn = repo.lookup(node) - if isactive: - label = 'branches.active' - notice = '' - elif hn not in repo.branchheads(tag, closed=False): - if not closed: - continue - label = 'branches.closed' - notice = _(' (closed)') - else: - label = 'branches.inactive' - notice = _(' (inactive)') - if tag == repo.dirstate.branch(): - label = 'branches.current' - rev = str(node).rjust(31 - encoding.colwidth(tag)) - rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset') - tag = ui.label(tag, label) ui.write("%s %s%s\n" % (tag, rev, notice)) @command('bundle', @@ -1324,7 +1359,7 @@ if not opts.get('close_branch'): for r in parents: - if r.extra().get('close') and r.branch() == branch: + if r.closesbranch() and r.branch() == branch: ui.status(_('reopening closed branch head %d\n') % r) if ui.debugflag: @@ -1662,7 +1697,8 @@ revs = set((int(r) for r in revs)) def events(): for r in rlog: - yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1))) + yield 'n', (r, list(set(p for p in rlog.parentrevs(r) + if p != -1))) if r in revs: yield 'l', (r, "r%i" % r) elif repo: @@ -1681,7 +1717,8 @@ if newb != b: yield 'a', newb b = newb - yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1))) + yield 'n', (r, list(set(p for p in cl.parentrevs(r) + if p != -1))) if tags: ls = labels.get(r) if ls: @@ -1739,17 +1776,22 @@ _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]')) def debugdiscovery(ui, repo, remoteurl="default", **opts): """runs the changeset discovery protocol in isolation""" - remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch')) + remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), + opts.get('branch')) remote = hg.peer(repo, opts, remoteurl) ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl)) # make sure tests are repeatable random.seed(12323) - def doit(localheads, remoteheads): + def doit(localheads, remoteheads, remote=remote): if opts.get('old'): if localheads: - raise util.Abort('cannot use localheads with old style discovery') + raise util.Abort('cannot use localheads with old style ' + 'discovery') + if not util.safehasattr(remote, 'branches'): + # enable in-client legacy support + remote = localrepo.locallegacypeer(remote.local()) common, _in, hds = treediscovery.findcommonincoming(repo, remote, force=True) common = set(common) @@ -1876,7 +1918,8 @@ " nodeid p1 p2\n") elif format == 1: ui.write(" rev flag offset length" - " size " + basehdr + " link p1 p2 nodeid\n") + " size " + basehdr + " link p1 p2" + " nodeid\n") for i in r: node = r.node(i) @@ -1887,7 +1930,7 @@ if format == 0: try: pp = r.parents(node) - except: + except Exception: pp = [nullid, nullid] ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % ( i, r.start(i), r.length(i), base, r.linkrev(i), @@ -1934,7 +1977,7 @@ problems = 0 # encoding - ui.status(_("Checking encoding (%s)...\n") % encoding.encoding) + ui.status(_("checking encoding (%s)...\n") % encoding.encoding) try: encoding.fromlocal("test") except util.Abort, inst: @@ -1943,7 +1986,7 @@ problems += 1 # compiled modules - ui.status(_("Checking installed modules (%s)...\n") + ui.status(_("checking installed modules (%s)...\n") % os.path.dirname(__file__)) try: import bdiff, mpatch, base85, osutil @@ -1957,7 +2000,7 @@ # templates import templater p = templater.templatepath() - ui.status(_("Checking templates (%s)...\n") % ' '.join(p)) + ui.status(_("checking templates (%s)...\n") % ' '.join(p)) try: templater.templater(templater.templatepath("map-cmdline.default")) except Exception, inst: @@ -1966,7 +2009,7 @@ problems += 1 # editor - ui.status(_("Checking commit editor...\n")) + ui.status(_("checking commit editor...\n")) editor = ui.geteditor() cmdpath = util.findexe(editor) or util.findexe(editor.split()[0]) if not cmdpath: @@ -1981,7 +2024,7 @@ problems += 1 # check username - ui.status(_("Checking username...\n")) + ui.status(_("checking username...\n")) try: ui.username() except util.Abort, e: @@ -1990,7 +2033,7 @@ problems += 1 if not problems: - ui.status(_("No problems detected\n")) + ui.status(_("no problems detected\n")) else: ui.write(_("%s problems detected," " please check your install!\n") % problems) @@ -2001,8 +2044,8 @@ def debugknown(ui, repopath, *ids, **opts): """test whether node ids are known to a repo - Every ID must be a full-length hex node id string. Returns a list of 0s and 1s - indicating unknown/known. + Every ID must be a full-length hex node id string. Returns a list of 0s + and 1s indicating unknown/known. """ repo = hg.peer(ui, opts, repopath) if not repo.capable('known'): @@ -2010,6 +2053,36 @@ flags = repo.known([bin(s) for s in ids]) ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags]))) +@command('debugobsolete', [] + commitopts2, + _('[OBSOLETED [REPLACEMENT] [REPL... ]')) +def debugobsolete(ui, repo, precursor=None, *successors, **opts): + """create arbitrary obsolete marker""" + if precursor is not None: + metadata = {} + if 'date' in opts: + metadata['date'] = opts['date'] + metadata['user'] = opts['user'] or ui.username() + succs = tuple(bin(succ) for succ in successors) + l = repo.lock() + try: + tr = repo.transaction('debugobsolete') + try: + repo.obsstore.create(tr, bin(precursor), succs, 0, metadata) + tr.close() + finally: + tr.release() + finally: + l.release() + else: + for m in obsolete.allmarkers(repo): + ui.write(hex(m.precnode())) + for repl in m.succnodes(): + ui.write(' ') + ui.write(hex(repl)) + ui.write(' %X ' % m._data[2]) + ui.write(m.metadata()) + ui.write('\n') + @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]')) def debugpushkey(ui, repopath, namespace, *keyinfo, **opts): '''access the pushkey key/value protocol @@ -2175,6 +2248,11 @@ elif delta != nullrev: numother += 1 + # Adjust size min value for empty cases + for size in (datasize, fullsize, deltasize): + if size[0] is None: + size[0] = 0 + numdeltas = numrevs - numfull numoprev = numprev - nump1prev - nump2prev totalrawsize = datasize[2] @@ -2182,7 +2260,8 @@ fulltotal = fullsize[2] fullsize[2] /= numfull deltatotal = deltasize[2] - deltasize[2] /= numrevs - numfull + if numrevs - numfull > 0: + deltasize[2] /= numrevs - numfull totalsize = fulltotal + deltatotal avgchainlen = sum(chainlengths) / numrevs compratio = totalrawsize / totalsize @@ -2234,13 +2313,17 @@ fmt2 = pcfmtstr(numdeltas, 4) ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)) if numprev > 0: - ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev)) - ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev)) - ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev)) + ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, + numprev)) + ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, + numprev)) + ui.write(' other : ' + fmt2 % pcfmt(numoprev, + numprev)) if gdelta: ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)) ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)) - ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas)) + ui.write('deltas against other : ' + fmt % pcfmt(numother, + numdeltas)) @command('debugrevspec', [], ('REVSPEC')) def debugrevspec(ui, repo, expr): @@ -2328,11 +2411,14 @@ items = list(repo.walk(m)) if not items: return + f = lambda fn: fn + if ui.configbool('ui', 'slash') and os.sep != '/': + f = lambda fn: util.normpath(fn) fmt = 'f %%-%ds %%-%ds %%s' % ( max([len(abs) for abs in items]), max([len(m.rel(abs)) for abs in items])) for abs in items: - line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '') + line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '') ui.write("%s\n" % line.rstrip()) @command('debugwireargs', @@ -2446,7 +2532,7 @@ ('', 'switch-parent', None, _('diff against the second parent')), ('r', 'rev', [], _('revisions to export'), _('REV')), ] + diffopts, - _('[OPTION]... [-o OUTFILESPEC] REV...')) + _('[OPTION]... [-o OUTFILESPEC] [-r] REV...')) def export(ui, repo, *changesets, **opts): """dump the header and diffs for one or more changesets @@ -2554,14 +2640,16 @@ @command( 'graft', - [('c', 'continue', False, _('resume interrupted graft')), + [('r', 'rev', [], _('revisions to graft'), _('REV')), + ('c', 'continue', False, _('resume interrupted graft')), ('e', 'edit', False, _('invoke editor on commit messages')), + ('', 'log', None, _('append graft info to log message')), ('D', 'currentdate', False, _('record the current date as commit date')), ('U', 'currentuser', False, _('record the current user as committer'), _('DATE'))] + commitopts2 + mergetoolopts + dryrunopts, - _('[OPTION]... REVISION...')) + _('[OPTION]... [-r] REV...')) def graft(ui, repo, *revs, **opts): '''copy changes from other branches onto the current branch @@ -2574,6 +2662,11 @@ Changesets that are ancestors of the current revision, that have already been grafted, or that are merges will be skipped. + If --log is specified, log messages will have a comment appended + of the form:: + + (grafted from CHANGESETHASH) + If a graft merge results in conflicts, the graft process is interrupted so that the current merge can be manually resolved. Once all conflicts are addressed, the graft process can be @@ -2606,6 +2699,9 @@ Returns 0 on successful completion. ''' + revs = list(revs) + revs.extend(opts['rev']) + if not opts.get('user') and opts.get('currentuser'): opts['user'] = ui.username() if not opts.get('date') and opts.get('currentdate'): @@ -2724,8 +2820,13 @@ date = ctx.date() if opts.get('date'): date = opts['date'] - repo.commit(text=ctx.description(), user=user, + message = ctx.description() + if opts.get('log'): + message += '\n(grafted from %s)' % ctx.hex() + node = repo.commit(text=message, user=user, date=date, extra=extra, editor=editor) + if node is None: + ui.status(_('graft for revision %s is empty\n') % ctx.rev()) finally: wlock.release() @@ -3019,7 +3120,9 @@ @command('help', [('e', 'extension', None, _('show only help for extensions')), - ('c', 'command', None, _('show only help for commands'))], + ('c', 'command', None, _('show only help for commands')), + ('k', 'keyword', '', _('show topics matching keyword')), + ], _('[-ec] [TOPIC]')) def help_(ui, name=None, unknowncmd=False, full=True, **opts): """show help for a given topic or a help overview @@ -3034,78 +3137,6 @@ textwidth = min(ui.termwidth(), 80) - 2 - def optrst(options): - data = [] - multioccur = False - for option in options: - if len(option) == 5: - shortopt, longopt, default, desc, optlabel = option - else: - shortopt, longopt, default, desc = option - optlabel = _("VALUE") # default label - - if _("DEPRECATED") in desc and not ui.verbose: - continue - - so = '' - if shortopt: - so = '-' + shortopt - lo = '--' + longopt - if default: - desc += _(" (default: %s)") % default - - if isinstance(default, list): - lo += " %s [+]" % optlabel - multioccur = True - elif (default is not None) and not isinstance(default, bool): - lo += " %s" % optlabel - - data.append((so, lo, desc)) - - rst = minirst.maketable(data, 1) - - if multioccur: - rst += _("\n[+] marked option can be specified multiple times\n") - - return rst - - # list all option lists - def opttext(optlist, width): - rst = '' - if not optlist: - return '' - - for title, options in optlist: - rst += '\n%s\n' % title - if options: - rst += "\n" - rst += optrst(options) - rst += '\n' - - return '\n' + minirst.format(rst, width) - - def addglobalopts(optlist, aliases): - if ui.quiet: - return [] - - if ui.verbose: - optlist.append((_("global options:"), globalopts)) - if name == 'shortlist': - optlist.append((_('use "hg help" for the full list ' - 'of commands'), ())) - else: - if name == 'shortlist': - msg = _('use "hg help" for the full list of commands ' - 'or "hg -v" for details') - elif name and not full: - msg = _('use "hg help %s" to show the full help text') % name - elif aliases: - msg = _('use "hg -v help%s" to show builtin aliases and ' - 'global options') % (name and " " + name or "") - else: - msg = _('use "hg -v help %s" to show more info') % name - optlist.append((msg, ())) - def helpcmd(name): try: aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd) @@ -3114,29 +3145,31 @@ # except block, nor can be used inside a lambda. python issue4617 prefix = inst.args[0] select = lambda c: c.lstrip('^').startswith(prefix) - helplist(select) - return + rst = helplist(select) + return rst + + rst = [] # check if it's an invalid alias and display its error if it is if getattr(entry[0], 'badalias', False): if not unknowncmd: + ui.pushbuffer() entry[0](ui) - return - - rst = "" + rst.append(ui.popbuffer()) + return rst # synopsis if len(entry) > 2: if entry[2].startswith('hg'): - rst += "%s\n" % entry[2] + rst.append("%s\n" % entry[2]) else: - rst += 'hg %s %s\n' % (aliases[0], entry[2]) + rst.append('hg %s %s\n' % (aliases[0], entry[2])) else: - rst += 'hg %s\n' % aliases[0] - + rst.append('hg %s\n' % aliases[0]) # aliases if full and not ui.quiet and len(aliases) > 1: - rst += _("\naliases: %s\n") % ', '.join(aliases[1:]) + rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:])) + rst.append('\n') # description doc = gettext(entry[0].__doc__) @@ -3147,9 +3180,12 @@ doc = _('shell alias for::\n\n %s') % entry[0].definition[1:] else: doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc) + doc = doc.splitlines(True) if ui.quiet or not full: - doc = doc.splitlines()[0] - rst += "\n" + doc + "\n" + rst.append(doc[0]) + else: + rst.extend(doc) + rst.append('\n') # check if this command shadows a non-trivial (multi-line) # extension help text @@ -3159,33 +3195,27 @@ if '\n' in doc.strip(): msg = _('use "hg help -e %s" to show help for ' 'the %s extension') % (name, name) - rst += '\n%s\n' % msg + rst.append('\n%s\n' % msg) except KeyError: pass # options if not ui.quiet and entry[1]: - rst += '\n' - rst += _("options:") - rst += '\n\n' - rst += optrst(entry[1]) + rst.append('\n%s\n\n' % _("options:")) + rst.append(help.optrst(entry[1], ui.verbose)) if ui.verbose: - rst += '\n' - rst += _("global options:") - rst += '\n\n' - rst += optrst(globalopts) - - keep = ui.verbose and ['verbose'] or [] - formatted, pruned = minirst.format(rst, textwidth, keep=keep) - ui.write(formatted) + rst.append('\n%s\n\n' % _("global options:")) + rst.append(help.optrst(globalopts, ui.verbose)) if not ui.verbose: if not full: - ui.write(_('\nuse "hg help %s" to show the full help text\n') + rst.append(_('\nuse "hg help %s" to show the full help text\n') % name) elif not ui.quiet: - ui.write(_('\nuse "hg -v help %s" to show more info\n') % name) + rst.append(_('\nuse "hg -v help %s" to show more info\n') + % name) + return rst def helplist(select=None): @@ -3218,38 +3248,60 @@ h[f] = doc.splitlines()[0].rstrip() cmds[f] = c.lstrip("^") + rst = [] if not h: - ui.status(_('no commands defined\n')) - return - - ui.status(header) + if not ui.quiet: + rst.append(_('no commands defined\n')) + return rst + + if not ui.quiet: + rst.append(header) fns = sorted(h) - m = max(map(len, fns)) for f in fns: if ui.verbose: commands = cmds[f].replace("|",", ") - ui.write(" %s:\n %s\n"%(commands, h[f])) + rst.append(" :%s: %s\n" % (commands, h[f])) else: - ui.write('%s\n' % (util.wrap(h[f], textwidth, - initindent=' %-*s ' % (m, f), - hangindent=' ' * (m + 4)))) + rst.append(' :%s: %s\n' % (f, h[f])) if not name: - text = help.listexts(_('enabled extensions:'), extensions.enabled()) - if text: - ui.write("\n%s" % minirst.format(text, textwidth)) - - ui.write(_("\nadditional help topics:\n\n")) + exts = help.listexts(_('enabled extensions:'), extensions.enabled()) + if exts: + rst.append('\n') + rst.extend(exts) + + rst.append(_("\nadditional help topics:\n\n")) topics = [] for names, header, doc in help.helptable: 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)) + rst.append(" :%s: %s\n" % (t, desc)) optlist = [] - addglobalopts(optlist, True) - ui.write(opttext(optlist, textwidth)) + if not ui.quiet: + if ui.verbose: + optlist.append((_("global options:"), globalopts)) + if name == 'shortlist': + optlist.append((_('use "hg help" for the full list ' + 'of commands'), ())) + else: + if name == 'shortlist': + msg = _('use "hg help" for the full list of commands ' + 'or "hg -v" for details') + elif name and not full: + msg = _('use "hg help %s" to show the full help ' + 'text') % name + else: + msg = _('use "hg -v help%s" to show builtin aliases and ' + 'global options') % (name and " " + name or "") + optlist.append((msg, ())) + + if optlist: + for title, options in optlist: + rst.append('\n%s\n' % title) + if options: + rst.append('\n%s\n' % help.optrst(options, ui.verbose)) + return rst def helptopic(name): for names, header, doc in help.helptable: @@ -3258,20 +3310,20 @@ else: raise error.UnknownCommand(name) + rst = ["%s\n\n" % header] # description if not doc: - doc = _("(no help text available)") + rst.append(" %s\n" % _("(no help text available)")) if util.safehasattr(doc, '__call__'): - doc = doc() - - ui.write("%s\n\n" % header) - ui.write("%s" % minirst.format(doc, textwidth, indent=4)) + rst += [" %s\n" % l for l in doc().splitlines()] + try: cmdutil.findcmd(name, table) - ui.write(_('\nuse "hg help -c %s" to see help for ' + rst.append(_('\nuse "hg help -c %s" to see help for ' 'the %s command\n') % (name, name)) except error.UnknownCommand: pass + return rst def helpext(name): try: @@ -3287,10 +3339,10 @@ head, tail = doc, "" else: head, tail = doc.split('\n', 1) - ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head)) + rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)] if tail: - ui.write(minirst.format(tail, textwidth)) - ui.status('\n') + rst.extend(tail.splitlines(True)) + rst.append('\n') if mod: try: @@ -3298,24 +3350,38 @@ except AttributeError: ct = {} modcmds = set([c.split('|', 1)[0] for c in ct]) - helplist(modcmds.__contains__) + rst.extend(helplist(modcmds.__contains__)) else: - ui.write(_('use "hg help extensions" for information on enabling ' + rst.append(_('use "hg help extensions" for information on enabling ' 'extensions\n')) + return rst def helpextcmd(name): cmd, ext, mod = extensions.disabledcmd(ui, name, ui.configbool('ui', 'strict')) doc = gettext(mod.__doc__).splitlines()[0] - msg = help.listexts(_("'%s' is provided by the following " + rst = help.listexts(_("'%s' is provided by the following " "extension:") % cmd, {ext: doc}, indent=4) - ui.write(minirst.format(msg, textwidth)) - ui.write('\n') - ui.write(_('use "hg help extensions" for information on enabling ' + rst.append('\n') + rst.append(_('use "hg help extensions" for information on enabling ' 'extensions\n')) - - if name and name != 'shortlist': + return rst + + + rst = [] + kw = opts.get('keyword') + if kw: + matches = help.topicmatch(kw) + for t, title in (('topics', _('Topics')), + ('commands', _('Commands')), + ('extensions', _('Extensions')), + ('extensioncommands', _('Extension Commands'))): + if matches[t]: + rst.append('%s:\n\n' % title) + rst.extend(minirst.maketable(sorted(matches[t]), 1)) + rst.append('\n') + elif name and name != 'shortlist': i = None if unknowncmd: queries = (helpextcmd,) @@ -3327,7 +3393,7 @@ queries = (helptopic, helpcmd, helpext, helpextcmd) for f in queries: try: - f(name) + rst = f(name) i = None break except error.UnknownCommand, inst: @@ -3336,9 +3402,13 @@ raise i else: # program name - ui.status(_("Mercurial Distributed SCM\n")) - ui.status('\n') - helplist() + if not ui.quiet: + rst = [_("Mercurial Distributed SCM\n"), '\n'] + rst.extend(helplist()) + + keep = ui.verbose and ['verbose'] or [] + formatted, pruned = minirst.format(''.join(rst), textwidth, keep=keep) + ui.write(formatted) @command('identify|id', @@ -3396,10 +3466,11 @@ if source: source, branches = hg.parseurl(ui.expandpath(source)) - repo = hg.peer(ui, opts, source) - revs, checkout = hg.addbranchrevs(repo, repo, branches, None) - - if not repo.local(): + peer = hg.peer(ui, opts, source) + repo = peer.local() + revs, checkout = hg.addbranchrevs(repo, peer, branches, None) + + if not repo: if num or branch or tags: raise util.Abort( _("can't query remote revision number, branch, or tags")) @@ -3408,16 +3479,16 @@ if not rev: rev = "tip" - remoterev = repo.lookup(rev) + remoterev = peer.lookup(rev) if default or id: output = [hexfunc(remoterev)] def getbms(): bms = [] - if 'bookmarks' in repo.listkeys('namespaces'): + if 'bookmarks' in peer.listkeys('namespaces'): hexremoterev = hex(remoterev) - bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems() + bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems() if bmr == hexremoterev] return bms @@ -3731,7 +3802,7 @@ tr.close() if msgs: repo.savecommitmessage('\n* * *\n'.join(msgs)) - except: + except: # re-raises # wlock.release() indirectly calls dirstate.write(): since # we're crashing, we do not want to change the working dir # parent after all, so make sure it writes nothing @@ -3768,6 +3839,17 @@ Returns 0 if there are incoming changes, 1 otherwise. """ + if opts.get('graph'): + cmdutil.checkunsupportedgraphflags([], opts) + def display(other, chlist, displayer): + revdag = cmdutil.graphrevs(other, chlist, opts) + showparents = [ctx.node() for ctx in repo[None].parents()] + cmdutil.displaygraph(ui, revdag, displayer, showparents, + graphmod.asciiedges) + + hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True) + return 0 + if opts.get('bundle') and opts.get('subrepos'): raise util.Abort(_('cannot combine --bundle and --subrepos')) @@ -3952,6 +4034,8 @@ Returns 0 on success. """ + if opts.get('graph'): + return cmdutil.graphlog(ui, repo, *pats, **opts) matchfn = scmutil.match(repo[None], pats, opts) limit = cmdutil.loglimit(opts) @@ -4124,17 +4208,43 @@ if not node: node = opts.get('rev') - if not node: + if node: + node = scmutil.revsingle(repo, node).node() + + if not node and repo._bookmarkcurrent: + bmheads = repo.bookmarkheads(repo._bookmarkcurrent) + curhead = repo[repo._bookmarkcurrent] + if len(bmheads) == 2: + if curhead == bmheads[0]: + node = bmheads[1] + else: + node = bmheads[0] + elif len(bmheads) > 2: + raise util.Abort(_("multiple matching bookmarks to merge - " + "please merge with an explicit rev or bookmark"), + hint=_("run 'hg heads' to see all heads")) + elif len(bmheads) <= 1: + raise util.Abort(_("no matching bookmark to merge - " + "please merge with an explicit rev or bookmark"), + hint=_("run 'hg heads' to see all heads")) + + if not node and not repo._bookmarkcurrent: branch = repo[None].branch() bheads = repo.branchheads(branch) - if len(bheads) > 2: + nbhs = [bh for bh in bheads if not repo[bh].bookmarks()] + + if len(nbhs) > 2: raise util.Abort(_("branch '%s' has %d heads - " "please merge with an explicit rev") % (branch, len(bheads)), hint=_("run 'hg heads .' to see heads")) parent = repo.dirstate.p1() - if len(bheads) == 1: + if len(nbhs) == 1: + if len(bheads) > 1: + raise util.Abort(_("heads are bookmarked - " + "please merge with an explicit rev"), + hint=_("run 'hg heads' to see all heads")) if len(repo.heads()) > 1: raise util.Abort(_("branch '%s' has one head - " "please merge with an explicit rev") @@ -4149,9 +4259,10 @@ raise util.Abort(_('working directory not at a head revision'), hint=_("use 'hg update' or merge with an " "explicit revision")) - node = parent == bheads[0] and bheads[-1] or bheads[0] - else: - node = scmutil.revsingle(repo, node).node() + if parent == nbhs[0]: + node = nbhs[-1] + else: + node = nbhs[0] if opts.get('preview'): # find nodes that are ancestors of p2 but not of p1 @@ -4193,6 +4304,18 @@ Returns 0 if there are outgoing changes, 1 otherwise. """ + if opts.get('graph'): + cmdutil.checkunsupportedgraphflags([], opts) + o = hg._outgoing(ui, repo, dest, opts) + if o is None: + return + + revdag = cmdutil.graphrevs(repo, o, opts) + displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True) + showparents = [ctx.node() for ctx in repo[None].parents()] + cmdutil.displaygraph(ui, revdag, displayer, showparents, + graphmod.asciiedges) + return 0 if opts.get('bookmarks'): dest = ui.expandpath(dest or 'default-push', dest or 'default') @@ -4349,34 +4472,32 @@ lock = repo.lock() try: # set phase - nodes = [ctx.node() for ctx in repo.set('%ld', revs)] - if not nodes: - raise util.Abort(_('empty revision set')) - olddata = repo._phaserev[:] + if not revs: + raise util.Abort(_('empty revision set')) + nodes = [repo[r].node() for r in revs] + olddata = repo._phasecache.getphaserevs(repo)[:] phases.advanceboundary(repo, targetphase, nodes) if opts['force']: phases.retractboundary(repo, targetphase, nodes) finally: lock.release() - if olddata is not None: - changes = 0 - newdata = repo._phaserev - changes = sum(o != newdata[i] for i, o in enumerate(olddata)) - rejected = [n for n in nodes - if newdata[repo[n].rev()] < targetphase] - if rejected: - ui.warn(_('cannot move %i changesets to a more permissive ' - 'phase, use --force\n') % len(rejected)) - ret = 1 - if changes: - msg = _('phase changed for %i changesets\n') % changes - if ret: - ui.status(msg) - else: - ui.note(msg) + newdata = repo._phasecache.getphaserevs(repo) + changes = sum(o != newdata[i] for i, o in enumerate(olddata)) + rejected = [n for n in nodes + if newdata[repo[n].rev()] < targetphase] + if rejected: + ui.warn(_('cannot move %i changesets to a more permissive ' + 'phase, use --force\n') % len(rejected)) + ret = 1 + if changes: + msg = _('phase changed for %i changesets\n') % changes + if ret: + ui.status(msg) else: - ui.warn(_('no phases changed\n')) - ret = 1 + ui.note(msg) + else: + ui.warn(_('no phases changed\n')) + ret = 1 return ret def postincoming(ui, repo, modheads, optupdate, checkout): @@ -4398,7 +4519,8 @@ if currentbranchheads == modheads: ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n")) elif currentbranchheads > 1: - ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n")) + ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to " + "merge)\n")) else: ui.status(_("(run 'hg heads' to see heads)\n")) else: @@ -4512,6 +4634,10 @@ If -r/--rev is used, the specified revision and all its ancestors will be pushed to the remote repository. + If -B/--bookmark is used, the specified bookmarked revision, its + ancestors, and the bookmark will be pushed to the remote + repository. + Please see :hg:`help urls` for important details about ``ssh://`` URLs. If DESTINATION is omitted, a default path will be used. @@ -4534,7 +4660,7 @@ revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) other = hg.peer(repo, opts, dest) if revs: - revs = [repo.lookup(rev) for rev in revs] + revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)] repo._subtoppath = dest try: @@ -4993,7 +5119,7 @@ def checkrepo(): if repo is None: - raise error.RepoError(_("There is no Mercurial repository here" + raise error.RepoError(_("there is no Mercurial repository here" " (.hg not found)")) if opts["stdio"]: @@ -5024,7 +5150,7 @@ o = opts.get('web_conf') or opts.get('webdir_conf') if not o: if not repo: - raise error.RepoError(_("There is no Mercurial repository" + raise error.RepoError(_("there is no Mercurial repository" " here (.hg not found)")) o = repo.root @@ -5336,7 +5462,7 @@ t += _(' (merge)') elif branch != parents[0].branch(): t += _(' (new branch)') - elif (parents[0].extra().get('close') and + elif (parents[0].closesbranch() and pnode in repo.branchheads(branch, closed=True)): t += _(' (head closed)') elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]): @@ -5355,12 +5481,12 @@ cl = repo.changelog for a in [cl.rev(n) for n in bheads]: new[a] = 1 - for a in cl.ancestors(*[cl.rev(n) for n in bheads]): + for a in cl.ancestors([cl.rev(n) for n in bheads]): new[a] = 1 for a in [p.rev() for p in parents]: if a >= 0: new[a] = 0 - for a in cl.ancestors(*[p.rev() for p in parents]): + for a in cl.ancestors([p.rev() for p in parents]): new[a] = 0 new = sum(new) @@ -5376,7 +5502,8 @@ t = [] source, branches = hg.parseurl(ui.expandpath('default')) other = hg.peer(repo, {}, source) - revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) + revs, checkout = hg.addbranchrevs(repo, other, branches, + opts.get('rev')) ui.debug('comparing with %s\n' % util.hidepassword(source)) repo.ui.pushbuffer() commoninc = discovery.findcommonincoming(repo, other) @@ -5594,9 +5721,9 @@ f = url.open(ui, fname) gen = changegroup.readbundle(f, fname) modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) - bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch()) finally: lock.release() + bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch()) return postincoming(ui, repo, modheads, opts.get('update'), None) @command('^update|up|checkout|co', @@ -5623,23 +5750,25 @@ none are found, the working directory is updated to the specified changeset. - The following rules apply when the working directory contains - uncommitted changes: - - 1. If neither -c/--check nor -C/--clean is specified, and if - the requested changeset is an ancestor or descendant of - the working directory's parent, the uncommitted changes - are merged into the requested changeset and the merged - result is left uncommitted. If the requested changeset is - not an ancestor or descendant (that is, it is on another - branch), the update is aborted and the uncommitted changes - are preserved. - - 2. With the -c/--check option, the update is aborted and the - uncommitted changes are preserved. - - 3. With the -C/--clean option, uncommitted changes are discarded and - the working directory is updated to the requested changeset. + .. container:: verbose + + The following rules apply when the working directory contains + uncommitted changes: + + 1. If neither -c/--check nor -C/--clean is specified, and if + the requested changeset is an ancestor or descendant of + the working directory's parent, the uncommitted changes + are merged into the requested changeset and the merged + result is left uncommitted. If the requested changeset is + not an ancestor or descendant (that is, it is on another + branch), the update is aborted and the uncommitted changes + are preserved. + + 2. With the -c/--check option, the update is aborted and the + uncommitted changes are preserved. + + 3. With the -C/--clean option, uncommitted changes are discarded and + the working directory is updated to the requested changeset. To cancel an uncommitted merge (and lose your changes), use :hg:`update --clean .`. diff -r 98823bd0d697 -r a06e2681dd17 mercurial/commandserver.py --- a/mercurial/commandserver.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/commandserver.py Wed Jul 18 19:08:25 2012 -0500 @@ -142,8 +142,8 @@ else: logfile = open(logpath, 'a') - # the ui here is really the repo ui so take its baseui so we don't end up - # with its local configuration + # the ui here is really the repo ui so take its baseui so we don't end + # up with its local configuration self.ui = repo.baseui self.repo = repo self.repoui = repo.ui @@ -166,7 +166,7 @@ # is the other end closed? if not data: - raise EOFError() + raise EOFError return data diff -r 98823bd0d697 -r a06e2681dd17 mercurial/config.py --- a/mercurial/config.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/config.py Wed Jul 18 19:08:25 2012 -0500 @@ -7,7 +7,7 @@ from i18n import _ import error, util -import re, os, errno +import os, errno class sortdict(dict): 'a simple sorted dictionary' @@ -35,6 +35,10 @@ def __delitem__(self, key): dict.__delitem__(self, key) self._list.remove(key) + def keys(self): + return self._list + def iterkeys(self): + return self._list.__iter__() class config(object): def __init__(self, data=None): @@ -101,13 +105,13 @@ self._source.pop((section, item), None) def parse(self, src, data, sections=None, remap=None, include=None): - sectionre = re.compile(r'\[([^\[]+)\]') - itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)') - contre = re.compile(r'\s+(\S|\S.*\S)\s*$') - emptyre = re.compile(r'(;|#|\s*$)') - commentre = re.compile(r'(;|#)') - unsetre = re.compile(r'%unset\s+(\S+)') - includere = re.compile(r'%include\s+(\S|\S.*\S)\s*$') + sectionre = util.compilere(r'\[([^\[]+)\]') + itemre = util.compilere(r'([^=\s][^=]*?)\s*=\s*(.*\S|)') + contre = util.compilere(r'\s+(\S|\S.*\S)\s*$') + emptyre = util.compilere(r'(;|#|\s*$)') + commentre = util.compilere(r'(;|#)') + unsetre = util.compilere(r'%unset\s+(\S+)') + includere = util.compilere(r'%include\s+(\S|\S.*\S)\s*$') section = "" item = None line = 0 diff -r 98823bd0d697 -r a06e2681dd17 mercurial/context.py --- a/mercurial/context.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/context.py Wed Jul 18 19:08:25 2012 -0500 @@ -8,6 +8,7 @@ from node import nullid, nullrev, short, hex, bin from i18n import _ import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding, phases +import copies import match as matchmod import os, errno, stat @@ -79,10 +80,12 @@ self._node = repo._tagscache.tags[changeid] self._rev = repo.changelog.rev(self._node) return - if changeid in repo.branchtags(): - self._node = repo.branchtags()[changeid] + try: + self._node = repo.branchtip(changeid) self._rev = repo.changelog.rev(self._node) return + except error.RepoLookupError: + pass self._node = repo.changelog._partialmatch(changeid) if self._node is not None: @@ -185,6 +188,8 @@ return self._changeset[4] def branch(self): return encoding.tolocal(self._changeset[5].get("branch")) + def closesbranch(self): + return 'close' in self._changeset[5] def extra(self): return self._changeset[5] def tags(self): @@ -192,18 +197,13 @@ def bookmarks(self): return self._repo.nodebookmarks(self._node) def phase(self): - if self._rev == -1: - return phases.public - if self._rev >= len(self._repo._phaserev): - # outdated cache - del self._repo._phaserev - return self._repo._phaserev[self._rev] + return self._repo._phasecache.phase(self._repo, self._rev) def phasestr(self): return phases.phasenames[self.phase()] def mutable(self): return self.phase() > phases.public def hidden(self): - return self._rev in self._repo.changelog.hiddenrevs + return self._rev in self._repo.hiddenrevs def parents(self): """return contexts for each parent changeset""" @@ -223,13 +223,48 @@ return [changectx(self._repo, x) for x in c] def ancestors(self): - for a in self._repo.changelog.ancestors(self._rev): + for a in self._repo.changelog.ancestors([self._rev]): yield changectx(self._repo, a) def descendants(self): - for d in self._repo.changelog.descendants(self._rev): + for d in self._repo.changelog.descendants([self._rev]): yield changectx(self._repo, d) + def obsolete(self): + """True if the changeset is obsolete""" + return (self.node() in self._repo.obsstore.precursors + and self.phase() > phases.public) + + def extinct(self): + """True if the changeset is extinct""" + # We should just compute a cache a check againts it. + # see revset implementation for details + # + # But this naive implementation does not require cache + if self.phase() <= phases.public: + return False + if not self.obsolete(): + return False + for desc in self.descendants(): + if not desc.obsolete(): + return False + return True + + def unstable(self): + """True if the changeset is not obsolete but it's ancestor are""" + # We should just compute /(obsolete()::) - obsolete()/ + # and keep it in a cache. + # + # But this naive implementation does not require cache + if self.phase() <= phases.public: + return False + if self.obsolete(): + return False + for anc in self.ancestors(): + if anc.obsolete(): + return True + return False + def _fileinfo(self, path): if '_manifest' in self.__dict__: try: @@ -239,7 +274,8 @@ _('not found in manifest')) if '_manifestdelta' in self.__dict__ or path in self.files(): if path in self._manifestdelta: - return self._manifestdelta[path], self._manifestdelta.flags(path) + return (self._manifestdelta[path], + self._manifestdelta.flags(path)) node, flag = self._repo.manifest.find(self._changeset[0], path) if not node: raise error.LookupError(self._node, path, @@ -636,27 +672,27 @@ return zip(hist[base][0], hist[base][1].splitlines(True)) - def ancestor(self, fc2, actx=None): + def ancestor(self, fc2, actx): """ find the common ancestor file context, if any, of self, and fc2 - If actx is given, it must be the changectx of the common ancestor + actx must be the changectx of the common ancestor of self's and fc2's respective changesets. """ - if actx is None: - actx = self.changectx().ancestor(fc2.changectx()) - - # the trivial case: changesets are unrelated, files must be too - if not actx: - return None - # the easy case: no (relevant) renames if fc2.path() == self.path() and self.path() in actx: return actx[self.path()] - acache = {} + + # the next easiest cases: unambiguous predecessor (name trumps + # history) + if self.path() in actx and fc2.path() not in actx: + return actx[self.path()] + if fc2.path() in actx and self.path() not in actx: + return actx[fc2.path()] # prime the ancestor cache for the working directory + acache = {} for c in (self, fc2): if c._filerev is None: pl = [(n.path(), n.filenode()) for n in c.parents()] @@ -697,6 +733,14 @@ c = visit.pop(max(visit)) yield c + def copies(self, c2): + if not util.safehasattr(self, "_copycache"): + self._copycache = {} + sc2 = str(c2) + if sc2 not in self._copycache: + self._copycache[sc2] = copies.pathcopies(c2) + return self._copycache[sc2] + class workingctx(changectx): """A workingctx object makes access to data related to the current working directory convenient. @@ -890,6 +934,8 @@ return self._clean def branch(self): return encoding.tolocal(self._extra['branch']) + def closesbranch(self): + return 'close' in self._extra def extra(self): return self._extra @@ -1008,7 +1054,7 @@ def ancestors(self): for a in self._repo.changelog.ancestors( - *[p.rev() for p in self._parents]): + [p.rev() for p in self._parents]): yield changectx(self._repo, a) def undelete(self, list): @@ -1043,7 +1089,7 @@ wlock.release() def dirs(self): - return self._repo.dirstate.dirs() + return set(self._repo.dirstate.dirs()) class workingfilectx(filectx): """A workingfilectx object makes access to data related to a particular diff -r 98823bd0d697 -r a06e2681dd17 mercurial/copies.py --- a/mercurial/copies.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/copies.py Wed Jul 18 19:08:25 2012 -0500 @@ -177,19 +177,22 @@ "diverge" is a mapping of source name -> list of destination names for divergent renames. + + "renamedelete" is a mapping of source name -> list of destination + names for files deleted in c1 that were renamed in c2 or vice-versa. """ # avoid silly behavior for update from empty dir if not c1 or not c2 or c1 == c2: - return {}, {} + return {}, {}, {} # avoid silly behavior for parent -> working dir if c2.node() is None and c1.node() == repo.dirstate.p1(): - return repo.dirstate.copies(), {} + return repo.dirstate.copies(), {}, {} limit = _findlimit(repo, c1.rev(), c2.rev()) if limit is None: # no common ancestor, no copies - return {}, {} + return {}, {}, {} m1 = c1.manifest() m2 = c2.manifest() ma = ca.manifest() @@ -283,32 +286,44 @@ for f in u2: checkcopies(f, m2, m1) + renamedelete = {} + renamedelete2 = set() diverge2 = set() for of, fl in diverge.items(): - if len(fl) == 1 or of in c2: + if len(fl) == 1 or of in c1 or of in c2: del diverge[of] # not actually divergent, or not a rename + if of not in c1 and of not in c2: + # renamed on one side, deleted on the other side, but filter + # out files that have been renamed and then deleted + renamedelete[of] = [f for f in fl if f in c1 or f in c2] + renamedelete2.update(fl) # reverse map for below else: diverge2.update(fl) # reverse map for below if fullcopy: - repo.ui.debug(" all copies found (* = to merge, ! = divergent):\n") + repo.ui.debug(" all copies found (* = to merge, ! = divergent, " + "% = renamed and deleted):\n") for f in fullcopy: note = "" if f in copy: note += "*" if f in diverge2: note += "!" + if f in renamedelete2: + note += "%" repo.ui.debug(" %s -> %s %s\n" % (f, fullcopy[f], note)) del diverge2 if not fullcopy: - return copy, diverge + return copy, diverge, renamedelete repo.ui.debug(" checking for directory renames\n") # generate a directory move map d1, d2 = c1.dirs(), c2.dirs() - invalid = set([""]) + d1.add('') + d2.add('') + invalid = set() dirmove = {} # examine each file copy for a potential directory move, which is @@ -337,7 +352,7 @@ del d1, d2, invalid if not dirmove: - return copy, diverge + return copy, diverge, renamedelete for d in dirmove: repo.ui.debug(" dir %s -> %s\n" % (d, dirmove[d])) @@ -354,4 +369,4 @@ repo.ui.debug(" file %s -> %s\n" % (f, copy[f])) break - return copy, diverge + return copy, diverge, renamedelete diff -r 98823bd0d697 -r a06e2681dd17 mercurial/dagparser.py --- a/mercurial/dagparser.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/dagparser.py Wed Jul 18 19:08:25 2012 -0500 @@ -268,7 +268,8 @@ s += c i += 1 c = nextch() - raise util.Abort(_("invalid character in dag description: %s...") % s) + raise util.Abort(_('invalid character in dag description: ' + '%s...') % s) def dagtextlines(events, addspaces=True, @@ -436,7 +437,9 @@ >>> dagtext([('n', (0, [-1])), ('a', 'ann'), ('n', (1, [0]))]) '+1 @ann +1' - >>> dagtext([('n', (0, [-1])), ('a', 'my annotation'), ('n', (1, [0]))]) + >>> dagtext([('n', (0, [-1])), + ... ('a', 'my annotation'), + ... ('n', (1, [0]))]) '+1 @"my annotation" +1' Commands: @@ -447,7 +450,9 @@ >>> dagtext([('n', (0, [-1])), ('c', 'my command'), ('n', (1, [0]))]) '+1 !"my command" +1' - >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))]) + >>> dagtext([('n', (0, [-1])), + ... ('C', 'my command line'), + ... ('n', (1, [0]))]) '+1 !!my command line\\n+1' Comments: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/dagutil.py --- a/mercurial/dagutil.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/dagutil.py Wed Jul 18 19:08:25 2012 -0500 @@ -26,25 +26,25 @@ def nodeset(self): '''set of all node idxs''' - raise NotImplementedError() + raise NotImplementedError def heads(self): '''list of head ixs''' - raise NotImplementedError() + raise NotImplementedError def parents(self, ix): '''list of parents ixs of ix''' - raise NotImplementedError() + raise NotImplementedError def inverse(self): '''inverse DAG, where parents becomes children, etc.''' - raise NotImplementedError() + raise NotImplementedError def ancestorset(self, starts, stops=None): ''' set of all ancestors of starts (incl), but stop walk at stops (excl) ''' - raise NotImplementedError() + raise NotImplementedError def descendantset(self, starts, stops=None): ''' @@ -59,7 +59,7 @@ By "connected list" we mean that if an ancestor and a descendant are in the list, then so is at least one path connecting them. ''' - raise NotImplementedError() + raise NotImplementedError def externalize(self, ix): '''return a list of (or set if given a set) of node ids''' diff -r 98823bd0d697 -r a06e2681dd17 mercurial/diffhelpers.c --- a/mercurial/diffhelpers.c Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/diffhelpers.c Wed Jul 18 19:08:25 2012 -0500 @@ -20,14 +20,14 @@ /* fixup the last lines of a and b when the patch has no newline at eof */ static void _fix_newline(PyObject *hunk, PyObject *a, PyObject *b) { - int hunksz = PyList_Size(hunk); + Py_ssize_t hunksz = PyList_Size(hunk); PyObject *s = PyList_GET_ITEM(hunk, hunksz-1); char *l = PyBytes_AsString(s); - int alen = PyList_Size(a); - int blen = PyList_Size(b); + Py_ssize_t alen = PyList_Size(a); + Py_ssize_t blen = PyList_Size(b); char c = l[0]; PyObject *hline; - int sz = PyBytes_GET_SIZE(s); + Py_ssize_t sz = PyBytes_GET_SIZE(s); if (sz > 1 && l[sz-2] == '\r') /* tolerate CRLF in last line */ @@ -57,6 +57,12 @@ return Py_BuildValue("l", 0); } +#if (PY_VERSION_HEX < 0x02050000) +static const char *addlines_format = "OOiiOO"; +#else +static const char *addlines_format = "OOnnOO"; +#endif + /* * read lines from fp into the hunk. The hunk is parsed into two arrays * a and b. a gets the old state of the text, b gets the new state @@ -68,13 +74,14 @@ { PyObject *fp, *hunk, *a, *b, *x; - int i; - int lena, lenb; - int num; - int todoa, todob; + Py_ssize_t i; + Py_ssize_t lena, lenb; + Py_ssize_t num; + Py_ssize_t todoa, todob; char *s, c; PyObject *l; - if (!PyArg_ParseTuple(args, "OOiiOO", &fp, &hunk, &lena, &lenb, &a, &b)) + if (!PyArg_ParseTuple(args, addlines_format, + &fp, &hunk, &lena, &lenb, &a, &b)) return NULL; while (1) { @@ -127,8 +134,8 @@ PyObject *a, *b; long bstart; - int alen, blen; - int i; + Py_ssize_t alen, blen; + Py_ssize_t i; char *sa, *sb; if (!PyArg_ParseTuple(args, "OOl", &a, &b, &bstart)) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/dirstate.py --- a/mercurial/dirstate.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/dirstate.py Wed Jul 18 19:08:25 2012 -0500 @@ -312,9 +312,9 @@ if self[f] not in "?r" and "_dirs" in self.__dict__: _decdirs(self._dirs, f) - def _addpath(self, f, check=False): + def _addpath(self, f, state, mode, size, mtime): oldstate = self[f] - if check or oldstate == "r": + if state == 'a' or oldstate == 'r': scmutil.checkfilename(f) if f in self._dirs: raise util.Abort(_('directory %r already in dirstate') % f) @@ -327,14 +327,14 @@ _('file %r in dirstate clashes with %r') % (d, f)) if oldstate in "?r" and "_dirs" in self.__dict__: _incdirs(self._dirs, f) + self._dirty = True + self._map[f] = (state, mode, size, mtime) def normal(self, f): '''Mark a file normal and clean.''' - self._dirty = True - self._addpath(f) s = os.lstat(self._join(f)) mtime = int(s.st_mtime) - self._map[f] = ('n', s.st_mode, s.st_size, mtime) + self._addpath(f, 'n', s.st_mode, s.st_size, mtime) if f in self._copymap: del self._copymap[f] if mtime > self._lastnormaltime: @@ -361,9 +361,7 @@ return if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: return - self._dirty = True - self._addpath(f) - self._map[f] = ('n', 0, -1, -1) + self._addpath(f, 'n', 0, -1, -1) if f in self._copymap: del self._copymap[f] @@ -372,17 +370,13 @@ if self._pl[1] == nullid: raise util.Abort(_("setting %r to other parent " "only allowed in merges") % f) - self._dirty = True - self._addpath(f) - self._map[f] = ('n', 0, -2, -1) + self._addpath(f, 'n', 0, -2, -1) if f in self._copymap: del self._copymap[f] def add(self, f): '''Mark a file added.''' - self._dirty = True - self._addpath(f, True) - self._map[f] = ('a', 0, -1, -1) + self._addpath(f, 'a', 0, -1, -1) if f in self._copymap: del self._copymap[f] @@ -406,10 +400,8 @@ '''Mark a file merged.''' if self._pl[1] == nullid: return self.normallookup(f) - self._dirty = True s = os.lstat(self._join(f)) - self._addpath(f) - self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime)) + self._addpath(f, 'm', s.st_mode, s.st_size, int(s.st_mtime)) if f in self._copymap: del self._copymap[f] @@ -498,12 +490,24 @@ return st = self._opener("dirstate", "w", atomictemp=True) + def finish(s): + st.write(s) + st.close() + self._lastnormaltime = 0 + self._dirty = self._dirtypl = False + # use the modification time of the newly created temporary file as the # filesystem's notion of 'now' - now = int(util.fstat(st).st_mtime) + now = util.fstat(st).st_mtime + copymap = self._copymap + try: + finish(parsers.pack_dirstate(self._map, copymap, self._pl, now)) + return + except AttributeError: + pass + now = int(now) cs = cStringIO.StringIO() - copymap = self._copymap pack = struct.pack write = cs.write write("".join(self._pl)) @@ -526,10 +530,7 @@ e = pack(_format, e[0], e[1], e[2], e[3], len(f)) write(e) write(f) - st.write(cs.getvalue()) - st.close() - self._lastnormaltime = 0 - self._dirty = self._dirtypl = False + finish(cs.getvalue()) def _dirignore(self, f): if f == '.': @@ -695,7 +696,8 @@ if not skipstep3 and not exact: visit = sorted([f for f in dmap if f not in results and matchfn(f)]) for nf, st in zip(visit, util.statfiles([join(i) for i in visit])): - if not st is None and not getkind(st.st_mode) in (regkind, lnkkind): + if (not st is None and + getkind(st.st_mode) not in (regkind, lnkkind)): st = None results[nf] = st for s in subrepos: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/discovery.py --- a/mercurial/discovery.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/discovery.py Wed Jul 18 19:08:25 2012 -0500 @@ -7,7 +7,7 @@ from node import nullid, short from i18n import _ -import util, setdiscovery, treediscovery, phases +import util, setdiscovery, treediscovery, phases, obsolete def findcommonincoming(repo, remote, heads=None, force=False): """Return a tuple (common, anyincoming, heads) used to identify the common @@ -86,14 +86,14 @@ self._computecommonmissing() return self._missing -def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None, - portable=False): +def findcommonoutgoing(repo, other, onlyheads=None, force=False, + commoninc=None, portable=False): '''Return an outgoing instance to identify the nodes present in repo but not in other. - If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive) - are included. If you already know the local repo's heads, passing them in - onlyheads is faster than letting them be recomputed here. + If onlyheads is given, only nodes ancestral to nodes in onlyheads + (inclusive) are included. If you already know the local repo's heads, + passing them in onlyheads is faster than letting them be recomputed here. If commoninc is given, it must the the result of a prior call to findcommonincoming(repo, other, force) to avoid recomputing it here. @@ -109,11 +109,13 @@ og.commonheads, _any, _hds = commoninc # compute outgoing - if not repo._phaseroots[phases.secret]: + mayexclude = (repo._phasecache.phaseroots[phases.secret] or repo.obsstore) + if not mayexclude: og.missingheads = onlyheads or repo.heads() elif onlyheads is None: # use visible heads as it should be cached - og.missingheads = phases.visibleheads(repo) + og.missingheads = visibleheads(repo) + # extinct changesets are silently ignored og.excluded = [ctx.node() for ctx in repo.set('secret()')] else: # compute common, missing and exclude secret stuff @@ -122,17 +124,18 @@ og._missing = missing = [] og.excluded = excluded = [] for node in allmissing: - if repo[node].phase() >= phases.secret: - excluded.append(node) - else: - missing.append(node) - if excluded: - # update missing heads + ctx = repo[node] + if not ctx.extinct(): + # extinct changesets are silently ignored + if ctx.phase() >= phases.secret: + excluded.append(node) + else: + missing.append(node) + if len(missing) == len(allmissing): + missingheads = onlyheads + else: # update missing heads missingheads = phases.newheads(repo, onlyheads, excluded) - else: - missingheads = onlyheads og.missingheads = missingheads - if portable: # recompute common and missingheads as if -r had been given for # each head of missing, and --base for each head of the proper @@ -140,97 +143,170 @@ og._computecommonmissing() cl = repo.changelog missingrevs = set(cl.rev(n) for n in og._missing) - og._common = set(cl.ancestors(*missingrevs)) - missingrevs + og._common = set(cl.ancestors(missingrevs)) - missingrevs commonheads = set(og.commonheads) og.missingheads = [h for h in og.missingheads if h not in commonheads] return og +def _headssummary(repo, remote, outgoing): + """compute a summary of branch and heads status before and after push + + return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping + + - branch: the branch name + - remoteheads: the list of remote heads known locally + None is the branch is new + - newheads: the new remote heads (known locally) with outgoing pushed + - unsyncedheads: the list of remote heads unknown locally. + """ + cl = repo.changelog + headssum = {} + # A. Create set of branches involved in the push. + branches = set(repo[n].branch() for n in outgoing.missing) + remotemap = remote.branchmap() + newbranches = branches - set(remotemap) + branches.difference_update(newbranches) + + # A. register remote heads + remotebranches = set() + for branch, heads in remote.branchmap().iteritems(): + remotebranches.add(branch) + known = [] + unsynced = [] + for h in heads: + if h in cl.nodemap: + known.append(h) + else: + unsynced.append(h) + headssum[branch] = (known, list(known), unsynced) + # B. add new branch data + missingctx = list(repo[n] for n in outgoing.missing) + touchedbranches = set() + for ctx in missingctx: + branch = ctx.branch() + touchedbranches.add(branch) + if branch not in headssum: + headssum[branch] = (None, [], []) + + # C drop data about untouched branches: + for branch in remotebranches - touchedbranches: + del headssum[branch] + + # D. Update newmap with outgoing changes. + # This will possibly add new heads and remove existing ones. + newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems() + if heads[0] is not None) + repo._updatebranchcache(newmap, missingctx) + for branch, newheads in newmap.iteritems(): + headssum[branch][1][:] = newheads + return headssum + +def _oldheadssummary(repo, remoteheads, outgoing, inc=False): + """Compute branchmapsummary for repo without branchmap support""" + + cl = repo.changelog + # 1-4b. old servers: Check for new topological heads. + # Construct {old,new}map with branch = None (topological branch). + # (code based on _updatebranchcache) + oldheads = set(h for h in remoteheads if h in cl.nodemap) + # all nodes in outgoing.missing are children of either: + # - an element of oldheads + # - another element of outgoing.missing + # - nullrev + # This explains why the new head are very simple to compute. + r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing) + newheads = list(c.node() for c in r) + unsynced = inc and set([None]) or set() + return {None: (oldheads, newheads, unsynced)} + def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False): """Check that a push won't add any outgoing head raise Abort error and display ui message as needed. """ + # Check for each named branch if we're creating new remote heads. + # To be a remote head after push, node must be either: + # - unknown locally + # - a local outgoing head descended from update + # - a remote head that's known locally and not + # ancestral to an outgoing head if remoteheads == [nullid]: # remote is empty, nothing to check. return - cl = repo.changelog if remote.capable('branchmap'): - # Check for each named branch if we're creating new remote heads. - # To be a remote head after push, node must be either: - # - unknown locally - # - a local outgoing head descended from update - # - a remote head that's known locally and not - # ancestral to an outgoing head - - # 1. Create set of branches involved in the push. - branches = set(repo[n].branch() for n in outgoing.missing) - - # 2. Check for new branches on the remote. - if remote.local(): - remotemap = phases.visiblebranchmap(remote) - else: - remotemap = remote.branchmap() - newbranches = branches - set(remotemap) - if newbranches and not newbranch: # new branch requires --new-branch - branchnames = ', '.join(sorted(newbranches)) - raise util.Abort(_("push creates new remote branches: %s!") - % branchnames, - hint=_("use 'hg push --new-branch' to create" - " new remote branches")) - branches.difference_update(newbranches) + headssum = _headssummary(repo, remote, outgoing) + else: + headssum = _oldheadssummary(repo, remoteheads, outgoing, inc) + newbranches = [branch for branch, heads in headssum.iteritems() + if heads[0] is None] + # 1. Check for new branches on the remote. + if newbranches and not newbranch: # new branch requires --new-branch + branchnames = ', '.join(sorted(newbranches)) + raise util.Abort(_("push creates new remote branches: %s!") + % branchnames, + hint=_("use 'hg push --new-branch' to create" + " new remote branches")) - # 3. Construct the initial oldmap and newmap dicts. - # They contain information about the remote heads before and - # after the push, respectively. - # Heads not found locally are not included in either dict, - # since they won't be affected by the push. - # unsynced contains all branches with incoming changesets. - oldmap = {} - newmap = {} - unsynced = set() - for branch in branches: - remotebrheads = remotemap[branch] - prunedbrheads = [h for h in remotebrheads if h in cl.nodemap] - oldmap[branch] = prunedbrheads - newmap[branch] = list(prunedbrheads) - if len(remotebrheads) > len(prunedbrheads): - unsynced.add(branch) + # 2 compute newly pushed bookmarks. We + # we don't warned about bookmarked heads. + localbookmarks = repo._bookmarks + remotebookmarks = remote.listkeys('bookmarks') + bookmarkedheads = set() + for bm in localbookmarks: + rnode = remotebookmarks.get(bm) + if rnode and rnode in repo: + lctx, rctx = repo[bm], repo[rnode] + if rctx == lctx.ancestor(rctx): + bookmarkedheads.add(lctx.node()) - # 4. Update newmap with outgoing changes. - # This will possibly add new heads and remove existing ones. - ctxgen = (repo[n] for n in outgoing.missing) - repo._updatebranchcache(newmap, ctxgen) - - else: - # 1-4b. old servers: Check for new topological heads. - # Construct {old,new}map with branch = None (topological branch). - # (code based on _updatebranchcache) - oldheads = set(h for h in remoteheads if h in cl.nodemap) - newheads = oldheads.union(outgoing.missing) - if len(newheads) > 1: - for latest in reversed(outgoing.missing): - if latest not in newheads: - continue - minhrev = min(cl.rev(h) for h in newheads) - reachable = cl.reachable(latest, cl.node(minhrev)) - reachable.remove(latest) - newheads.difference_update(reachable) - branches = set([None]) - newmap = {None: newheads} - oldmap = {None: oldheads} - unsynced = inc and branches or set() - - # 5. Check for new heads. + # 3. Check for new heads. # If there are more heads after the push than before, a suitable # error message, depending on unsynced status, is displayed. error = None - for branch in branches: - newhs = set(newmap[branch]) - oldhs = set(oldmap[branch]) + unsynced = False + allmissing = set(outgoing.missing) + for branch, heads in headssum.iteritems(): + if heads[0] is None: + # Maybe we should abort if we push more that one head + # for new branches ? + continue + if heads[2]: + unsynced = True + oldhs = set(heads[0]) + candidate_newhs = set(heads[1]) + # add unsynced data + oldhs.update(heads[2]) + candidate_newhs.update(heads[2]) + dhs = None + if repo.obsstore: + # remove future heads which are actually obsolete by another + # pushed element: + # + # XXX There is several case this case does not handle properly + # + # (1) if is public, it won't be affected by obsolete marker + # and a new is created + # + # (2) if the new heads have ancestors which are not obsolete and + # not ancestors of any other heads we will have a new head too. + # + # This two case will be easy to handle for know changeset but much + # more tricky for unsynced changes. + newhs = set() + for nh in candidate_newhs: + for suc in obsolete.anysuccessors(repo.obsstore, nh): + if suc != nh and suc in allmissing: + break + else: + newhs.add(nh) + else: + newhs = candidate_newhs if len(newhs) > len(oldhs): - dhs = list(newhs - oldhs) + # strip updates to existing remote heads from the new heads list + dhs = list(newhs - bookmarkedheads - oldhs) + if dhs: if error is None: if branch not in ('default', None): error = _("push creates new remote head %s " @@ -238,7 +314,7 @@ else: error = _("push creates new remote head %s!" ) % short(dhs[0]) - if branch in unsynced: + if heads[2]: # unsynced hint = _("you should pull and merge or " "use push -f to force") else: @@ -254,3 +330,43 @@ # 6. Check for unsynced changes on involved branches. if unsynced: repo.ui.warn(_("note: unsynced remote changes!\n")) + +def visibleheads(repo): + """return the set of visible head of this repo""" + # XXX we want a cache on this + sroots = repo._phasecache.phaseroots[phases.secret] + if sroots or repo.obsstore: + # XXX very slow revset. storing heads or secret "boundary" + # would help. + revset = repo.set('heads(not (%ln:: + extinct()))', sroots) + + vheads = [ctx.node() for ctx in revset] + if not vheads: + vheads.append(nullid) + else: + vheads = repo.heads() + return vheads + + +def visiblebranchmap(repo): + """return a branchmap for the visible set""" + # XXX Recomputing this data on the fly is very slow. We should build a + # XXX cached version while computin the standard branchmap version. + sroots = repo._phasecache.phaseroots[phases.secret] + if sroots or repo.obsstore: + vbranchmap = {} + for branch, nodes in repo.branchmap().iteritems(): + # search for secret heads. + for n in nodes: + if repo[n].phase() >= phases.secret: + nodes = None + break + # if secret heads were found we must compute them again + if nodes is None: + s = repo.set('heads(branch(%s) - secret() - extinct())', + branch) + nodes = [c.node() for c in s] + vbranchmap[branch] = nodes + else: + vbranchmap = repo.branchmap() + return vbranchmap diff -r 98823bd0d697 -r a06e2681dd17 mercurial/dispatch.py --- a/mercurial/dispatch.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/dispatch.py Wed Jul 18 19:08:25 2012 -0500 @@ -12,7 +12,8 @@ import ui as uimod class request(object): - def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None): + def __init__(self, args, ui=None, repo=None, fin=None, fout=None, + ferr=None): self.args = args self.ui = ui self.repo = repo @@ -87,7 +88,7 @@ return _dispatch(req) finally: ui.flush() - except: + except: # re-raises # enter the debugger when we hit an exception if '--debugger' in req.args: traceback.print_exc() @@ -203,19 +204,59 @@ return inst.code except socket.error, inst: ui.warn(_("abort: %s\n") % inst.args[-1]) - except: - ui.warn(_("** unknown exception encountered," - " please report by visiting\n")) - ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n")) - ui.warn(_("** Python %s\n") % sys.version.replace('\n', '')) - ui.warn(_("** Mercurial Distributed SCM (version %s)\n") - % util.version()) - ui.warn(_("** Extensions loaded: %s\n") - % ", ".join([x[0] for x in extensions.extensions()])) + except: # re-raises + myver = util.version() + # For compatibility checking, we discard the portion of the hg + # version after the + on the assumption that if a "normal + # user" is running a build with a + in it the packager + # probably built from fairly close to a tag and anyone with a + # 'make local' copy of hg (where the version number can be out + # of date) will be clueful enough to notice the implausible + # version number and try updating. + compare = myver.split('+')[0] + ct = tuplever(compare) + worst = None, ct, '' + for name, mod in extensions.extensions(): + testedwith = getattr(mod, 'testedwith', 'unknown') + report = getattr(mod, 'buglink', _('the extension author.')) + if testedwith == 'unknown': + # We found an untested extension. It's likely the culprit. + worst = name, testedwith, report + break + if compare not in testedwith.split() and testedwith != 'internal': + tested = [tuplever(v) for v in testedwith.split()] + nearest = max([t for t in tested if t < ct]) + if nearest < worst[1]: + worst = name, nearest, report + if worst[0] is not None: + name, testedwith, report = worst + if not isinstance(testedwith, str): + testedwith = '.'.join([str(c) for c in testedwith]) + warning = (_('** Unknown exception encountered with ' + 'possibly-broken third-party extension %s\n' + '** which supports versions %s of Mercurial.\n' + '** Please disable %s and try your action again.\n' + '** If that fixes the bug please report it to %s\n') + % (name, testedwith, name, report)) + else: + warning = (_("** unknown exception encountered, " + "please report by visiting\n") + + _("** http://mercurial.selenic.com/wiki/BugTracker\n")) + warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) + + (_("** Mercurial Distributed SCM (version %s)\n") % myver) + + (_("** Extensions loaded: %s\n") % + ", ".join([x[0] for x in extensions.extensions()]))) + ui.warn(warning) raise return -1 +def tuplever(v): + try: + return tuple([int(i) for i in v.split('.')]) + except ValueError: + return tuple() + def aliasargs(fn, givenargs): args = getattr(fn, 'args', []) if args: @@ -532,7 +573,8 @@ if cmd and util.safehasattr(fn, 'shell'): d = lambda: fn(ui, *args[1:]) - return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {}) + return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, + [], {}) restorecommands() @@ -612,7 +654,7 @@ s = get_times() def print_time(): t = get_times() - ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % + ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3])) atexit.register(print_time) @@ -680,7 +722,8 @@ return _dispatch(req) if not path: raise error.RepoError(_("no repository found in '%s'" - " (.hg not found)") % os.getcwd()) + " (.hg not found)") + % os.getcwd()) raise if repo: ui = repo.ui @@ -703,7 +746,7 @@ field = ui.config('profiling', 'sort', default='inlinetime') climit = ui.configint('profiling', 'nested', default=5) - if not format in ['text', 'kcachegrind']: + if format not in ['text', 'kcachegrind']: ui.warn(_("unrecognized profiling format '%s'" " - Ignored\n") % format) format = 'text' diff -r 98823bd0d697 -r a06e2681dd17 mercurial/exewrapper.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/exewrapper.c Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,101 @@ +/* + exewrapper.c - wrapper for calling a python script on Windows + + Copyright 2012 Adrian Buehlmann and others + + This software may be used and distributed according to the terms of the + GNU General Public License version 2 or any later version. +*/ + +#include +#include + + +#ifdef __GNUC__ +int strcat_s(char *d, size_t n, const char *s) +{ + return !strncat(d, s, n); +} +#endif + + +static char pyscript[MAX_PATH + 10]; + +int main(int argc, char *argv[]) +{ + char *dot; + int ret; + int i; + int n; + char **pyargv; + WIN32_FIND_DATA fdata; + HANDLE hfind; + const char *err; + + if (GetModuleFileName(NULL, pyscript, sizeof(pyscript)) == 0) + { + err = "GetModuleFileName failed"; + goto bail; + } + + dot = strrchr(pyscript, '.'); + if (dot == NULL) { + err = "malformed module filename"; + goto bail; + } + *dot = 0; /* cut trailing ".exe" */ + + hfind = FindFirstFile(pyscript, &fdata); + if (hfind != INVALID_HANDLE_VALUE) { + /* pyscript exists, close handle */ + FindClose(hfind); + } else { + /* file pyscript isn't there, take exe.py */ + strcat_s(pyscript, sizeof(pyscript), "exe.py"); + } + + /* + Only add the pyscript to the args, if it's not already there. It may + already be there, if the script spawned a child process of itself, in + the same way as it got called, that is, with the pyscript already in + place. So we optionally accept the pyscript as the first argument + (argv[1]), letting our exe taking the role of the python interpreter. + */ + if (argc >= 2 && strcmp(argv[1], pyscript) == 0) { + /* + pyscript is already in the args, so there is no need to copy + the args and we can directly call the python interpreter with + the original args. + */ + return Py_Main(argc, argv); + } + + /* + Start assembling the args for the Python interpreter call. We put the + name of our exe (argv[0]) in the position where the python.exe + canonically is, and insert the pyscript next. + */ + pyargv = malloc((argc + 5) * sizeof(char*)); + if (pyargv == NULL) { + err = "not enough memory"; + goto bail; + } + n = 0; + pyargv[n++] = argv[0]; + pyargv[n++] = pyscript; + + /* copy remaining args from the command line */ + for (i = 1; i < argc; i++) + pyargv[n++] = argv[i]; + /* argv[argc] is guaranteed to be NULL, so we forward that guarantee */ + pyargv[n] = NULL; + + ret = Py_Main(n, pyargv); /* The Python interpreter call */ + + free(pyargv); + return ret; + +bail: + fprintf(stderr, "abort: %s\n", err); + return 255; +} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/extensions.py --- a/mercurial/extensions.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/extensions.py Wed Jul 18 19:08:25 2012 -0500 @@ -42,7 +42,12 @@ fd, fpath, desc = imp.find_module(f, [d]) return imp.load_module(module_name, fd, fpath, desc) else: - return imp.load_source(module_name, path) + try: + return imp.load_source(module_name, path) + except IOError, exc: + if not exc.filename: + exc.filename = path # python does not fill this + raise def load(ui, name, path): # unused ui argument kept for backwards compatibility @@ -274,7 +279,7 @@ paths = _disabledpaths() if not paths: - return None + return {} exts = {} for name, path in paths.iteritems(): @@ -301,7 +306,7 @@ def disabledcmd(ui, cmd, strict=False): '''import disabled extensions until cmd is found. - returns (cmdname, extname, doc)''' + returns (cmdname, extname, module)''' paths = _disabledpaths(strip_init=True) if not paths: @@ -329,18 +334,19 @@ cmd = aliases[0] return (cmd, name, mod) + ext = None # first, search for an extension with the same name as the command path = paths.pop(cmd, None) if path: ext = findcmd(cmd, cmd, path) - if ext: - return ext - - # otherwise, interrogate each extension until there's a match - for name, path in paths.iteritems(): - ext = findcmd(cmd, name, path) - if ext: - return ext + if not ext: + # otherwise, interrogate each extension until there's a match + for name, path in paths.iteritems(): + ext = findcmd(cmd, name, path) + if ext: + break + if ext and 'DEPRECATED' not in ext.__doc__: + return ext raise error.UnknownCommand(cmd) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/graphmod.py --- a/mercurial/graphmod.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/graphmod.py Wed Jul 18 19:08:25 2012 -0500 @@ -163,3 +163,206 @@ pending.update([p for p in cl.parentrevs(r)]) seen.add(r) return sorted(kept) + +def asciiedges(type, char, lines, seen, rev, parents): + """adds edge info to changelog DAG walk suitable for ascii()""" + if rev not in seen: + seen.append(rev) + nodeidx = seen.index(rev) + + knownparents = [] + newparents = [] + for parent in parents: + if parent in seen: + knownparents.append(parent) + else: + newparents.append(parent) + + ncols = len(seen) + nextseen = seen[:] + nextseen[nodeidx:nodeidx + 1] = newparents + edges = [(nodeidx, nextseen.index(p)) for p in knownparents] + + while len(newparents) > 2: + # ascii() only knows how to add or remove a single column between two + # calls. Nodes with more than two parents break this constraint so we + # introduce intermediate expansion lines to grow the active node list + # slowly. + edges.append((nodeidx, nodeidx)) + edges.append((nodeidx, nodeidx + 1)) + nmorecols = 1 + yield (type, char, lines, (nodeidx, edges, ncols, nmorecols)) + char = '\\' + lines = [] + nodeidx += 1 + ncols += 1 + edges = [] + del newparents[0] + + if len(newparents) > 0: + edges.append((nodeidx, nodeidx)) + if len(newparents) > 1: + edges.append((nodeidx, nodeidx + 1)) + nmorecols = len(nextseen) - ncols + seen[:] = nextseen + yield (type, char, lines, (nodeidx, edges, ncols, nmorecols)) + +def _fixlongrightedges(edges): + for (i, (start, end)) in enumerate(edges): + if end > start: + edges[i] = (start, end + 1) + +def _getnodelineedgestail( + node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail): + if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0: + # Still going in the same non-vertical direction. + if n_columns_diff == -1: + start = max(node_index + 1, p_node_index) + tail = ["|", " "] * (start - node_index - 1) + tail.extend(["/", " "] * (n_columns - start)) + return tail + else: + return ["\\", " "] * (n_columns - node_index - 1) + else: + return ["|", " "] * (n_columns - node_index - 1) + +def _drawedges(edges, nodeline, interline): + for (start, end) in edges: + if start == end + 1: + interline[2 * end + 1] = "/" + elif start == end - 1: + interline[2 * start + 1] = "\\" + elif start == end: + interline[2 * start] = "|" + else: + if 2 * end >= len(nodeline): + continue + nodeline[2 * end] = "+" + if start > end: + (start, end) = (end, start) + for i in range(2 * start + 1, 2 * end): + if nodeline[i] != "+": + nodeline[i] = "-" + +def _getpaddingline(ni, n_columns, edges): + line = [] + line.extend(["|", " "] * ni) + if (ni, ni - 1) in edges or (ni, ni) in edges: + # (ni, ni - 1) (ni, ni) + # | | | | | | | | + # +---o | | o---+ + # | | c | | c | | + # | |/ / | |/ / + # | | | | | | + c = "|" + else: + c = " " + line.extend([c, " "]) + line.extend(["|", " "] * (n_columns - ni - 1)) + return line + +def asciistate(): + """returns the initial value for the "state" argument to ascii()""" + return [0, 0] + +def ascii(ui, state, type, char, text, coldata): + """prints an ASCII graph of the DAG + + takes the following arguments (one call per node in the graph): + + - ui to write to + - Somewhere to keep the needed state in (init to asciistate()) + - Column of the current node in the set of ongoing edges. + - Type indicator of node data, usually 'C' for changesets. + - Payload: (char, lines): + - Character to use as node's symbol. + - List of lines to display as the node's text. + - Edges; a list of (col, next_col) indicating the edges between + the current node and its parents. + - Number of columns (ongoing edges) in the current revision. + - The difference between the number of columns (ongoing edges) + in the next revision and the number of columns (ongoing edges) + in the current revision. That is: -1 means one column removed; + 0 means no columns added or removed; 1 means one column added. + """ + + idx, edges, ncols, coldiff = coldata + assert -2 < coldiff < 2 + if coldiff == -1: + # Transform + # + # | | | | | | + # o | | into o---+ + # |X / |/ / + # | | | | + _fixlongrightedges(edges) + + # add_padding_line says whether to rewrite + # + # | | | | | | | | + # | o---+ into | o---+ + # | / / | | | # <--- padding line + # o | | | / / + # o | | + add_padding_line = (len(text) > 2 and coldiff == -1 and + [x for (x, y) in edges if x + 1 < y]) + + # fix_nodeline_tail says whether to rewrite + # + # | | o | | | | o | | + # | | |/ / | | |/ / + # | o | | into | o / / # <--- fixed nodeline tail + # | |/ / | |/ / + # o | | o | | + fix_nodeline_tail = len(text) <= 2 and not add_padding_line + + # nodeline is the line containing the node character (typically o) + nodeline = ["|", " "] * idx + nodeline.extend([char, " "]) + + nodeline.extend( + _getnodelineedgestail(idx, state[1], ncols, coldiff, + state[0], fix_nodeline_tail)) + + # shift_interline is the line containing the non-vertical + # edges between this entry and the next + shift_interline = ["|", " "] * idx + if coldiff == -1: + n_spaces = 1 + edge_ch = "/" + elif coldiff == 0: + n_spaces = 2 + edge_ch = "|" + else: + n_spaces = 3 + edge_ch = "\\" + shift_interline.extend(n_spaces * [" "]) + shift_interline.extend([edge_ch, " "] * (ncols - idx - 1)) + + # draw edges from the current node to its parents + _drawedges(edges, nodeline, shift_interline) + + # lines is the list of all graph lines to print + lines = [nodeline] + if add_padding_line: + lines.append(_getpaddingline(idx, ncols, edges)) + lines.append(shift_interline) + + # make sure that there are as many graph lines as there are + # log strings + while len(text) < len(lines): + text.append("") + if len(lines) < len(text): + extra_interline = ["|", " "] * (ncols + coldiff) + while len(lines) < len(text): + lines.append(extra_interline) + + # print lines + indentation_level = max(ncols, ncols + coldiff) + for (line, logstr) in zip(lines, text): + ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr) + ui.write(ln.rstrip() + '\n') + + # ... and start over + state[0] = coldiff + state[1] = idx diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hbisect.py --- a/mercurial/hbisect.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hbisect.py Wed Jul 18 19:08:25 2012 -0500 @@ -69,10 +69,10 @@ # build children dict children = {} - visit = [badrev] + visit = util.deque([badrev]) candidates = [] while visit: - rev = visit.pop(0) + rev = visit.popleft() if ancestors[rev] == []: candidates.append(rev) for prev in clparents(rev): @@ -132,7 +132,7 @@ def load_state(repo): - state = {'good': [], 'bad': [], 'skip': []} + state = {'current': [], 'good': [], 'bad': [], 'skip': []} if os.path.exists(repo.join("bisect.state")): for l in repo.opener("bisect.state"): kind, node = l[:-1].split() @@ -164,10 +164,11 @@ - ``pruned`` : csets that are goods, bads or skipped - ``untested`` : csets whose fate is yet unknown - ``ignored`` : csets ignored due to DAG topology + - ``current`` : the cset currently being bisected """ state = load_state(repo) - if status in ('good', 'bad', 'skip'): - return [repo.changelog.rev(n) for n in state[status]] + if status in ('good', 'bad', 'skip', 'current'): + return map(repo.changelog.rev, state[status]) else: # In the floowing sets, we do *not* call 'bisect()' with more # than one level of recusrsion, because that can be very, very @@ -233,7 +234,7 @@ if rev in get(repo, 'skip'): # i18n: bisect changeset status return _('skipped') - if rev in get(repo, 'untested'): + if rev in get(repo, 'untested') or rev in get(repo, 'current'): # i18n: bisect changeset status return _('untested') if rev in get(repo, 'ignored'): diff -r 98823bd0d697 -r a06e2681dd17 mercurial/help.py --- a/mercurial/help.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/help.py Wed Jul 18 19:08:25 2012 -0500 @@ -6,27 +6,114 @@ # GNU General Public License version 2 or any later version. from i18n import gettext, _ -import sys, os +import itertools, sys, os import extensions, revset, fileset, templatekw, templatefilters, filemerge -import util +import encoding, util, minirst def listexts(header, exts, indent=1): '''return a text listing of the given extensions''' - if not exts: - return '' - maxlength = max(len(e) for e in exts) - result = '\n%s\n\n' % header - for name, desc in sorted(exts.iteritems()): - result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2, - ':%s:' % name, desc) - return result + rst = [] + if exts: + rst.append('\n%s\n\n' % header) + for name, desc in sorted(exts.iteritems()): + rst.append('%s:%s: %s\n' % (' ' * indent, name, desc)) + return rst def extshelp(): - doc = loaddoc('extensions')() - doc += listexts(_('enabled extensions:'), extensions.enabled()) - doc += listexts(_('disabled extensions:'), extensions.disabled()) + rst = loaddoc('extensions')().splitlines(True) + rst.extend(listexts(_('enabled extensions:'), extensions.enabled())) + rst.extend(listexts(_('disabled extensions:'), extensions.disabled())) + doc = ''.join(rst) return doc +def optrst(options, verbose): + data = [] + multioccur = False + for option in options: + if len(option) == 5: + shortopt, longopt, default, desc, optlabel = option + else: + shortopt, longopt, default, desc = option + optlabel = _("VALUE") # default label + + if _("DEPRECATED") in desc and not verbose: + continue + + so = '' + if shortopt: + so = '-' + shortopt + lo = '--' + longopt + if default: + desc += _(" (default: %s)") % default + + if isinstance(default, list): + lo += " %s [+]" % optlabel + multioccur = True + elif (default is not None) and not isinstance(default, bool): + lo += " %s" % optlabel + + data.append((so, lo, desc)) + + rst = minirst.maketable(data, 1) + + if multioccur: + rst.append(_("\n[+] marked option can be specified multiple times\n")) + + return ''.join(rst) + +def topicmatch(kw): + """Return help topics matching kw. + + Returns {'section': [(name, summary), ...], ...} where section is + one of topics, commands, extensions, or extensioncommands. + """ + kw = encoding.lower(kw) + def lowercontains(container): + return kw in encoding.lower(container) # translated in helptable + results = {'topics': [], + 'commands': [], + 'extensions': [], + 'extensioncommands': [], + } + for names, header, doc in helptable: + if (sum(map(lowercontains, names)) + or lowercontains(header) + or lowercontains(doc())): + results['topics'].append((names[0], header)) + import commands # avoid cycle + for cmd, entry in commands.table.iteritems(): + if cmd.startswith('debug'): + continue + if len(entry) == 3: + summary = entry[2] + else: + summary = '' + # translate docs *before* searching there + docs = _(getattr(entry[0], '__doc__', None)) or '' + if kw in cmd or lowercontains(summary) or lowercontains(docs): + doclines = docs.splitlines() + if doclines: + summary = doclines[0] + cmdname = cmd.split('|')[0].lstrip('^') + results['commands'].append((cmdname, summary)) + for name, docs in itertools.chain( + extensions.enabled().iteritems(), + extensions.disabled().iteritems()): + # extensions.load ignores the UI argument + mod = extensions.load(None, name, '') + if lowercontains(name) or lowercontains(docs): + # extension docs are already translated + results['extensions'].append((name, docs.splitlines()[0])) + for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems(): + if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])): + cmdname = cmd.split('|')[0].lstrip('^') + if entry[0].__doc__: + cmddoc = gettext(entry[0].__doc__).splitlines()[0] + else: + cmddoc = _('(no help text available)') + results['extensioncommands'].append((cmdname, cmddoc)) + return results + def loaddoc(topic): """Return a delayed loader for help/topic.txt.""" @@ -112,5 +199,5 @@ addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols) addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals) addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols) -addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords) +addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords) addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/help/config.txt --- a/mercurial/help/config.txt Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/help/config.txt Wed Jul 18 19:08:25 2012 -0500 @@ -1377,6 +1377,12 @@ ``errorlog`` Where to output the error log. Default is stderr. +``comparisoncontext`` + Number of lines of context to show in side-by-side file comparison. If + negative or the value ``full``, whole files are shown. Default is 5. + This setting can be overridden by a ``context`` request parameter to the + ``comparison`` command, taking the same values. + ``hidden`` Whether to hide the repository in the hgwebdir index. Default is False. diff -r 98823bd0d697 -r a06e2681dd17 mercurial/help/hgignore.txt --- a/mercurial/help/hgignore.txt Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/help/hgignore.txt Wed Jul 18 19:08:25 2012 -0500 @@ -33,6 +33,11 @@ commands support the ``-I`` and ``-X`` options; see :hg:`help ` and :hg:`help patterns` for details. +Files that are already tracked are not affected by .hgignore, even +if they appear in .hgignore. An untracked file X can be explicitly +added with :hg:`add X`, even if X would be excluded by a pattern +in .hgignore. + Syntax ------ diff -r 98823bd0d697 -r a06e2681dd17 mercurial/help/hgweb.txt --- a/mercurial/help/hgweb.txt Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/help/hgweb.txt Wed Jul 18 19:08:25 2012 -0500 @@ -1,47 +1,50 @@ Mercurial's internal web server, hgweb, can serve either a single -repository, or a collection of them. In the latter case, a special -configuration file can be used to specify the repository paths to use -and global web configuration options. +repository, or a tree of repositories. In the second case, repository +paths and global options can be defined using a dedicated +configuration file common to :hg:`serve`, ``hgweb.wsgi``, +``hgweb.cgi`` and ``hgweb.fcgi``. -This file uses the same syntax as other Mercurial configuration files, -but only the following sections are recognized: +This file uses the same syntax as other Mercurial configuration files +but recognizes only the following sections: - web - paths - collections -The ``web`` section can specify all the settings described in the web -section of the hgrc(5) documentation. See :hg:`help config` for -information on where to find the manual page. +The ``web`` options are thorougly described in :hg:`help config`. + +The ``paths`` section maps URL paths to paths of repositories in the +filesystem. hgweb will not expose the filesystem directly - only +Mercurial repositories can be published and only according to the +configuration. -The ``paths`` section provides mappings of physical repository -paths to virtual ones. For instance:: +The left hand side is the path in the URL. Note that hgweb reserves +subpaths like ``rev`` or ``file``, try using different names for +nested repositories to avoid confusing effects. + +The right hand side is the path in the filesystem. If the specified +path ends with ``*`` or ``**`` the filesystem will be searched +recursively for repositories below that point. +With ``*`` it will not recurse into the repositories it finds (except for +``.hg/patches``). +With ``**`` it will also search inside repository working directories +and possibly find subrepositories. + +In this example:: [paths] - projects/a = /foo/bar - projects/b = /baz/quux - web/root = /real/root/* - / = /real/root2/* - virtual/root2 = /real/root2/** + /projects/a = /srv/tmprepos/a + /projects/b = c:/repos/b + / = /srv/repos/* + /user/bob = /home/bob/repos/** - The first two entries make two repositories in different directories appear under the same directory in the web interface -- The third entry maps every Mercurial repository found in '/real/root' - into 'web/root'. This format is preferred over the [collections] one, - since using absolute paths as configuration keys is not supported on every - platform (especially on Windows). -- The fourth entry is a special case mapping all repositories in - '/real/root2' in the root of the virtual directory. -- The fifth entry recursively finds all repositories under the real - root, and maps their relative paths under the virtual root. +- The third entry will publish every Mercurial repository found in + ``/srv/repos/``, for instance the repository ``/srv/repos/quux/`` + will appear as ``http://server/quux/`` +- The fourth entry will publish both ``http://server/user/bob/quux/`` + and ``http://server/user/bob/quux/testsubrepo/`` -The ``collections`` section provides mappings of trees of physical -repositories paths to virtual ones, though the paths syntax is generally -preferred. For instance:: - - [collections] - /foo = /foo - -Here, the left side will be stripped off all repositories found in the -right side. Thus ``/foo/bar`` and ``foo/quux/baz`` will be listed as -``bar`` and ``quux/baz`` respectively. +The ``collections`` section is deprecated and has been superseeded by +``paths``. diff -r 98823bd0d697 -r a06e2681dd17 mercurial/help/revisions.txt --- a/mercurial/help/revisions.txt Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/help/revisions.txt Wed Jul 18 19:08:25 2012 -0500 @@ -12,13 +12,13 @@ identifier. A short-form identifier is only valid if it is the prefix of exactly one full-length identifier. -Any other string is treated as a tag or branch name. A tag name is a -symbolic name associated with a revision identifier. A branch name -denotes the tipmost revision of that branch. Tag and branch names must -not contain the ":" character. +Any other string is treated as a bookmark, tag, or branch name. A +bookmark is a movable pointer to a revision. A tag is a permanent name +associated with a revision. A branch name denotes the tipmost revision +of that branch. Bookmark, tag, and branch names must not contain the ":" +character. -The reserved name "tip" is a special tag that always identifies the -most recent revision. +The reserved name "tip" always identifies the most recent revision. The reserved name "null" indicates the null revision. This is the revision of an empty repository, and the parent of revision 0. diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hg.py --- a/mercurial/hg.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hg.py Wed Jul 18 19:08:25 2012 -0500 @@ -9,7 +9,7 @@ from i18n import _ from lock import release from node import hex, nullid -import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks +import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks import lock, util, extensions, error, node, scmutil import cmdutil, discovery import merge as mergemod @@ -20,21 +20,22 @@ path = util.expandpath(util.urllocalpath(path)) return (os.path.isfile(path) and bundlerepo or localrepo) -def addbranchrevs(lrepo, repo, branches, revs): +def addbranchrevs(lrepo, other, branches, revs): + peer = other.peer() # a courtesy to callers using a localrepo for other hashbranch, branches = branches if not hashbranch and not branches: return revs or None, revs and revs[0] or None revs = revs and list(revs) or [] - if not repo.capable('branchmap'): + if not peer.capable('branchmap'): if branches: raise util.Abort(_("remote branch lookup not supported")) revs.append(hashbranch) return revs, revs[0] - branchmap = repo.branchmap() + branchmap = peer.branchmap() def primary(branch): if branch == '.': - if not lrepo or not lrepo.local(): + if not lrepo: raise util.Abort(_("dirstate branch not accessible")) branch = lrepo.dirstate.branch() if branch in branchmap: @@ -64,9 +65,9 @@ schemes = { 'bundle': bundlerepo, 'file': _local, - 'http': httprepo, - 'https': httprepo, - 'ssh': sshrepo, + 'http': httppeer, + 'https': httppeer, + 'ssh': sshpeer, 'static-http': statichttprepo, } @@ -88,20 +89,29 @@ return False return repo.local() -def repository(ui, path='', create=False): +def _peerorrepo(ui, path, create=False): """return a repository object for the specified path""" - repo = _peerlookup(path).instance(ui, path, create) - ui = getattr(repo, "ui", ui) + obj = _peerlookup(path).instance(ui, path, create) + ui = getattr(obj, "ui", ui) for name, module in extensions.extensions(): hook = getattr(module, 'reposetup', None) if hook: - hook(ui, repo) + hook(ui, obj) + return obj + +def repository(ui, path='', create=False): + """return a repository object for the specified path""" + peer = _peerorrepo(ui, path, create) + repo = peer.local() + if not repo: + raise util.Abort(_("repository '%s' is not local") % + (path or peer.url())) return repo def peer(uiorrepo, opts, path, create=False): '''return a repository peer for the specified path''' rui = remoteui(uiorrepo, opts) - return repository(rui, path, create) + return _peerorrepo(rui, path, create).peer() def defaultdest(source): '''return default destination of clone if none is given''' @@ -124,7 +134,7 @@ srcrepo = repository(ui, source) rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None) else: - srcrepo = source + srcrepo = source.local() origsource = source = srcrepo.url() checkout = None @@ -203,7 +213,7 @@ else: ui.debug("copied %d files\n" % num) return destlock - except: + except: # re-raises release(destlock) raise @@ -213,7 +223,7 @@ Create a copy of an existing repository in a new directory. The source and destination are URLs, as passed to the repository - function. Returns a pair of repository objects, the source and + function. Returns a pair of repository peers, the source and newly created destination. The location of the source is added to the new repository's @@ -247,12 +257,12 @@ if isinstance(source, str): origsource = ui.expandpath(source) source, branch = parseurl(origsource, branch) - srcrepo = repository(remoteui(ui, peeropts), source) + srcpeer = peer(ui, peeropts, source) else: - srcrepo = source + srcpeer = source.peer() # in case we were called with a localrepo branch = (None, branch or []) - origsource = source = srcrepo.url() - rev, checkout = addbranchrevs(srcrepo, srcrepo, branch, rev) + origsource = source = srcpeer.url() + rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev) if dest is None: dest = defaultdest(source) @@ -263,6 +273,8 @@ dest = util.urllocalpath(dest) source = util.urllocalpath(source) + if not dest: + raise util.Abort(_("empty destination path is not valid")) if os.path.exists(dest): if not os.path.isdir(dest): raise util.Abort(_("destination '%s' already exists") % dest) @@ -280,6 +292,7 @@ self.rmtree(self.dir_, True) srclock = destlock = dircleanup = None + srcrepo = srcpeer.local() try: abspath = origsource if islocal(origsource): @@ -289,7 +302,8 @@ dircleanup = DirCleanup(dest) copy = False - if srcrepo.cancopy() and islocal(dest) and not srcrepo.revs("secret()"): + if (srcrepo and srcrepo.cancopy() and islocal(dest) + and not srcrepo.revs("secret()")): copy = not pull and not rev if copy: @@ -324,13 +338,12 @@ # we need to re-init the repo after manually copying the data # into it - destrepo = repository(remoteui(ui, peeropts), dest) + destpeer = peer(ui, peeropts, dest) srcrepo.hook('outgoing', source='clone', node=node.hex(node.nullid)) else: try: - destrepo = repository(remoteui(ui, peeropts), dest, - create=True) + destpeer = peer(ui, peeropts, dest, create=True) except OSError, inst: if inst.errno == errno.EEXIST: dircleanup.close() @@ -340,16 +353,16 @@ revs = None if rev: - if not srcrepo.capable('lookup'): + if not srcpeer.capable('lookup'): raise util.Abort(_("src repository does not support " "revision lookup and so doesn't " "support clone by revision")) - revs = [srcrepo.lookup(r) for r in rev] + revs = [srcpeer.lookup(r) for r in rev] checkout = revs[0] - if destrepo.local(): - destrepo.clone(srcrepo, heads=revs, stream=stream) - elif srcrepo.local(): - srcrepo.push(destrepo, revs=revs) + if destpeer.local(): + destpeer.local().clone(srcpeer, heads=revs, stream=stream) + elif srcrepo: + srcrepo.push(destpeer, revs=revs) else: raise util.Abort(_("clone from remote to remote not supported")) @@ -357,8 +370,9 @@ dircleanup.close() # clone all bookmarks except divergent ones - if destrepo.local() and srcrepo.capable("pushkey"): - rb = srcrepo.listkeys('bookmarks') + destrepo = destpeer.local() + if destrepo and srcpeer.capable("pushkey"): + rb = srcpeer.listkeys('bookmarks') for k, n in rb.iteritems(): try: m = destrepo.lookup(n) @@ -367,11 +381,11 @@ pass if rb: bookmarks.write(destrepo) - elif srcrepo.local() and destrepo.capable("pushkey"): + elif srcrepo and destpeer.capable("pushkey"): for k, n in srcrepo._bookmarks.iteritems(): - destrepo.pushkey('bookmarks', k, '', hex(n)) + destpeer.pushkey('bookmarks', k, '', hex(n)) - if destrepo.local(): + if destrepo: fp = destrepo.opener("hgrc", "w", text=True) fp.write("[paths]\n") u = util.url(abspath) @@ -384,9 +398,7 @@ if update: if update is not True: - checkout = update - if srcrepo.local(): - checkout = srcrepo.lookup(update) + checkout = srcrepo.lookup(update) for test in (checkout, 'default', 'tip'): if test is None: continue @@ -399,13 +411,13 @@ destrepo.ui.status(_("updating to branch %s\n") % bn) _update(destrepo, uprev) - return srcrepo, destrepo + return srcpeer, destpeer finally: release(srclock, destlock) if dircleanup is not None: dircleanup.cleanup() - if srcrepo is not None: - srcrepo.close() + if srcpeer is not None: + srcpeer.close() def _showstats(repo, stats): repo.ui.status(_("%d files updated, %d files merged, " @@ -506,7 +518,7 @@ ui.status(_('comparing with %s\n') % util.hidepassword(dest)) revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev')) if revs: - revs = [repo.lookup(rev) for rev in revs] + revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)] other = peer(repo, opts, dest) outgoing = discovery.findcommonoutgoing(repo, other, revs, diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hgweb/common.py --- a/mercurial/hgweb/common.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hgweb/common.py Wed Jul 18 19:08:25 2012 -0500 @@ -95,7 +95,7 @@ def __getattr__(self, attr): if attr in ('close', 'readline', 'readlines', '__iter__'): return getattr(self.f, attr) - raise AttributeError() + raise AttributeError def _statusmessage(code): from BaseHTTPServer import BaseHTTPRequestHandler diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hgweb/hgweb_mod.py Wed Jul 18 19:08:25 2012 -0500 @@ -36,7 +36,7 @@ self.repo = repo self.repo.ui.setconfig('ui', 'report_untrusted', 'off') - self.repo.ui.setconfig('ui', 'interactive', 'off') + self.repo.ui.setconfig('ui', 'nontty', 'true') hook.redirect(True) self.mtime = -1 self.size = -1 @@ -73,7 +73,8 @@ self.repo = hg.repository(self.repo.ui, self.repo.root) self.maxchanges = int(self.config("web", "maxchanges", 10)) self.stripecount = int(self.config("web", "stripes", 1)) - self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) + self.maxshortchanges = int(self.config("web", "maxshortchanges", + 60)) self.maxfiles = int(self.config("web", "maxfiles", 10)) self.allowpull = self.configbool("web", "allowpull", True) encoding.encoding = self.config("web", "encoding", diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hgweb/hgwebdir_mod.py Wed Jul 18 19:08:25 2012 -0500 @@ -23,10 +23,10 @@ repos = [] for prefix, root in cleannames(paths): roothead, roottail = os.path.split(root) - # "foo = /bar/*" makes every subrepo of /bar/ to be - # mounted as foo/subrepo - # and "foo = /bar/**" also recurses into the subdirectories, - # remember to use it without working dir. + # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below + # /bar/ be served as as foo/N . + # '*' will not search inside dirs with .hg (except .hg/patches), + # '**' will search inside dirs with .hg (and thus also find subrepos). try: recurse = {'*': False, '**': True}[roottail] except KeyError: @@ -97,7 +97,7 @@ else: u = ui.ui() u.setconfig('ui', 'report_untrusted', 'off') - u.setconfig('ui', 'interactive', 'off') + u.setconfig('ui', 'nontty', 'true') if not isinstance(self.conf, (dict, list, tuple)): map = {'paths': 'hgweb-paths'} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hgweb/webcommands.py Wed Jul 18 19:08:25 2012 -0500 @@ -22,7 +22,7 @@ __all__ = [ 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev', 'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff', - 'annotate', 'filelog', 'archive', 'static', 'graph', 'help', + 'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help', ] def log(web, req, tmpl): @@ -441,7 +441,7 @@ tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems()) heads = web.repo.heads() parity = paritygen(web.stripecount) - sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev()) + sortkey = lambda ctx: (not ctx.closesbranch(), ctx.rev()) def entries(limit, **map): count = 0 @@ -586,6 +586,31 @@ diff = filediff +def comparison(web, req, tmpl): + ctx = webutil.changectx(web.repo, req) + path = webutil.cleanpath(web.repo, req.form['file'][0]) + rename = path in ctx and webutil.renamelink(ctx[path]) or [] + + parsecontext = lambda v: v == 'full' and -1 or int(v) + if 'context' in req.form: + context = parsecontext(req.form['context'][0]) + else: + context = parsecontext(web.config('web', 'comparisoncontext', '5')) + + comparison = webutil.compare(tmpl, ctx, path, context) + return tmpl('filecomparison', + file=path, + node=hex(ctx.node()), + rev=ctx.rev(), + date=ctx.date(), + desc=ctx.description(), + author=ctx.user(), + rename=rename, + branch=webutil.nodebranchnodefault(ctx), + parent=webutil.parents(ctx), + child=webutil.children(ctx), + comparison=comparison) + def annotate(web, req, tmpl): fctx = webutil.filectx(web.repo, req) f = fctx.path() @@ -784,24 +809,76 @@ dag = graphmod.dagwalker(web.repo, range(startrev, downrev - 1, -1)) tree = list(graphmod.colored(dag, web.repo)) - canvasheight = (len(tree) + 1) * bg_height - 27 - data = [] - for (id, type, ctx, vtx, edges) in tree: - if type != graphmod.CHANGESET: - continue - node = str(ctx) - age = templatefilters.age(ctx.date()) - desc = templatefilters.firstline(ctx.description()) - desc = cgi.escape(templatefilters.nonempty(desc)) - user = cgi.escape(templatefilters.person(ctx.user())) - branch = ctx.branch() - branch = branch, web.repo.branchtags().get(branch) == ctx.node() - data.append((node, vtx, edges, desc, user, age, branch, ctx.tags(), - ctx.bookmarks())) + + def getcolumns(tree): + cols = 0 + for (id, type, ctx, vtx, edges) in tree: + if type != graphmod.CHANGESET: + continue + cols = max(cols, max([edge[0] for edge in edges] or [0]), + max([edge[1] for edge in edges] or [0])) + return cols + + def graphdata(usetuples, **map): + data = [] + + row = 0 + for (id, type, ctx, vtx, edges) in tree: + if type != graphmod.CHANGESET: + continue + node = str(ctx) + age = templatefilters.age(ctx.date()) + desc = templatefilters.firstline(ctx.description()) + desc = cgi.escape(templatefilters.nonempty(desc)) + user = cgi.escape(templatefilters.person(ctx.user())) + branch = ctx.branch() + try: + branchnode = web.repo.branchtip(branch) + except error.RepoLookupError: + branchnode = None + branch = branch, branchnode == ctx.node() + + if usetuples: + data.append((node, vtx, edges, desc, user, age, branch, + ctx.tags(), ctx.bookmarks())) + else: + edgedata = [dict(col=edge[0], nextcol=edge[1], + color=(edge[2] - 1) % 6 + 1, + width=edge[3], bcolor=edge[4]) + for edge in edges] + + data.append( + dict(node=node, + col=vtx[0], + color=(vtx[1] - 1) % 6 + 1, + edges=edgedata, + row=row, + nextrow=row + 1, + desc=desc, + user=user, + age=age, + bookmarks=webutil.nodebookmarksdict( + web.repo, ctx.node()), + branches=webutil.nodebranchdict(web.repo, ctx), + inbranch=webutil.nodeinbranch(web.repo, ctx), + tags=webutil.nodetagsdict(web.repo, ctx.node()))) + + row += 1 + + return data + + cols = getcolumns(tree) + rows = len(tree) + canvasheight = (rows + 1) * bg_height - 27 return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev, lessvars=lessvars, morevars=morevars, downrev=downrev, - canvasheight=canvasheight, jsdata=data, bg_height=bg_height, + cols=cols, rows=rows, + canvaswidth=(cols + 1) * bg_height, + truecanvasheight=rows * bg_height, + canvasheight=canvasheight, bg_height=bg_height, + jsdata=lambda **x: graphdata(True, **x), + nodes=lambda **x: graphdata(False, **x), node=revnode_hex, changenav=changenav) def _getdoc(e): @@ -850,6 +927,7 @@ u = webutil.wsgiui() u.pushbuffer() + u.verbose = True try: commands.help_(u, topicname) except error.UnknownCommand: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hgweb/webutil.py Wed Jul 18 19:08:25 2012 -0500 @@ -6,10 +6,11 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import os, copy +import os, mimetypes, copy from mercurial import match, patch, scmutil, error, ui, util from mercurial.i18n import _ from mercurial.node import hex, nullid +import difflib def up(p): if p[0] != "/": @@ -98,16 +99,23 @@ branches = [] branch = ctx.branch() # If this is an empty repo, ctx.node() == nullid, - # ctx.branch() == 'default', but branchtags() is - # an empty dict. Using dict.get avoids a traceback. - if repo.branchtags().get(branch) == ctx.node(): + # ctx.branch() == 'default'. + try: + branchnode = repo.branchtip(branch) + except error.RepoLookupError: + branchnode = None + if branchnode == ctx.node(): branches.append({"name": branch}) return branches def nodeinbranch(repo, ctx): branches = [] branch = ctx.branch() - if branch != 'default' and repo.branchtags().get(branch) != ctx.node(): + try: + branchnode = repo.branchtip(branch) + except error.RepoLookupError: + branchnode = None + if branch != 'default' and branchnode != ctx.node(): branches.append({"name": branch}) return branches @@ -213,6 +221,92 @@ yield tmpl('diffblock', parity=parity.next(), blockno=blockno, lines=prettyprintlines(''.join(block), blockno)) +def compare(tmpl, ctx, path, context): + '''Generator function that provides side-by-side comparison data.''' + + def filelines(f): + if util.binary(f.data()): + mt = mimetypes.guess_type(f.path())[0] + if not mt: + mt = 'application/octet-stream' + return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))] + return f.data().splitlines() + + def compline(type, leftlineno, leftline, rightlineno, rightline): + lineid = leftlineno and ("l%s" % leftlineno) or '' + lineid += rightlineno and ("r%s" % rightlineno) or '' + return tmpl('comparisonline', + type=type, + lineid=lineid, + leftlinenumber="% 6s" % (leftlineno or ''), + leftline=leftline or '', + rightlinenumber="% 6s" % (rightlineno or ''), + rightline=rightline or '') + + def getblock(opcodes): + for type, llo, lhi, rlo, rhi in opcodes: + len1 = lhi - llo + len2 = rhi - rlo + count = min(len1, len2) + for i in xrange(count): + yield compline(type=type, + leftlineno=llo + i + 1, + leftline=leftlines[llo + i], + rightlineno=rlo + i + 1, + rightline=rightlines[rlo + i]) + if len1 > len2: + for i in xrange(llo + count, lhi): + yield compline(type=type, + leftlineno=i + 1, + leftline=leftlines[i], + rightlineno=None, + rightline=None) + elif len2 > len1: + for i in xrange(rlo + count, rhi): + yield compline(type=type, + leftlineno=None, + leftline=None, + rightlineno=i + 1, + rightline=rightlines[i]) + + if path in ctx: + fctx = ctx[path] + rightrev = fctx.filerev() + rightnode = fctx.filenode() + rightlines = filelines(fctx) + parents = fctx.parents() + if not parents: + leftrev = -1 + leftnode = nullid + leftlines = () + else: + pfctx = parents[0] + leftrev = pfctx.filerev() + leftnode = pfctx.filenode() + leftlines = filelines(pfctx) + else: + rightrev = -1 + rightnode = nullid + rightlines = () + fctx = ctx.parents()[0][path] + leftrev = fctx.filerev() + leftnode = fctx.filenode() + leftlines = filelines(fctx) + + s = difflib.SequenceMatcher(None, leftlines, rightlines) + if context < 0: + blocks = [tmpl('comparisonblock', lines=getblock(s.get_opcodes()))] + else: + blocks = (tmpl('comparisonblock', lines=getblock(oc)) + for oc in s.get_grouped_opcodes(n=context)) + + yield tmpl('comparison', + leftrev=leftrev, + leftnode=hex(leftnode), + rightrev=rightrev, + rightnode=hex(rightnode), + blocks=blocks) + def diffstatgen(ctx): '''Generator function that provides the diffstat data.''' diff -r 98823bd0d697 -r a06e2681dd17 mercurial/hook.py --- a/mercurial/hook.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/hook.py Wed Jul 18 19:08:25 2012 -0500 @@ -169,7 +169,11 @@ path = util.expandpath(path) if repo: path = os.path.join(repo.root, path) - mod = extensions.loadpath(path, 'hghook.%s' % hname) + try: + mod = extensions.loadpath(path, 'hghook.%s' % hname) + except Exception: + ui.write(_("loading %s hook failed:\n") % hname) + raise hookfn = getattr(mod, cmd) else: hookfn = cmd[7:].strip() diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/__init__.py --- a/mercurial/httpclient/__init__.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/httpclient/__init__.py Wed Jul 18 19:08:25 2012 -0500 @@ -45,6 +45,7 @@ import select import socket +import _readers import socketutil logger = logging.getLogger(__name__) @@ -54,8 +55,6 @@ HTTP_VER_1_0 = 'HTTP/1.0' HTTP_VER_1_1 = 'HTTP/1.1' -_LEN_CLOSE_IS_END = -1 - OUTGOING_BUFFER_SIZE = 1 << 15 INCOMING_BUFFER_SIZE = 1 << 20 @@ -83,23 +82,19 @@ The response will continue to load as available. If you need the complete response before continuing, check the .complete() method. """ - def __init__(self, sock, timeout): + def __init__(self, sock, timeout, method): self.sock = sock + self.method = method self.raw_response = '' - self._body = None self._headers_len = 0 - self._content_len = 0 self.headers = None self.will_close = False self.status_line = '' self.status = None + self.continued = False self.http_version = None self.reason = None - self._chunked = False - self._chunked_done = False - self._chunked_until_next = 0 - self._chunked_skip_bytes = 0 - self._chunked_preloaded_block = None + self._reader = None self._read_location = 0 self._eol = EOL @@ -117,11 +112,12 @@ socket is closed, this will nearly always return False, even in cases where all the data has actually been loaded. """ - if self._chunked: - return self._chunked_done - if self._content_len == _LEN_CLOSE_IS_END: - return False - return self._body is not None and len(self._body) >= self._content_len + if self._reader: + return self._reader.done() + + def _close(self): + if self._reader is not None: + self._reader._close() def readline(self): """Read a single line from the response body. @@ -129,30 +125,34 @@ This may block until either a line ending is found or the response is complete. """ - eol = self._body.find('\n', self._read_location) - while eol == -1 and not self.complete(): + # TODO: move this into the reader interface where it can be + # smarter (and probably avoid copies) + bytes = [] + while not bytes: + try: + bytes = [self._reader.read(1)] + except _readers.ReadNotReady: + self._select() + while bytes[-1] != '\n' and not self.complete(): self._select() - eol = self._body.find('\n', self._read_location) - if eol != -1: - eol += 1 - else: - eol = len(self._body) - data = self._body[self._read_location:eol] - self._read_location = eol - return data + bytes.append(self._reader.read(1)) + if bytes[-1] != '\n': + next = self._reader.read(1) + while next and next != '\n': + bytes.append(next) + next = self._reader.read(1) + bytes.append(next) + return ''.join(bytes) def read(self, length=None): # if length is None, unbounded read while (not self.complete() # never select on a finished read and (not length # unbounded, so we wait for complete() - or (self._read_location + length) > len(self._body))): + or length > self._reader.available_data)): self._select() if not length: - length = len(self._body) - self._read_location - elif len(self._body) < (self._read_location + length): - length = len(self._body) - self._read_location - r = self._body[self._read_location:self._read_location + length] - self._read_location += len(r) + length = self._reader.available_data + r = self._reader.read(length) if self.complete() and self.will_close: self.sock.close() return r @@ -160,93 +160,35 @@ def _select(self): r, _, _ = select.select([self.sock], [], [], self._timeout) if not r: - # socket was not readable. If the response is not complete - # and we're not a _LEN_CLOSE_IS_END response, raise a timeout. - # If we are a _LEN_CLOSE_IS_END response and we have no data, - # raise a timeout. - if not (self.complete() or - (self._content_len == _LEN_CLOSE_IS_END and self._body)): + # socket was not readable. If the response is not + # complete, raise a timeout. + if not self.complete(): logger.info('timed out with timeout of %s', self._timeout) raise HTTPTimeoutException('timeout reading data') - logger.info('cl: %r body: %r', self._content_len, self._body) try: data = self.sock.recv(INCOMING_BUFFER_SIZE) - # If the socket was readable and no data was read, that - # means the socket was closed. If this isn't a - # _CLOSE_IS_END socket, then something is wrong if we're - # here (we shouldn't enter _select() if the response is - # complete), so abort. - if not data and self._content_len != _LEN_CLOSE_IS_END: - raise HTTPRemoteClosedError( - 'server appears to have closed the socket mid-response') except socket.sslerror, e: if e.args[0] != socket.SSL_ERROR_WANT_READ: raise logger.debug('SSL_WANT_READ in _select, should retry later') return True logger.debug('response read %d data during _select', len(data)) + # If the socket was readable and no data was read, that means + # the socket was closed. Inform the reader (if any) so it can + # raise an exception if this is an invalid situation. if not data: - if self.headers and self._content_len == _LEN_CLOSE_IS_END: - self._content_len = len(self._body) + if self._reader: + self._reader._close() return False else: self._load_response(data) return True - def _chunked_parsedata(self, data): - if self._chunked_preloaded_block: - data = self._chunked_preloaded_block + data - self._chunked_preloaded_block = None - while data: - logger.debug('looping with %d data remaining', len(data)) - # Slice out anything we should skip - if self._chunked_skip_bytes: - if len(data) <= self._chunked_skip_bytes: - self._chunked_skip_bytes -= len(data) - data = '' - break - else: - data = data[self._chunked_skip_bytes:] - self._chunked_skip_bytes = 0 - - # determine how much is until the next chunk - if self._chunked_until_next: - amt = self._chunked_until_next - logger.debug('reading remaining %d of existing chunk', amt) - self._chunked_until_next = 0 - body = data - else: - try: - amt, body = data.split(self._eol, 1) - except ValueError: - self._chunked_preloaded_block = data - logger.debug('saving %r as a preloaded block for chunked', - self._chunked_preloaded_block) - return - amt = int(amt, base=16) - logger.debug('reading chunk of length %d', amt) - if amt == 0: - self._chunked_done = True - - # read through end of what we have or the chunk - self._body += body[:amt] - if len(body) >= amt: - data = body[amt:] - self._chunked_skip_bytes = len(self._eol) - else: - self._chunked_until_next = amt - len(body) - self._chunked_skip_bytes = 0 - data = '' - def _load_response(self, data): - if self._chunked: - self._chunked_parsedata(data) - return - elif self._body is not None: - self._body += data - return - - # We haven't seen end of headers yet + # Being here implies we're not at the end of the headers yet, + # since at the end of this method if headers were completely + # loaded we replace this method with the load() method of the + # reader we created. self.raw_response += data # This is a bogus server with bad line endings if self._eol not in self.raw_response: @@ -270,6 +212,7 @@ http_ver, status = hdrs.split(' ', 1) if status.startswith('100'): self.raw_response = body + self.continued = True logger.debug('continue seen, setting body to %r', body) return @@ -289,23 +232,46 @@ if self._eol != EOL: hdrs = hdrs.replace(self._eol, '\r\n') headers = rfc822.Message(cStringIO.StringIO(hdrs)) + content_len = None if HDR_CONTENT_LENGTH in headers: - self._content_len = int(headers[HDR_CONTENT_LENGTH]) + content_len = int(headers[HDR_CONTENT_LENGTH]) if self.http_version == HTTP_VER_1_0: self.will_close = True elif HDR_CONNECTION_CTRL in headers: self.will_close = ( headers[HDR_CONNECTION_CTRL].lower() == CONNECTION_CLOSE) - if self._content_len == 0: - self._content_len = _LEN_CLOSE_IS_END if (HDR_XFER_ENCODING in headers and headers[HDR_XFER_ENCODING].lower() == XFER_ENCODING_CHUNKED): - self._body = '' - self._chunked_parsedata(body) - self._chunked = True - if self._body is None: - self._body = body + self._reader = _readers.ChunkedReader(self._eol) + logger.debug('using a chunked reader') + else: + # HEAD responses are forbidden from returning a body, and + # it's implausible for a CONNECT response to use + # close-is-end logic for an OK response. + if (self.method == 'HEAD' or + (self.method == 'CONNECT' and content_len is None)): + content_len = 0 + if content_len is not None: + logger.debug('using a content-length reader with length %d', + content_len) + self._reader = _readers.ContentLengthReader(content_len) + else: + # Response body had no length specified and is not + # chunked, so the end of the body will only be + # identifiable by the termination of the socket by the + # server. My interpretation of the spec means that we + # are correct in hitting this case if + # transfer-encoding, content-length, and + # connection-control were left unspecified. + self._reader = _readers.CloseIsEndReader() + logger.debug('using a close-is-end reader') + self.will_close = True + + if body: + self._reader._load(body) + logger.debug('headers complete') self.headers = headers + self._load_response = self._reader._load class HTTPConnection(object): @@ -382,13 +348,14 @@ {}, HTTP_VER_1_0) sock.send(data) sock.setblocking(0) - r = self.response_class(sock, self.timeout) + r = self.response_class(sock, self.timeout, 'CONNECT') timeout_exc = HTTPTimeoutException( 'Timed out waiting for CONNECT response from proxy') while not r.complete(): try: if not r._select(): - raise timeout_exc + if not r.complete(): + raise timeout_exc except HTTPTimeoutException: # This raise/except pattern looks goofy, but # _select can raise the timeout as well as the @@ -405,6 +372,10 @@ else: sock = socketutil.create_connection((self.host, self.port)) if self.ssl: + # This is the default, but in the case of proxied SSL + # requests the proxy logic above will have cleared + # blocking mode, so reenable it just to be safe. + sock.setblocking(1) logger.debug('wrapping socket for ssl with options %r', self.ssl_opts) sock = socketutil.wrap_socket(sock, **self.ssl_opts) @@ -527,7 +498,7 @@ out = outgoing_headers or body blocking_on_continue = False if expect_continue and not outgoing_headers and not ( - response and response.headers): + response and (response.headers or response.continued)): logger.info( 'waiting up to %s seconds for' ' continue response from server', @@ -550,11 +521,6 @@ 'server, optimistically sending request body') else: raise HTTPTimeoutException('timeout sending data') - # TODO exceptional conditions with select? (what are those be?) - # TODO if the response is loading, must we finish sending at all? - # - # Certainly not if it's going to close the connection and/or - # the response is already done...I think. was_first = first # incoming data @@ -572,11 +538,11 @@ logger.info('socket appears closed in read') self.sock = None self._current_response = None + if response is not None: + response._close() # This if/elif ladder is a bit subtle, # comments in each branch should help. - if response is not None and ( - response.complete() or - response._content_len == _LEN_CLOSE_IS_END): + if response is not None and response.complete(): # Server responded completely and then # closed the socket. We should just shut # things down and let the caller get their @@ -605,7 +571,7 @@ 'response was missing or incomplete!') logger.debug('read %d bytes in request()', len(data)) if response is None: - response = self.response_class(r[0], self.timeout) + response = self.response_class(r[0], self.timeout, method) response._load_response(data) # Jump to the next select() call so we load more # data if the server is still sending us content. @@ -613,10 +579,6 @@ except socket.error, e: if e[0] != errno.EPIPE and not was_first: raise - if (response._content_len - and response._content_len != _LEN_CLOSE_IS_END): - outgoing_headers = sent_data + outgoing_headers - reconnect('read') # outgoing data if w and out: @@ -661,7 +623,7 @@ # close if the server response said to or responded before eating # the whole request if response is None: - response = self.response_class(self.sock, self.timeout) + response = self.response_class(self.sock, self.timeout, method) complete = response.complete() data_left = bool(outgoing_headers or body) if data_left: @@ -679,7 +641,8 @@ raise httplib.ResponseNotReady() r = self._current_response while r.headers is None: - r._select() + if not r._select() and not r.complete(): + raise _readers.HTTPRemoteClosedError() if r.will_close: self.sock = None self._current_response = None @@ -705,7 +668,7 @@ class HTTPStateError(httplib.HTTPException): """Invalid internal state encountered.""" - -class HTTPRemoteClosedError(httplib.HTTPException): - """The server closed the remote socket in the middle of a response.""" +# Forward this exception type from _readers since it needs to be part +# of the public API. +HTTPRemoteClosedError = _readers.HTTPRemoteClosedError # no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/_readers.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/httpclient/_readers.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,195 @@ +# Copyright 2011, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"""Reader objects to abstract out different body response types. + +This module is package-private. It is not expected that these will +have any clients outside of httpplus. +""" + +import httplib +import itertools +import logging + +logger = logging.getLogger(__name__) + + +class ReadNotReady(Exception): + """Raised when read() is attempted but not enough data is loaded.""" + + +class HTTPRemoteClosedError(httplib.HTTPException): + """The server closed the remote socket in the middle of a response.""" + + +class AbstractReader(object): + """Abstract base class for response readers. + + Subclasses must implement _load, and should implement _close if + it's not an error for the server to close their socket without + some termination condition being detected during _load. + """ + def __init__(self): + self._finished = False + self._done_chunks = [] + + @property + def available_data(self): + return sum(map(len, self._done_chunks)) + + def done(self): + return self._finished + + def read(self, amt): + if self.available_data < amt and not self._finished: + raise ReadNotReady() + need = [amt] + def pred(s): + needed = need[0] > 0 + need[0] -= len(s) + return needed + blocks = list(itertools.takewhile(pred, self._done_chunks)) + self._done_chunks = self._done_chunks[len(blocks):] + over_read = sum(map(len, blocks)) - amt + if over_read > 0 and blocks: + logger.debug('need to reinsert %d data into done chunks', over_read) + last = blocks[-1] + blocks[-1], reinsert = last[:-over_read], last[-over_read:] + self._done_chunks.insert(0, reinsert) + result = ''.join(blocks) + assert len(result) == amt or (self._finished and len(result) < amt) + return result + + def _load(self, data): # pragma: no cover + """Subclasses must implement this. + + As data is available to be read out of this object, it should + be placed into the _done_chunks list. Subclasses should not + rely on data remaining in _done_chunks forever, as it may be + reaped if the client is parsing data as it comes in. + """ + raise NotImplementedError + + def _close(self): + """Default implementation of close. + + The default implementation assumes that the reader will mark + the response as finished on the _finished attribute once the + entire response body has been read. In the event that this is + not true, the subclass should override the implementation of + close (for example, close-is-end responses have to set + self._finished in the close handler.) + """ + if not self._finished: + raise HTTPRemoteClosedError( + 'server appears to have closed the socket mid-response') + + +class AbstractSimpleReader(AbstractReader): + """Abstract base class for simple readers that require no response decoding. + + Examples of such responses are Connection: Close (close-is-end) + and responses that specify a content length. + """ + def _load(self, data): + if data: + assert not self._finished, ( + 'tried to add data (%r) to a closed reader!' % data) + logger.debug('%s read an addtional %d data', self.name, len(data)) + self._done_chunks.append(data) + + +class CloseIsEndReader(AbstractSimpleReader): + """Reader for responses that specify Connection: Close for length.""" + name = 'close-is-end' + + def _close(self): + logger.info('Marking close-is-end reader as closed.') + self._finished = True + + +class ContentLengthReader(AbstractSimpleReader): + """Reader for responses that specify an exact content length.""" + name = 'content-length' + + def __init__(self, amount): + AbstractReader.__init__(self) + self._amount = amount + if amount == 0: + self._finished = True + self._amount_seen = 0 + + def _load(self, data): + AbstractSimpleReader._load(self, data) + self._amount_seen += len(data) + if self._amount_seen >= self._amount: + self._finished = True + logger.debug('content-length read complete') + + +class ChunkedReader(AbstractReader): + """Reader for chunked transfer encoding responses.""" + def __init__(self, eol): + AbstractReader.__init__(self) + self._eol = eol + self._leftover_skip_amt = 0 + self._leftover_data = '' + + def _load(self, data): + assert not self._finished, 'tried to add data to a closed reader!' + logger.debug('chunked read an addtional %d data', len(data)) + position = 0 + if self._leftover_data: + logger.debug('chunked reader trying to finish block from leftover data') + # TODO: avoid this string concatenation if possible + data = self._leftover_data + data + position = self._leftover_skip_amt + self._leftover_data = '' + self._leftover_skip_amt = 0 + datalen = len(data) + while position < datalen: + split = data.find(self._eol, position) + if split == -1: + self._leftover_data = data + self._leftover_skip_amt = position + return + amt = int(data[position:split], base=16) + block_start = split + len(self._eol) + # If the whole data chunk plus the eol trailer hasn't + # loaded, we'll wait for the next load. + if block_start + amt + len(self._eol) > len(data): + self._leftover_data = data + self._leftover_skip_amt = position + return + if amt == 0: + self._finished = True + logger.debug('closing chunked redaer due to chunk of length 0') + return + self._done_chunks.append(data[block_start:block_start + amt]) + position = block_start + amt + len(self._eol) +# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/__init__.py --- a/mercurial/httpclient/tests/__init__.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/simple_http_test.py --- a/mercurial/httpclient/tests/simple_http_test.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,401 +0,0 @@ -# Copyright 2010, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import socket -import unittest - -import http - -# relative import to ease embedding the library -import util - - -class SimpleHttpTest(util.HttpTestBase, unittest.TestCase): - - def _run_simple_test(self, host, server_data, expected_req, expected_data): - con = http.HTTPConnection(host) - con._connect() - con.sock.data = server_data - con.request('GET', '/') - - self.assertStringEqual(expected_req, con.sock.sent) - self.assertEqual(expected_data, con.getresponse().read()) - - def test_broken_data_obj(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - self.assertRaises(http.BadRequestData, - con.request, 'POST', '/', body=1) - - def test_no_keepalive_http_1_0(self): - expected_request_one = """GET /remote/.hg/requires HTTP/1.1 -Host: localhost:9999 -range: bytes=0- -accept-encoding: identity -accept: application/mercurial-0.1 -user-agent: mercurial/proto-1.0 - -""".replace('\n', '\r\n') - expected_response_headers = """HTTP/1.0 200 OK -Server: SimpleHTTP/0.6 Python/2.6.1 -Date: Sun, 01 May 2011 13:56:57 GMT -Content-type: application/octet-stream -Content-Length: 33 -Last-Modified: Sun, 01 May 2011 13:56:56 GMT - -""".replace('\n', '\r\n') - expected_response_body = """revlogv1 -store -fncache -dotencode -""" - con = http.HTTPConnection('localhost:9999') - con._connect() - con.sock.data = [expected_response_headers, expected_response_body] - con.request('GET', '/remote/.hg/requires', - headers={'accept-encoding': 'identity', - 'range': 'bytes=0-', - 'accept': 'application/mercurial-0.1', - 'user-agent': 'mercurial/proto-1.0', - }) - self.assertStringEqual(expected_request_one, con.sock.sent) - self.assertEqual(con.sock.closed, False) - self.assertNotEqual(con.sock.data, []) - self.assert_(con.busy()) - resp = con.getresponse() - self.assertStringEqual(resp.read(), expected_response_body) - self.failIf(con.busy()) - self.assertEqual(con.sock, None) - self.assertEqual(resp.sock.data, []) - self.assert_(resp.sock.closed) - - def test_multiline_header(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - con.sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Multiline: Value\r\n', - ' Rest of value\r\n', - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ] - con.request('GET', '/') - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - resp = con.getresponse() - self.assertEqual('1234567890', resp.read()) - self.assertEqual(['Value\n Rest of value'], - resp.headers.getheaders('multiline')) - # Socket should not be closed - self.assertEqual(resp.sock.closed, False) - self.assertEqual(con.sock.closed, False) - - def testSimpleRequest(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - con.sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'MultiHeader: Value\r\n' - 'MultiHeader: Other Value\r\n' - 'MultiHeader: One More!\r\n' - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ] - con.request('GET', '/') - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - resp = con.getresponse() - self.assertEqual('1234567890', resp.read()) - self.assertEqual(['Value', 'Other Value', 'One More!'], - resp.headers.getheaders('multiheader')) - self.assertEqual(['BogusServer 1.0'], - resp.headers.getheaders('server')) - - def testHeaderlessResponse(self): - con = http.HTTPConnection('1.2.3.4', use_ssl=False) - con._connect() - con.sock.data = ['HTTP/1.1 200 OK\r\n', - '\r\n' - '1234567890' - ] - con.request('GET', '/') - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - resp = con.getresponse() - self.assertEqual('1234567890', resp.read()) - self.assertEqual({}, dict(resp.headers)) - self.assertEqual(resp.status, 200) - - def testReadline(self): - con = http.HTTPConnection('1.2.3.4') - con._connect() - # make sure it trickles in one byte at a time - # so that we touch all the cases in readline - con.sock.data = list(''.join( - ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Connection: Close\r\n', - '\r\n' - '1\n2\nabcdefg\n4\n5'])) - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - con.request('GET', '/') - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - r = con.getresponse() - for expected in ['1\n', '2\n', 'abcdefg\n', '4\n', '5']: - actual = r.readline() - self.assertEqual(expected, actual, - 'Expected %r, got %r' % (expected, actual)) - - def testIPv6(self): - self._run_simple_test('[::1]:8221', - ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 10', - '\r\n\r\n' - '1234567890'], - ('GET / HTTP/1.1\r\n' - 'Host: [::1]:8221\r\n' - 'accept-encoding: identity\r\n\r\n'), - '1234567890') - self._run_simple_test('::2', - ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 10', - '\r\n\r\n' - '1234567890'], - ('GET / HTTP/1.1\r\n' - 'Host: ::2\r\n' - 'accept-encoding: identity\r\n\r\n'), - '1234567890') - self._run_simple_test('[::3]:443', - ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 10', - '\r\n\r\n' - '1234567890'], - ('GET / HTTP/1.1\r\n' - 'Host: ::3\r\n' - 'accept-encoding: identity\r\n\r\n'), - '1234567890') - - def testEarlyContinueResponse(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.data = ['HTTP/1.1 403 Forbidden\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 18', - '\r\n\r\n' - "You can't do that."] - expected_req = self.doPost(con, expect_body=False) - self.assertEqual(('1.2.3.4', 80), sock.sa) - self.assertStringEqual(expected_req, sock.sent) - self.assertEqual("You can't do that.", con.getresponse().read()) - self.assertEqual(sock.closed, True) - - def testDeniedAfterContinueTimeoutExpires(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.data = ['HTTP/1.1 403 Forbidden\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 18\r\n', - 'Connection: close', - '\r\n\r\n' - "You can't do that."] - sock.read_wait_sentinel = 'Dear server, send response!' - sock.close_on_empty = True - # send enough data out that we'll chunk it into multiple - # blocks and the socket will close before we can send the - # whole request. - post_body = ('This is some POST data\n' * 1024 * 32 + - 'Dear server, send response!\n' + - 'This is some POST data\n' * 1024 * 32) - expected_req = self.doPost(con, expect_body=False, - body_to_send=post_body) - self.assertEqual(('1.2.3.4', 80), sock.sa) - self.assert_('POST data\n' in sock.sent) - self.assert_('Dear server, send response!\n' in sock.sent) - # We expect not all of our data was sent. - self.assertNotEqual(sock.sent, expected_req) - self.assertEqual("You can't do that.", con.getresponse().read()) - self.assertEqual(sock.closed, True) - - def testPostData(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.read_wait_sentinel = 'POST data' - sock.early_data = ['HTTP/1.1 100 Co', 'ntinue\r\n\r\n'] - sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 16', - '\r\n\r\n', - "You can do that."] - expected_req = self.doPost(con, expect_body=True) - self.assertEqual(('1.2.3.4', 80), sock.sa) - self.assertEqual(expected_req, sock.sent) - self.assertEqual("You can do that.", con.getresponse().read()) - self.assertEqual(sock.closed, False) - - def testServerWithoutContinue(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.read_wait_sentinel = 'POST data' - sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 16', - '\r\n\r\n', - "You can do that."] - expected_req = self.doPost(con, expect_body=True) - self.assertEqual(('1.2.3.4', 80), sock.sa) - self.assertEqual(expected_req, sock.sent) - self.assertEqual("You can do that.", con.getresponse().read()) - self.assertEqual(sock.closed, False) - - def testServerWithSlowContinue(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.read_wait_sentinel = 'POST data' - sock.data = ['HTTP/1.1 100 ', 'Continue\r\n\r\n', - 'HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 16', - '\r\n\r\n', - "You can do that."] - expected_req = self.doPost(con, expect_body=True) - self.assertEqual(('1.2.3.4', 80), sock.sa) - self.assertEqual(expected_req, sock.sent) - resp = con.getresponse() - self.assertEqual("You can do that.", resp.read()) - self.assertEqual(200, resp.status) - self.assertEqual(sock.closed, False) - - def testSlowConnection(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - # simulate one byte arriving at a time, to check for various - # corner cases - con.sock.data = list('HTTP/1.1 200 OK\r\n' - 'Server: BogusServer 1.0\r\n' - 'Content-Length: 10' - '\r\n\r\n' - '1234567890') - con.request('GET', '/') - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - self.assertEqual('1234567890', con.getresponse().read()) - - def testTimeout(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - con.sock.data = [] - con.request('GET', '/') - self.assertRaises(http.HTTPTimeoutException, - con.getresponse) - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - - def test_conn_keep_alive_but_server_close_anyway(self): - sockets = [] - def closingsocket(*args, **kwargs): - s = util.MockSocket(*args, **kwargs) - sockets.append(s) - s.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Connection: Keep-Alive\r\n', - 'Content-Length: 16', - '\r\n\r\n', - 'You can do that.'] - s.close_on_empty = True - return s - - socket.socket = closingsocket - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - con.request('GET', '/') - r1 = con.getresponse() - r1.read() - self.assertFalse(con.sock.closed) - self.assert_(con.sock.remote_closed) - con.request('GET', '/') - self.assertEqual(2, len(sockets)) - - def test_server_closes_before_end_of_body(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - s = con.sock - s.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Connection: Keep-Alive\r\n', - 'Content-Length: 16', - '\r\n\r\n', - 'You can '] # Note: this is shorter than content-length - s.close_on_empty = True - con.request('GET', '/') - r1 = con.getresponse() - self.assertRaises(http.HTTPRemoteClosedError, r1.read) - - def test_no_response_raises_response_not_ready(self): - con = http.HTTPConnection('foo') - self.assertRaises(http.httplib.ResponseNotReady, con.getresponse) -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/test_bogus_responses.py --- a/mercurial/httpclient/tests/test_bogus_responses.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -# Copyright 2010, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Tests against malformed responses. - -Server implementations that respond with only LF instead of CRLF have -been observed. Checking against ones that use only CR is a hedge -against that potential insanit.y -""" -import unittest - -import http - -# relative import to ease embedding the library -import util - - -class SimpleHttpTest(util.HttpTestBase, unittest.TestCase): - - def bogusEOL(self, eol): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - con.sock.data = ['HTTP/1.1 200 OK%s' % eol, - 'Server: BogusServer 1.0%s' % eol, - 'Content-Length: 10', - eol * 2, - '1234567890'] - con.request('GET', '/') - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 80), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - self.assertEqual('1234567890', con.getresponse().read()) - - def testOnlyLinefeed(self): - self.bogusEOL('\n') - - def testOnlyCarriageReturn(self): - self.bogusEOL('\r') -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/test_chunked_transfer.py --- a/mercurial/httpclient/tests/test_chunked_transfer.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -# Copyright 2010, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import cStringIO -import unittest - -import http - -# relative import to ease embedding the library -import util - - -def chunkedblock(x, eol='\r\n'): - r"""Make a chunked transfer-encoding block. - - >>> chunkedblock('hi') - '2\r\nhi\r\n' - >>> chunkedblock('hi' * 10) - '14\r\nhihihihihihihihihihi\r\n' - >>> chunkedblock('hi', eol='\n') - '2\nhi\n' - """ - return ''.join((hex(len(x))[2:], eol, x, eol)) - - -class ChunkedTransferTest(util.HttpTestBase, unittest.TestCase): - def testChunkedUpload(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.read_wait_sentinel = '0\r\n\r\n' - sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 6', - '\r\n\r\n', - "Thanks"] - - zz = 'zz\n' - con.request('POST', '/', body=cStringIO.StringIO( - (zz * (0x8010 / 3)) + 'end-of-body')) - expected_req = ('POST / HTTP/1.1\r\n' - 'transfer-encoding: chunked\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - expected_req += chunkedblock('zz\n' * (0x8000 / 3) + 'zz') - expected_req += chunkedblock( - '\n' + 'zz\n' * ((0x1b - len('end-of-body')) / 3) + 'end-of-body') - expected_req += '0\r\n\r\n' - self.assertEqual(('1.2.3.4', 80), sock.sa) - self.assertStringEqual(expected_req, sock.sent) - self.assertEqual("Thanks", con.getresponse().read()) - self.assertEqual(sock.closed, False) - - def testChunkedDownload(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'transfer-encoding: chunked', - '\r\n\r\n', - chunkedblock('hi '), - chunkedblock('there'), - chunkedblock(''), - ] - con.request('GET', '/') - self.assertStringEqual('hi there', con.getresponse().read()) - - def testChunkedDownloadBadEOL(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.data = ['HTTP/1.1 200 OK\n', - 'Server: BogusServer 1.0\n', - 'transfer-encoding: chunked', - '\n\n', - chunkedblock('hi ', eol='\n'), - chunkedblock('there', eol='\n'), - chunkedblock('', eol='\n'), - ] - con.request('GET', '/') - self.assertStringEqual('hi there', con.getresponse().read()) - - def testChunkedDownloadPartialChunkBadEOL(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.data = ['HTTP/1.1 200 OK\n', - 'Server: BogusServer 1.0\n', - 'transfer-encoding: chunked', - '\n\n', - chunkedblock('hi ', eol='\n'), - ] + list(chunkedblock('there\n' * 5, eol='\n')) + [ - chunkedblock('', eol='\n')] - con.request('GET', '/') - self.assertStringEqual('hi there\nthere\nthere\nthere\nthere\n', - con.getresponse().read()) - - def testChunkedDownloadPartialChunk(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'transfer-encoding: chunked', - '\r\n\r\n', - chunkedblock('hi '), - ] + list(chunkedblock('there\n' * 5)) + [chunkedblock('')] - con.request('GET', '/') - self.assertStringEqual('hi there\nthere\nthere\nthere\nthere\n', - con.getresponse().read()) - - def testChunkedDownloadEarlyHangup(self): - con = http.HTTPConnection('1.2.3.4:80') - con._connect() - sock = con.sock - broken = chunkedblock('hi'*20)[:-1] - sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'transfer-encoding: chunked', - '\r\n\r\n', - broken, - ] - sock.close_on_empty = True - con.request('GET', '/') - resp = con.getresponse() - self.assertRaises(http.HTTPRemoteClosedError, resp.read) -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/test_proxy_support.py --- a/mercurial/httpclient/tests/test_proxy_support.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -# Copyright 2010, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest -import socket - -import http - -# relative import to ease embedding the library -import util - - -def make_preloaded_socket(data): - """Make a socket pre-loaded with data so it can be read during connect. - - Useful for https proxy tests because we have to read from the - socket during _connect rather than later on. - """ - def s(*args, **kwargs): - sock = util.MockSocket(*args, **kwargs) - sock.early_data = data[:] - return sock - return s - - -class ProxyHttpTest(util.HttpTestBase, unittest.TestCase): - - def _run_simple_test(self, host, server_data, expected_req, expected_data): - con = http.HTTPConnection(host) - con._connect() - con.sock.data = server_data - con.request('GET', '/') - - self.assertEqual(expected_req, con.sock.sent) - self.assertEqual(expected_data, con.getresponse().read()) - - def testSimpleRequest(self): - con = http.HTTPConnection('1.2.3.4:80', - proxy_hostport=('magicproxy', 4242)) - con._connect() - con.sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'MultiHeader: Value\r\n' - 'MultiHeader: Other Value\r\n' - 'MultiHeader: One More!\r\n' - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ] - con.request('GET', '/') - - expected_req = ('GET http://1.2.3.4/ HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('127.0.0.42', 4242), con.sock.sa) - self.assertStringEqual(expected_req, con.sock.sent) - resp = con.getresponse() - self.assertEqual('1234567890', resp.read()) - self.assertEqual(['Value', 'Other Value', 'One More!'], - resp.headers.getheaders('multiheader')) - self.assertEqual(['BogusServer 1.0'], - resp.headers.getheaders('server')) - - def testSSLRequest(self): - con = http.HTTPConnection('1.2.3.4:443', - proxy_hostport=('magicproxy', 4242)) - socket.socket = make_preloaded_socket( - ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 10\r\n', - '\r\n' - '1234567890']) - con._connect() - con.sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ] - connect_sent = con.sock.sent - con.sock.sent = '' - con.request('GET', '/') - - expected_connect = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n' - '\r\n') - expected_request = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('127.0.0.42', 4242), con.sock.sa) - self.assertStringEqual(expected_connect, connect_sent) - self.assertStringEqual(expected_request, con.sock.sent) - resp = con.getresponse() - self.assertEqual(resp.status, 200) - self.assertEqual('1234567890', resp.read()) - self.assertEqual(['BogusServer 1.0'], - resp.headers.getheaders('server')) - - def testSSLProxyFailure(self): - con = http.HTTPConnection('1.2.3.4:443', - proxy_hostport=('magicproxy', 4242)) - socket.socket = make_preloaded_socket( - ['HTTP/1.1 407 Proxy Authentication Required\r\n\r\n']) - self.assertRaises(http.HTTPProxyConnectFailedException, con._connect) - self.assertRaises(http.HTTPProxyConnectFailedException, - con.request, 'GET', '/') -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/test_ssl.py --- a/mercurial/httpclient/tests/test_ssl.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -# Copyright 2011, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import unittest - -import http - -# relative import to ease embedding the library -import util - - - -class HttpSslTest(util.HttpTestBase, unittest.TestCase): - def testSslRereadRequired(self): - con = http.HTTPConnection('1.2.3.4:443') - con._connect() - # extend the list instead of assign because of how - # MockSSLSocket works. - con.sock.data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'MultiHeader: Value\r\n' - 'MultiHeader: Other Value\r\n' - 'MultiHeader: One More!\r\n' - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ] - con.request('GET', '/') - - expected_req = ('GET / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'accept-encoding: identity\r\n\r\n') - - self.assertEqual(('1.2.3.4', 443), con.sock.sa) - self.assertEqual(expected_req, con.sock.sent) - resp = con.getresponse() - self.assertEqual('1234567890', resp.read()) - self.assertEqual(['Value', 'Other Value', 'One More!'], - resp.headers.getheaders('multiheader')) - self.assertEqual(['BogusServer 1.0'], - resp.headers.getheaders('server')) - - def testSslRereadInEarlyResponse(self): - con = http.HTTPConnection('1.2.3.4:443') - con._connect() - con.sock.early_data = ['HTTP/1.1 200 OK\r\n', - 'Server: BogusServer 1.0\r\n', - 'MultiHeader: Value\r\n' - 'MultiHeader: Other Value\r\n' - 'MultiHeader: One More!\r\n' - 'Content-Length: 10\r\n', - '\r\n' - '1234567890' - ] - - expected_req = self.doPost(con, False) - self.assertEqual(None, con.sock, - 'Connection should have disowned socket') - - resp = con.getresponse() - self.assertEqual(('1.2.3.4', 443), resp.sock.sa) - self.assertEqual(expected_req, resp.sock.sent) - self.assertEqual('1234567890', resp.read()) - self.assertEqual(['Value', 'Other Value', 'One More!'], - resp.headers.getheaders('multiheader')) - self.assertEqual(['BogusServer 1.0'], - resp.headers.getheaders('server')) -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httpclient/tests/util.py --- a/mercurial/httpclient/tests/util.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,195 +0,0 @@ -# Copyright 2010, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. - -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import difflib -import socket - -import http - - -class MockSocket(object): - """Mock non-blocking socket object. - - This is ONLY capable of mocking a nonblocking socket. - - Attributes: - early_data: data to always send as soon as end of headers is seen - data: a list of strings to return on recv(), with the - assumption that the socket would block between each - string in the list. - read_wait_sentinel: data that must be written to the socket before - beginning the response. - close_on_empty: If true, close the socket when it runs out of data - for the client. - """ - def __init__(self, af, socktype, proto): - self.af = af - self.socktype = socktype - self.proto = proto - - self.early_data = [] - self.data = [] - self.remote_closed = self.closed = False - self.close_on_empty = False - self.sent = '' - self.read_wait_sentinel = http._END_HEADERS - - def close(self): - self.closed = True - - def connect(self, sa): - self.sa = sa - - def setblocking(self, timeout): - assert timeout == 0 - - def recv(self, amt=-1): - if self.early_data: - datalist = self.early_data - elif not self.data: - return '' - else: - datalist = self.data - if amt == -1: - return datalist.pop(0) - data = datalist.pop(0) - if len(data) > amt: - datalist.insert(0, data[amt:]) - if not self.data and not self.early_data and self.close_on_empty: - self.remote_closed = True - return data[:amt] - - @property - def ready_for_read(self): - return ((self.early_data and http._END_HEADERS in self.sent) - or (self.read_wait_sentinel in self.sent and self.data) - or self.closed or self.remote_closed) - - def send(self, data): - # this is a horrible mock, but nothing needs us to raise the - # correct exception yet - assert not self.closed, 'attempted to write to a closed socket' - assert not self.remote_closed, ('attempted to write to a' - ' socket closed by the server') - if len(data) > 8192: - data = data[:8192] - self.sent += data - return len(data) - - -def mockselect(r, w, x, timeout=0): - """Simple mock for select() - """ - readable = filter(lambda s: s.ready_for_read, r) - return readable, w[:], [] - - -class MockSSLSocket(object): - def __init__(self, sock): - self._sock = sock - self._fail_recv = True - - def __getattr__(self, key): - return getattr(self._sock, key) - - def __setattr__(self, key, value): - if key not in ('_sock', '_fail_recv'): - return setattr(self._sock, key, value) - return object.__setattr__(self, key, value) - - def recv(self, amt=-1): - try: - if self._fail_recv: - raise socket.sslerror(socket.SSL_ERROR_WANT_READ) - return self._sock.recv(amt=amt) - finally: - self._fail_recv = not self._fail_recv - - -def mocksslwrap(sock, keyfile=None, certfile=None, - server_side=False, cert_reqs=http.socketutil.CERT_NONE, - ssl_version=None, ca_certs=None, - do_handshake_on_connect=True, - suppress_ragged_eofs=True): - return MockSSLSocket(sock) - - -def mockgetaddrinfo(host, port, unused, streamtype): - assert unused == 0 - assert streamtype == socket.SOCK_STREAM - if host.count('.') != 3: - host = '127.0.0.42' - return [(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, '', - (host, port))] - - -class HttpTestBase(object): - def setUp(self): - self.orig_socket = socket.socket - socket.socket = MockSocket - - self.orig_getaddrinfo = socket.getaddrinfo - socket.getaddrinfo = mockgetaddrinfo - - self.orig_select = http.select.select - http.select.select = mockselect - - self.orig_sslwrap = http.socketutil.wrap_socket - http.socketutil.wrap_socket = mocksslwrap - - def tearDown(self): - socket.socket = self.orig_socket - http.select.select = self.orig_select - http.socketutil.wrap_socket = self.orig_sslwrap - socket.getaddrinfo = self.orig_getaddrinfo - - def assertStringEqual(self, l, r): - try: - self.assertEqual(l, r, ('failed string equality check, ' - 'see stdout for details')) - except: - add_nl = lambda li: map(lambda x: x + '\n', li) - print 'failed expectation:' - print ''.join(difflib.unified_diff( - add_nl(l.splitlines()), add_nl(r.splitlines()), - fromfile='expected', tofile='got')) - raise - - def doPost(self, con, expect_body, body_to_send='This is some POST data'): - con.request('POST', '/', body=body_to_send, - expect_continue=True) - expected_req = ('POST / HTTP/1.1\r\n' - 'Host: 1.2.3.4\r\n' - 'content-length: %d\r\n' - 'Expect: 100-Continue\r\n' - 'accept-encoding: identity\r\n\r\n' % - len(body_to_send)) - if expect_body: - expected_req += body_to_send - return expected_req -# no-check-code diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httppeer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/httppeer.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,248 @@ +# httppeer.py - HTTP repository proxy classes for mercurial +# +# Copyright 2005, 2006 Matt Mackall +# Copyright 2006 Vadim Gelfer +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from node import nullid +from i18n import _ +import changegroup, statichttprepo, error, httpconnection, url, util, wireproto +import os, urllib, urllib2, zlib, httplib +import errno, socket + +def zgenerator(f): + zd = zlib.decompressobj() + try: + for chunk in util.filechunkiter(f): + while chunk: + yield zd.decompress(chunk, 2**18) + chunk = zd.unconsumed_tail + except httplib.HTTPException: + raise IOError(None, _('connection ended unexpectedly')) + yield zd.flush() + +class httppeer(wireproto.wirepeer): + def __init__(self, ui, path): + self.path = path + self.caps = None + self.handler = None + self.urlopener = None + u = util.url(path) + if u.query or u.fragment: + raise util.Abort(_('unsupported URL component: "%s"') % + (u.query or u.fragment)) + + # urllib cannot handle URLs with embedded user or passwd + self._url, authinfo = u.authinfo() + + self.ui = ui + self.ui.debug('using %s\n' % self._url) + + self.urlopener = url.opener(ui, authinfo) + + def __del__(self): + if self.urlopener: + for h in self.urlopener.handlers: + h.close() + getattr(h, "close_all", lambda : None)() + + def url(self): + return self.path + + # look up capabilities only when needed + + def _fetchcaps(self): + self.caps = set(self._call('capabilities').split()) + + def _capabilities(self): + if self.caps is None: + try: + self._fetchcaps() + except error.RepoError: + self.caps = set() + self.ui.debug('capabilities: %s\n' % + (' '.join(self.caps or ['none']))) + return self.caps + + def lock(self): + raise util.Abort(_('operation not supported over http')) + + def _callstream(self, cmd, **args): + if cmd == 'pushkey': + args['data'] = '' + data = args.pop('data', None) + size = 0 + if util.safehasattr(data, 'length'): + size = data.length + elif data is not None: + size = len(data) + headers = args.pop('headers', {}) + if data is not None and 'Content-Type' not in headers: + headers['Content-Type'] = 'application/mercurial-0.1' + + + if size and self.ui.configbool('ui', 'usehttp2', False): + headers['Expect'] = '100-Continue' + headers['X-HgHttp2'] = '1' + + self.ui.debug("sending %s command\n" % cmd) + q = [('cmd', cmd)] + headersize = 0 + if len(args) > 0: + httpheader = self.capable('httpheader') + if httpheader: + headersize = int(httpheader.split(',')[0]) + if headersize > 0: + # The headers can typically carry more data than the URL. + encargs = urllib.urlencode(sorted(args.items())) + headerfmt = 'X-HgArg-%s' + contentlen = headersize - len(headerfmt % '000' + ': \r\n') + headernum = 0 + for i in xrange(0, len(encargs), contentlen): + headernum += 1 + header = headerfmt % str(headernum) + headers[header] = encargs[i:i + contentlen] + varyheaders = [headerfmt % str(h) for h in range(1, headernum + 1)] + headers['Vary'] = ','.join(varyheaders) + else: + q += sorted(args.items()) + qs = '?%s' % urllib.urlencode(q) + cu = "%s%s" % (self._url, qs) + req = urllib2.Request(cu, data, headers) + if data is not None: + self.ui.debug("sending %s bytes\n" % size) + req.add_unredirected_header('Content-Length', '%d' % size) + try: + resp = self.urlopener.open(req) + except urllib2.HTTPError, inst: + if inst.code == 401: + raise util.Abort(_('authorization failed')) + raise + except httplib.HTTPException, inst: + self.ui.debug('http error while sending %s command\n' % cmd) + self.ui.traceback() + raise IOError(None, inst) + except IndexError: + # this only happens with Python 2.3, later versions raise URLError + raise util.Abort(_('http error, possibly caused by proxy setting')) + # record the url we got redirected to + resp_url = resp.geturl() + if resp_url.endswith(qs): + resp_url = resp_url[:-len(qs)] + if self._url.rstrip('/') != resp_url.rstrip('/'): + if not self.ui.quiet: + self.ui.warn(_('real URL is %s\n') % resp_url) + self._url = resp_url + try: + proto = resp.getheader('content-type') + except AttributeError: + proto = resp.headers.get('content-type', '') + + safeurl = util.hidepassword(self._url) + if proto.startswith('application/hg-error'): + raise error.OutOfBandError(resp.read()) + # accept old "text/plain" and "application/hg-changegroup" for now + if not (proto.startswith('application/mercurial-') or + proto.startswith('text/plain') or + proto.startswith('application/hg-changegroup')): + self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu)) + raise error.RepoError( + _("'%s' does not appear to be an hg repository:\n" + "---%%<--- (%s)\n%s\n---%%<---\n") + % (safeurl, proto or 'no content-type', resp.read())) + + if proto.startswith('application/mercurial-'): + try: + version = proto.split('-', 1)[1] + version_info = tuple([int(n) for n in version.split('.')]) + except ValueError: + raise error.RepoError(_("'%s' sent a broken Content-Type " + "header (%s)") % (safeurl, proto)) + if version_info > (0, 1): + raise error.RepoError(_("'%s' uses newer protocol %s") % + (safeurl, version)) + + return resp + + def _call(self, cmd, **args): + fp = self._callstream(cmd, **args) + try: + return fp.read() + finally: + # if using keepalive, allow connection to be reused + fp.close() + + def _callpush(self, cmd, cg, **args): + # have to stream bundle to a temp file because we do not have + # http 1.1 chunked transfer. + + types = self.capable('unbundle') + try: + types = types.split(',') + except AttributeError: + # servers older than d1b16a746db6 will send 'unbundle' as a + # boolean capability. They only support headerless/uncompressed + # bundles. + types = [""] + for x in types: + if x in changegroup.bundletypes: + type = x + break + + tempname = changegroup.writebundle(cg, None, type) + fp = httpconnection.httpsendfile(self.ui, tempname, "rb") + headers = {'Content-Type': 'application/mercurial-0.1'} + + try: + try: + r = self._call(cmd, data=fp, headers=headers, **args) + vals = r.split('\n', 1) + if len(vals) < 2: + raise error.ResponseError(_("unexpected response:"), r) + return vals + except socket.error, err: + if err.args[0] in (errno.ECONNRESET, errno.EPIPE): + raise util.Abort(_('push failed: %s') % err.args[1]) + raise util.Abort(err.args[1]) + finally: + fp.close() + os.unlink(tempname) + + def _abort(self, exception): + raise exception + + def _decompress(self, stream): + return util.chunkbuffer(zgenerator(stream)) + +class httpspeer(httppeer): + def __init__(self, ui, path): + if not url.has_https: + raise util.Abort(_('Python support for SSL and HTTPS ' + 'is not installed')) + httppeer.__init__(self, ui, path) + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new http repository')) + try: + if path.startswith('https:'): + inst = httpspeer(ui, path) + else: + inst = httppeer(ui, path) + try: + # Try to do useful work when checking compatibility. + # Usually saves a roundtrip since we want the caps anyway. + inst._fetchcaps() + except error.RepoError: + # No luck, try older compatibility check. + inst.between([(nullid, nullid)]) + return inst + except error.RepoError, httpexception: + try: + r = statichttprepo.instance(ui, "static-" + path, create) + ui.note('(falling back to static-http)\n') + return r + except error.RepoError: + raise httpexception # use the original http RepoError instead diff -r 98823bd0d697 -r a06e2681dd17 mercurial/httprepo.py --- a/mercurial/httprepo.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,247 +0,0 @@ -# httprepo.py - HTTP repository proxy classes for mercurial -# -# Copyright 2005, 2006 Matt Mackall -# Copyright 2006 Vadim Gelfer -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -from node import nullid -from i18n import _ -import changegroup, statichttprepo, error, httpconnection, url, util, wireproto -import os, urllib, urllib2, zlib, httplib -import errno, socket - -def zgenerator(f): - zd = zlib.decompressobj() - try: - for chunk in util.filechunkiter(f): - while chunk: - yield zd.decompress(chunk, 2**18) - chunk = zd.unconsumed_tail - except httplib.HTTPException: - raise IOError(None, _('connection ended unexpectedly')) - yield zd.flush() - -class httprepository(wireproto.wirerepository): - def __init__(self, ui, path): - self.path = path - self.caps = None - self.handler = None - self.urlopener = None - u = util.url(path) - if u.query or u.fragment: - raise util.Abort(_('unsupported URL component: "%s"') % - (u.query or u.fragment)) - - # urllib cannot handle URLs with embedded user or passwd - self._url, authinfo = u.authinfo() - - self.ui = ui - self.ui.debug('using %s\n' % self._url) - - self.urlopener = url.opener(ui, authinfo) - - def __del__(self): - if self.urlopener: - for h in self.urlopener.handlers: - h.close() - getattr(h, "close_all", lambda : None)() - - def url(self): - return self.path - - # look up capabilities only when needed - - def _fetchcaps(self): - self.caps = set(self._call('capabilities').split()) - - def get_caps(self): - if self.caps is None: - try: - self._fetchcaps() - except error.RepoError: - self.caps = set() - self.ui.debug('capabilities: %s\n' % - (' '.join(self.caps or ['none']))) - return self.caps - - capabilities = property(get_caps) - - def lock(self): - raise util.Abort(_('operation not supported over http')) - - def _callstream(self, cmd, **args): - if cmd == 'pushkey': - args['data'] = '' - data = args.pop('data', None) - size = 0 - if util.safehasattr(data, 'length'): - size = data.length - elif data is not None: - size = len(data) - headers = args.pop('headers', {}) - - if size and self.ui.configbool('ui', 'usehttp2', False): - headers['Expect'] = '100-Continue' - headers['X-HgHttp2'] = '1' - - self.ui.debug("sending %s command\n" % cmd) - q = [('cmd', cmd)] - headersize = 0 - if len(args) > 0: - httpheader = self.capable('httpheader') - if httpheader: - headersize = int(httpheader.split(',')[0]) - if headersize > 0: - # The headers can typically carry more data than the URL. - encargs = urllib.urlencode(sorted(args.items())) - headerfmt = 'X-HgArg-%s' - contentlen = headersize - len(headerfmt % '000' + ': \r\n') - headernum = 0 - for i in xrange(0, len(encargs), contentlen): - headernum += 1 - header = headerfmt % str(headernum) - headers[header] = encargs[i:i + contentlen] - varyheaders = [headerfmt % str(h) for h in range(1, headernum + 1)] - headers['Vary'] = ','.join(varyheaders) - else: - q += sorted(args.items()) - qs = '?%s' % urllib.urlencode(q) - cu = "%s%s" % (self._url, qs) - req = urllib2.Request(cu, data, headers) - if data is not None: - self.ui.debug("sending %s bytes\n" % size) - req.add_unredirected_header('Content-Length', '%d' % size) - try: - resp = self.urlopener.open(req) - except urllib2.HTTPError, inst: - if inst.code == 401: - raise util.Abort(_('authorization failed')) - raise - except httplib.HTTPException, inst: - self.ui.debug('http error while sending %s command\n' % cmd) - self.ui.traceback() - raise IOError(None, inst) - except IndexError: - # this only happens with Python 2.3, later versions raise URLError - raise util.Abort(_('http error, possibly caused by proxy setting')) - # record the url we got redirected to - resp_url = resp.geturl() - if resp_url.endswith(qs): - resp_url = resp_url[:-len(qs)] - if self._url.rstrip('/') != resp_url.rstrip('/'): - if not self.ui.quiet: - self.ui.warn(_('real URL is %s\n') % resp_url) - self._url = resp_url - try: - proto = resp.getheader('content-type') - except AttributeError: - proto = resp.headers.get('content-type', '') - - safeurl = util.hidepassword(self._url) - if proto.startswith('application/hg-error'): - raise error.OutOfBandError(resp.read()) - # accept old "text/plain" and "application/hg-changegroup" for now - if not (proto.startswith('application/mercurial-') or - proto.startswith('text/plain') or - proto.startswith('application/hg-changegroup')): - self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu)) - raise error.RepoError( - _("'%s' does not appear to be an hg repository:\n" - "---%%<--- (%s)\n%s\n---%%<---\n") - % (safeurl, proto or 'no content-type', resp.read())) - - if proto.startswith('application/mercurial-'): - try: - version = proto.split('-', 1)[1] - version_info = tuple([int(n) for n in version.split('.')]) - except ValueError: - raise error.RepoError(_("'%s' sent a broken Content-Type " - "header (%s)") % (safeurl, proto)) - if version_info > (0, 1): - raise error.RepoError(_("'%s' uses newer protocol %s") % - (safeurl, version)) - - return resp - - def _call(self, cmd, **args): - fp = self._callstream(cmd, **args) - try: - return fp.read() - finally: - # if using keepalive, allow connection to be reused - fp.close() - - def _callpush(self, cmd, cg, **args): - # have to stream bundle to a temp file because we do not have - # http 1.1 chunked transfer. - - types = self.capable('unbundle') - try: - types = types.split(',') - except AttributeError: - # servers older than d1b16a746db6 will send 'unbundle' as a - # boolean capability. They only support headerless/uncompressed - # bundles. - types = [""] - for x in types: - if x in changegroup.bundletypes: - type = x - break - - tempname = changegroup.writebundle(cg, None, type) - fp = httpconnection.httpsendfile(self.ui, tempname, "rb") - headers = {'Content-Type': 'application/mercurial-0.1'} - - try: - try: - r = self._call(cmd, data=fp, headers=headers, **args) - vals = r.split('\n', 1) - if len(vals) < 2: - raise error.ResponseError(_("unexpected response:"), r) - return vals - except socket.error, err: - if err.args[0] in (errno.ECONNRESET, errno.EPIPE): - raise util.Abort(_('push failed: %s') % err.args[1]) - raise util.Abort(err.args[1]) - finally: - fp.close() - os.unlink(tempname) - - def _abort(self, exception): - raise exception - - def _decompress(self, stream): - return util.chunkbuffer(zgenerator(stream)) - -class httpsrepository(httprepository): - def __init__(self, ui, path): - if not url.has_https: - raise util.Abort(_('Python support for SSL and HTTPS ' - 'is not installed')) - httprepository.__init__(self, ui, path) - -def instance(ui, path, create): - if create: - raise util.Abort(_('cannot create new http repository')) - try: - if path.startswith('https:'): - inst = httpsrepository(ui, path) - else: - inst = httprepository(ui, path) - try: - # Try to do useful work when checking compatibility. - # Usually saves a roundtrip since we want the caps anyway. - inst._fetchcaps() - except error.RepoError: - # No luck, try older compatibility check. - inst.between([(nullid, nullid)]) - return inst - except error.RepoError, httpexception: - try: - r = statichttprepo.instance(ui, "static-" + path, create) - ui.note('(falling back to static-http)\n') - return r - except error.RepoError: - raise httpexception # use the original http RepoError instead diff -r 98823bd0d697 -r a06e2681dd17 mercurial/keepalive.py --- a/mercurial/keepalive.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/keepalive.py Wed Jul 18 19:08:25 2012 -0500 @@ -136,7 +136,7 @@ def add(self, host, connection, ready): self._lock.acquire() try: - if not host in self._hostmap: + if host not in self._hostmap: self._hostmap[host] = [] self._hostmap[host].append(connection) self._connmap[connection] = host @@ -290,7 +290,7 @@ # worked. We'll check the version below, too. except (socket.error, httplib.HTTPException): r = None - except: + except: # re-raises # adding this block just in case we've missed # something we will still raise the exception, but # lets try and close the connection and remove it @@ -534,7 +534,7 @@ if self.auto_open: self.connect() else: - raise httplib.NotConnected() + raise httplib.NotConnected # send the data to the server. if we get a broken pipe, then close # the socket. we want to reconnect when somebody tries to send again. @@ -758,7 +758,7 @@ try: N = int(sys.argv[1]) url = sys.argv[2] - except: + except (IndexError, ValueError): print "%s " % sys.argv[0] else: test(url, N) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/localrepo.py --- a/mercurial/localrepo.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/localrepo.py Wed Jul 18 19:08:25 2012 -0500 @@ -4,12 +4,11 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. - from node import bin, hex, nullid, nullrev, short from i18n import _ -import repo, changegroup, subrepo, discovery, pushkey +import peer, changegroup, subrepo, discovery, pushkey, obsolete import changelog, dirstate, filelog, manifest, context, bookmarks, phases -import lock, transaction, store, encoding +import lock, transaction, store, encoding, base85 import scmutil, util, extensions, hook, error, revset import match as matchmod import merge as mergemod @@ -24,50 +23,135 @@ def join(self, obj, fname): return obj.sjoin(fname) -class localrepository(repo.repository): - capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey', - 'known', 'getbundle')) +MODERNCAPS = set(('lookup', 'branchmap', 'pushkey', 'known', 'getbundle')) +LEGACYCAPS = MODERNCAPS.union(set(['changegroupsubset'])) + +class localpeer(peer.peerrepository): + '''peer for a local repo; reflects only the most recent API''' + + def __init__(self, repo, caps=MODERNCAPS): + peer.peerrepository.__init__(self) + self._repo = repo + self.ui = repo.ui + self._caps = repo._restrictcapabilities(caps) + self.requirements = repo.requirements + self.supportedformats = repo.supportedformats + + def close(self): + self._repo.close() + + def _capabilities(self): + return self._caps + + def local(self): + return self._repo + + def canpush(self): + return True + + def url(self): + return self._repo.url() + + def lookup(self, key): + return self._repo.lookup(key) + + def branchmap(self): + return discovery.visiblebranchmap(self._repo) + + def heads(self): + return discovery.visibleheads(self._repo) + + def known(self, nodes): + return self._repo.known(nodes) + + def getbundle(self, source, heads=None, common=None): + return self._repo.getbundle(source, heads=heads, common=common) + + # TODO We might want to move the next two calls into legacypeer and add + # unbundle instead. + + def lock(self): + return self._repo.lock() + + def addchangegroup(self, cg, source, url): + return self._repo.addchangegroup(cg, source, url) + + def pushkey(self, namespace, key, old, new): + return self._repo.pushkey(namespace, key, old, new) + + def listkeys(self, namespace): + return self._repo.listkeys(namespace) + + def debugwireargs(self, one, two, three=None, four=None, five=None): + '''used to test argument passing over the wire''' + return "%s %s %s %s %s" % (one, two, three, four, five) + +class locallegacypeer(localpeer): + '''peer extension which implements legacy methods too; used for tests with + restricted capabilities''' + + def __init__(self, repo): + localpeer.__init__(self, repo, caps=LEGACYCAPS) + + def branches(self, nodes): + return self._repo.branches(nodes) + + def between(self, pairs): + return self._repo.between(pairs) + + def changegroup(self, basenodes, source): + return self._repo.changegroup(basenodes, source) + + def changegroupsubset(self, bases, heads, source): + return self._repo.changegroupsubset(bases, heads, source) + +class localrepository(object): + supportedformats = set(('revlogv1', 'generaldelta')) supported = supportedformats | set(('store', 'fncache', 'shared', 'dotencode')) + openerreqs = set(('revlogv1', 'generaldelta')) + requirements = ['revlogv1'] + + def _baserequirements(self, create): + return self.requirements[:] def __init__(self, baseui, path=None, create=False): - repo.repository.__init__(self) - self.root = os.path.realpath(util.expandpath(path)) - self.path = os.path.join(self.root, ".hg") + self.wopener = scmutil.opener(path, expand=True) + self.wvfs = self.wopener + self.root = self.wvfs.base + self.path = self.wvfs.join(".hg") self.origroot = path self.auditor = scmutil.pathauditor(self.root, self._checknested) self.opener = scmutil.opener(self.path) - self.wopener = scmutil.opener(self.root) + self.vfs = self.opener self.baseui = baseui self.ui = baseui.copy() - self._dirtyphases = False # A list of callback to shape the phase if no data were found. # Callback are in the form: func(repo, roots) --> processed root. # This list it to be filled by extension during repo setup self._phasedefaults = [] - try: self.ui.readconfig(self.join("hgrc"), self.root) extensions.loadall(self.ui) except IOError: pass - if not os.path.isdir(self.path): + if not self.vfs.isdir(): if create: - if not os.path.exists(path): - util.makedirs(path) - util.makedir(self.path, notindexed=True) - requirements = ["revlogv1"] + if not self.wvfs.exists(): + self.wvfs.makedirs() + self.vfs.makedir(notindexed=True) + requirements = self._baserequirements(create) if self.ui.configbool('format', 'usestore', True): - os.mkdir(os.path.join(self.path, "store")) + self.vfs.mkdir("store") requirements.append("store") if self.ui.configbool('format', 'usefncache', True): requirements.append("fncache") if self.ui.configbool('format', 'dotencode', True): requirements.append('dotencode') # create an invalid changelog - self.opener.append( + self.vfs.append( "00changelog.i", '\0\0\0\2' # represents revlogv2 ' dummy changelog to prevent using the old repo layout' @@ -81,7 +165,7 @@ raise error.RepoError(_("repository %s already exists") % path) else: try: - requirements = scmutil.readrequires(self.opener, self.supported) + requirements = scmutil.readrequires(self.vfs, self.supported) except IOError, inst: if inst.errno != errno.ENOENT: raise @@ -101,6 +185,7 @@ self.store = store.store(requirements, self.sharedpath, scmutil.opener) self.spath = self.store.path self.sopener = self.store.opener + self.svfs = self.sopener self.sjoin = self.store.join self.opener.createmode = self.store.createmode self._applyrequirements(requirements) @@ -120,11 +205,16 @@ # Maps a property name to its util.filecacheentry self._filecache = {} + def close(self): + pass + + def _restrictcapabilities(self, caps): + return caps + def _applyrequirements(self, requirements): self.requirements = requirements - openerreqs = set(('revlogv1', 'generaldelta')) self.sopener.options = dict((r, 1) for r in requirements - if r in openerreqs) + if r in self.openerreqs) def _writerequirements(self): reqfile = self.opener("requires", "w") @@ -170,6 +260,9 @@ parts.pop() return False + def peer(self): + return localpeer(self) # not cached to avoid reference cycle + @filecache('bookmarks') def _bookmarks(self): return bookmarks.read(self) @@ -181,24 +274,41 @@ def _writebookmarks(self, marks): bookmarks.write(self) + def bookmarkheads(self, bookmark): + name = bookmark.split('@', 1)[0] + heads = [] + for mark, n in self._bookmarks.iteritems(): + if mark.split('@', 1)[0] == name: + heads.append(n) + return heads + @storecache('phaseroots') - def _phaseroots(self): - self._dirtyphases = False - phaseroots = phases.readroots(self) - phases.filterunknown(self, phaseroots) - return phaseroots + def _phasecache(self): + return phases.phasecache(self, self._phasedefaults) + + @storecache('obsstore') + def obsstore(self): + store = obsolete.obsstore(self.sopener) + return store @propertycache - def _phaserev(self): - cache = [phases.public] * len(self) - for phase in phases.trackedphases: - roots = map(self.changelog.rev, self._phaseroots[phase]) - if roots: - for rev in roots: - cache[rev] = phase - for rev in self.changelog.descendants(*roots): - cache[rev] = phase - return cache + def hiddenrevs(self): + """hiddenrevs: revs that should be hidden by command and tools + + This set is carried on the repo to ease initialisation and lazy + loading it'll probably move back to changelog for efficienty and + consistency reason + + Note that the hiddenrevs will needs invalidations when + - a new changesets is added (possible unstable above extinct) + - a new obsolete marker is added (possible new extinct changeset) + """ + hidden = set() + if self.obsstore: + ### hide extinct changeset that are not accessible by any mean + hiddenquery = 'extinct() - ::(. + bookmark() + tagged())' + hidden.update(self.revs(hiddenquery)) + return hidden @storecache('00changelog.i') def changelog(self): @@ -296,7 +406,8 @@ fp.write('\n') for name in names: m = munge and munge(name) or name - if self._tagscache.tagtypes and name in self._tagscache.tagtypes: + if (self._tagscache.tagtypes and + name in self._tagscache.tagtypes): old = self.tags().get(name, nullid) fp.write('%s %s\n' % (hex(old), m)) fp.write('%s %s\n' % (hex(node), m)) @@ -376,7 +487,8 @@ @propertycache def _tagscache(self): - '''Returns a tagscache object that contains various tags related caches.''' + '''Returns a tagscache object that contains various tags related + caches.''' # This simplifies its cache management by having one decorated # function (this one) and the rest simply fetch things from it. @@ -505,7 +617,7 @@ partial = self._branchcache self._branchtags(partial, lrev) - # this private cache holds all heads (not just tips) + # this private cache holds all heads (not just the branch tips) self._branchcache = partial def branchmap(self): @@ -513,17 +625,27 @@ self.updatebranchcache() return self._branchcache + def _branchtip(self, heads): + '''return the tipmost branch head in heads''' + tip = heads[-1] + for h in reversed(heads): + if not self[h].closesbranch(): + tip = h + break + return tip + + def branchtip(self, branch): + '''return the tip node for a given branch''' + if branch not in self.branchmap(): + raise error.RepoLookupError(_("unknown branch '%s'") % branch) + return self._branchtip(self.branchmap()[branch]) + def branchtags(self): '''return a dict where branch names map to the tipmost head of the branch, open heads come before closed''' bt = {} for bn, heads in self.branchmap().iteritems(): - tip = heads[-1] - for h in reversed(heads): - if 'close' not in self.changelog.read(h)[5]: - tip = h - break - bt[bn] = tip + bt[bn] = self._branchtip(heads) return bt def _readbranchcache(self): @@ -546,6 +668,9 @@ continue node, label = l.split(" ", 1) label = encoding.tolocal(label.strip()) + if not node in self: + raise ValueError('invalidating branch cache because node '+ + '%s does not exist' % node) partial.setdefault(label, []).append(bin(node)) except KeyboardInterrupt: raise @@ -567,6 +692,10 @@ pass def _updatebranchcache(self, partial, ctxgen): + """Given a branchhead cache, partial, that may have extra nodes or be + missing heads, and a generator of nodes that are at least a superset of + heads missing, this function updates partial to be correct. + """ # collect new branch entries newbranches = {} for c in ctxgen: @@ -576,21 +705,51 @@ # 1 (branch a) -> 2 (branch b) -> 3 (branch a) for branch, newnodes in newbranches.iteritems(): bheads = partial.setdefault(branch, []) - bheads.extend(newnodes) - if len(bheads) <= 1: - continue - bheads = sorted(bheads, key=lambda x: self[x].rev()) - # starting from tip means fewer passes over reachable - while newnodes: - latest = newnodes.pop() - if latest not in bheads: + # Remove candidate heads that no longer are in the repo (e.g., as + # the result of a strip that just happened). Avoid using 'node in + # self' here because that dives down into branchcache code somewhat + # recrusively. + bheadrevs = [self.changelog.rev(node) for node in bheads + if self.changelog.hasnode(node)] + newheadrevs = [self.changelog.rev(node) for node in newnodes + if self.changelog.hasnode(node)] + ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs) + # Remove duplicates - nodes that are in newheadrevs and are already + # in bheadrevs. This can happen if you strip a node whose parent + # was already a head (because they're on different branches). + bheadrevs = sorted(set(bheadrevs).union(newheadrevs)) + + # Starting from tip means fewer passes over reachable. If we know + # the new candidates are not ancestors of existing heads, we don't + # have to examine ancestors of existing heads + if ctxisnew: + iterrevs = sorted(newheadrevs) + else: + iterrevs = list(bheadrevs) + + # This loop prunes out two kinds of heads - heads that are + # superceded by a head in newheadrevs, and newheadrevs that are not + # heads because an existing head is their descendant. + while iterrevs: + latest = iterrevs.pop() + if latest not in bheadrevs: continue - minbhrev = self[bheads[0]].node() - reachable = self.changelog.reachable(latest, minbhrev) - reachable.remove(latest) - if reachable: - bheads = [b for b in bheads if b not in reachable] - partial[branch] = bheads + ancestors = set(self.changelog.ancestors([latest], + bheadrevs[0])) + if ancestors: + bheadrevs = [b for b in bheadrevs if b not in ancestors] + partial[branch] = [self.changelog.node(rev) for rev in bheadrevs] + + # There may be branches that cease to exist when the last commit in the + # branch was stripped. This code filters them out. Note that the + # branch that ceased to exist may not be in newbranches because + # newbranches is the set of candidate heads, which when you strip the + # last commit in a branch will be the parent branch. + for branch in partial.keys(): + nodes = [head for head in partial[branch] + if self.changelog.hasnode(head)] + if not nodes: + del partial[branch] def lookup(self, key): return self[key].node() @@ -605,16 +764,20 @@ def known(self, nodes): nm = self.changelog.nodemap + pc = self._phasecache result = [] for n in nodes: r = nm.get(n) - resp = not (r is None or self._phaserev[r] >= phases.secret) + resp = not (r is None or pc.phase(self, r) >= phases.secret) result.append(resp) return result def local(self): return self + def cancopy(self): + return self.local() # so statichttprepo's override of local() works + def join(self, f): return os.path.join(self.path, f) @@ -833,9 +996,6 @@ self.sjoin('phaseroots')) self.invalidate() - # Discard all cache entries to force reloading everything. - self._filecache.clear() - parentgone = (parents[0] not in self.changelog.nodemap or parents[1] not in self.changelog.nodemap) if parentgone: @@ -856,6 +1016,9 @@ else: ui.status(_('working directory now based on ' 'revision %d\n') % parents) + # TODO: if we know which new heads may result from this rollback, pass + # them to destroy(), which will prevent the branchhead cache from being + # invalidated. self.destroyed() return 0 @@ -867,7 +1030,6 @@ pass delcache('_tagscache') - delcache('_phaserev') self._branchcache = None # in UTF-8 self._branchcachetip = None @@ -901,6 +1063,9 @@ pass self.invalidatecaches() + # Discard all cache entries to force reloading everything. + self._filecache.clear() + def _lock(self, lockname, wait, releasefn, acquirefn, desc): try: l = lock.lock(lockname, 0, releasefn, desc=desc) @@ -937,9 +1102,8 @@ def unlock(): self.store.write() - if self._dirtyphases: - phases.writeroots(self) - self._dirtyphases = False + if '_phasecache' in vars(self): + self._phasecache.write() for k, ce in self._filecache.items(): if k == 'dirstate': continue @@ -1195,16 +1359,17 @@ p1, p2 = self.dirstate.parents() hookp1, hookp2 = hex(p1), (p2 != nullid and hex(p2) or '') try: - self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2) + self.hook("precommit", throw=True, parent1=hookp1, + parent2=hookp2) ret = self.commitctx(cctx, True) - except: + except: # re-raises if edited: self.ui.write( _('note: commit message saved in %s\n') % msgfn) raise # update bookmarks, dirstate and mergestate - bookmarks.update(self, p1, ret) + bookmarks.update(self, [p1, p2], ret) for f in changes[0] + changes[1]: self.dirstate.normal(f) for f in changes[2]: @@ -1300,12 +1465,27 @@ tr.release() lock.release() - def destroyed(self): + def destroyed(self, newheadnodes=None): '''Inform the repository that nodes have been destroyed. Intended for use by strip and rollback, so there's a common - place for anything that has to be done after destroying history.''' - # XXX it might be nice if we could take the list of destroyed - # nodes, but I don't see an easy way for rollback() to do that + place for anything that has to be done after destroying history. + + If you know the branchheadcache was uptodate before nodes were removed + and you also know the set of candidate new heads that may have resulted + from the destruction, you can set newheadnodes. This will enable the + code to update the branchheads cache, rather than having future code + decide it's invalid and regenrating it from scratch. + ''' + # If we have info, newheadnodes, on how to update the branch cache, do + # it, Otherwise, since nodes were destroyed, the cache is stale and this + # will be caught the next time it is read. + if newheadnodes: + tiprev = len(self) - 1 + ctxgen = (self[node] for node in newheadnodes + if self.changelog.hasnode(node)) + self._updatebranchcache(self._branchcache, ctxgen) + self._writebranchcache(self._branchcache, self.changelog.tip(), + tiprev) # Ensure the persistent tag cache is updated. Doing it now # means that the tag cache only has to worry about destroyed @@ -1330,7 +1510,8 @@ def status(self, node1='.', node2=None, match=None, ignored=False, clean=False, unknown=False, listsubrepos=False): - """return status of files between two nodes or node and working directory + """return status of files between two nodes or node and working + directory. If node1 is None, use the first dirstate parent instead. If node2 is None, compare node1 with working directory. @@ -1338,6 +1519,8 @@ def mfmatches(ctx): mf = ctx.manifest().copy() + if match.always(): + return mf for fn in mf.keys(): if not match(fn): del mf[fn] @@ -1423,10 +1606,11 @@ mf2 = mfmatches(ctx2) modified, added, clean = [], [], [] + withflags = mf1.withflags() | mf2.withflags() for fn in mf2: if fn in mf1: if (fn not in deleted and - (mf1.flags(fn) != mf2.flags(fn) or + ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or (mf1[fn] != mf2[fn] and (mf2[fn] or ctx1[fn].cmp(ctx2[fn]))))): modified.append(fn) @@ -1501,8 +1685,7 @@ fbheads = set(self.changelog.nodesbetween([start], bheads)[2]) bheads = [h for h in bheads if h in fbheads] if not closed: - bheads = [h for h in bheads if - ('close' not in self.changelog.read(h)[5])] + bheads = [h for h in bheads if not self[h].closesbranch()] return bheads def branches(self, nodes): @@ -1539,6 +1722,10 @@ return r def pull(self, remote, heads=None, force=False): + # don't open transaction for nothing or you break future useful + # rollback call + tr = None + trname = 'pull\n' + util.hidepassword(remote.url()) lock = self.lock() try: tmp = discovery.findcommonincoming(self, remote, heads=heads, @@ -1549,6 +1736,7 @@ added = [] result = 0 else: + tr = self.transaction(trname) if heads is None and list(common) == [nullid]: self.ui.status(_("requesting all changes\n")) elif heads is None and remote.capable('changegroupsubset'): @@ -1594,7 +1782,18 @@ # Remote is old or publishing all common changesets # should be seen as public phases.advanceboundary(self, phases.public, subset) + + remoteobs = remote.listkeys('obsolete') + if 'dump' in remoteobs: + if tr is None: + tr = self.transaction(trname) + data = base85.b85decode(remoteobs['dump']) + self.obsstore.mergemarkers(tr, data) + if tr is not None: + tr.close() finally: + if tr is not None: + tr.release() lock.release() return result @@ -1623,6 +1822,8 @@ # unbundle assumes local user cannot lock remote repo (new ssh # servers, http servers). + if not remote.canpush(): + raise util.Abort(_("destination does not support push")) # get local lock as we might write phase data locallock = self.lock() try: @@ -1648,6 +1849,22 @@ else: # something to push if not force: + # if self.obsstore == False --> no obsolete + # then, save the iteration + if self.obsstore: + # this message are here for 80 char limit reason + mso = _("push includes an obsolete changeset: %s!") + msu = _("push includes an unstable changeset: %s!") + # If we are to push if there is at least one + # obsolete or unstable changeset in missing, at + # least one of the missinghead will be obsolete or + # unstable. So checking heads only is ok + for node in outgoing.missingheads: + ctx = self[node] + if ctx.obsolete(): + raise util.Abort(_(mso) % ctx) + elif ctx.unstable(): + raise util.Abort(_(msu) % ctx) discovery.checkheads(self, remote, outgoing, remoteheads, newbranch, bool(inc)) @@ -1672,7 +1889,8 @@ # http: return remote's addchangegroup() or 0 for error ret = remote.unbundle(cg, remoteheads, 'push') else: - # we return an integer indicating remote head count change + # we return an integer indicating remote head count + # change ret = remote.addchangegroup(cg, 'push', self.url()) if ret: @@ -1698,7 +1916,7 @@ # * missingheads part of comon (::commonheads) common = set(outgoing.common) cheads = [node for node in revs if node in common] - # and + # and # * commonheads parents on missing revset = self.set('%ln and parents(roots(%ln))', outgoing.commonheads, @@ -1733,6 +1951,12 @@ if not r: self.ui.warn(_('updating %s to public failed!\n') % newremotehead) + if ('obsolete' in remote.listkeys('namespaces') + and self.obsstore): + data = self.listkeys('obsolete')['dump'] + r = remote.pushkey('obsolete', 'dump', '', data) + if not r: + self.ui.warn(_('failed to push obsolete markers!\n')) finally: if lock is not None: lock.release() @@ -1783,7 +2007,7 @@ bases = [nullid] csets, bases, heads = cl.nodesbetween(bases, heads) # We assume that all ancestors of bases are known - common = set(cl.ancestors(*[cl.rev(n) for n in bases])) + common = set(cl.ancestors([cl.rev(n) for n in bases])) return self._changegroupsubset(common, csets, heads, source) def getlocalbundle(self, source, outgoing): @@ -1904,7 +2128,8 @@ for fname in sorted(changedfiles): filerevlog = self.file(fname) if not len(filerevlog): - raise util.Abort(_("empty or missing revlog for %s") % fname) + raise util.Abort(_("empty or missing revlog for %s") + % fname) fstate[0] = fname fstate[1] = fnodes.pop(fname, {}) @@ -2004,7 +2229,8 @@ for fname in sorted(changedfiles): filerevlog = self.file(fname) if not len(filerevlog): - raise util.Abort(_("empty or missing revlog for %s") % fname) + raise util.Abort(_("empty or missing revlog for %s") + % fname) fstate[0] = fname nodelist = gennodelst(filerevlog) if nodelist: @@ -2149,7 +2375,7 @@ heads = cl.heads() dh = len(heads) - len(oldheads) for h in heads: - if h not in oldheads and 'close' in self[h].extra(): + if h not in oldheads and self[h].closesbranch(): dh -= 1 htext = "" if dh: @@ -2220,7 +2446,7 @@ resp = int(l) except ValueError: raise error.ResponseError( - _('Unexpected response from remote server:'), l) + _('unexpected response from remote server:'), l) if resp == 1: raise util.Abort(_('operation forbidden by server')) elif resp == 2: @@ -2233,9 +2459,11 @@ total_files, total_bytes = map(int, l.split(' ', 1)) except (ValueError, TypeError): raise error.ResponseError( - _('Unexpected response from remote server:'), l) + _('unexpected response from remote server:'), l) self.ui.status(_('%d files to transfer, %s of data\n') % (total_files, util.bytecount(total_bytes))) + handled_bytes = 0 + self.ui.progress(_('clone'), 0, total=total_bytes) start = time.time() for i in xrange(total_files): # XXX doesn't support '\n' or '\r' in filenames @@ -2245,23 +2473,28 @@ size = int(size) except (ValueError, TypeError): raise error.ResponseError( - _('Unexpected response from remote server:'), l) + _('unexpected response from remote server:'), l) if self.ui.debugflag: self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size))) # for backwards compat, name was partially encoded ofp = self.sopener(store.decodedir(name), 'w') for chunk in util.filechunkiter(fp, limit=size): + handled_bytes += len(chunk) + self.ui.progress(_('clone'), handled_bytes, + total=total_bytes) ofp.write(chunk) ofp.close() elapsed = time.time() - start if elapsed <= 0: elapsed = 0.001 + self.ui.progress(_('clone'), None) self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % (util.bytecount(total_bytes), elapsed, util.bytecount(total_bytes / elapsed))) - # new requirements = old non-format requirements + new format-related + # new requirements = old non-format requirements + + # new format-related # requirements from the streamed-in repository requirements.update(set(self.requirements) - self.supportedformats) self._applyrequirements(requirements) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/lsprof.py --- a/mercurial/lsprof.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/lsprof.py Wed Jul 18 19:08:25 2012 -0500 @@ -38,8 +38,8 @@ d = d[:top] cols = "% 12s %12s %11.4f %11.4f %s\n" hcols = "% 12s %12s %12s %12s %s\n" - file.write(hcols % ("CallCount", "Recursive", "Total(ms)", - "Inline(ms)", "module:lineno(function)")) + file.write(hcols % ("CallCount", "Recursive", "Total(s)", + "Inline(s)", "module:lineno(function)")) count = 0 for e in d: file.write(cols % (e.callcount, e.reccallcount, e.totaltime, diff -r 98823bd0d697 -r a06e2681dd17 mercurial/manifest.py --- a/mercurial/manifest.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/manifest.py Wed Jul 18 19:08:25 2012 -0500 @@ -19,6 +19,8 @@ self._flags = flags def flags(self, f): return self._flags.get(f, "") + def withflags(self): + return set(self._flags.keys()) def set(self, f, flags): self._flags[f] = flags def copy(self): @@ -124,8 +126,8 @@ addlist[start:end] = array.array('c', content) else: del addlist[start:end] - return "".join(struct.pack(">lll", start, end, len(content)) + content - for start, end, content in x) + return "".join(struct.pack(">lll", start, end, len(content)) + + content for start, end, content in x) def checkforbidden(l): for f in l: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/match.py --- a/mercurial/match.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/match.py Wed Jul 18 19:08:25 2012 -0500 @@ -9,6 +9,14 @@ import scmutil, util, fileset from i18n import _ +def _rematcher(pat): + m = util.compilere(pat) + try: + # slightly faster, provided by facebook's re2 bindings + return m.test_match + except AttributeError: + return m.match + def _expandsets(pats, ctx): '''convert set: patterns into a list of files in the given context''' fset = set() @@ -121,6 +129,8 @@ return self._files def anypats(self): return self._anypats + def always(self): + return False class exact(match): def __init__(self, root, cwd, files): @@ -129,6 +139,8 @@ class always(match): def __init__(self, root, cwd): match.__init__(self, root, cwd, []) + def always(self): + return True class narrowmatcher(match): """Adapt a matcher to work on a subdirectory only. @@ -275,8 +287,8 @@ try: pat = '(?:%s)' % '|'.join([_regex(k, p, tail) for (k, p) in pats]) if len(pat) > 20000: - raise OverflowError() - return pat, re.compile(pat).match + raise OverflowError + return pat, _rematcher(pat) except OverflowError: # We're using a Python with a tiny regex engine and we # made it explode, so we'll divide the pattern list in two @@ -290,7 +302,7 @@ except re.error: for k, p in pats: try: - re.compile('(?:%s)' % _regex(k, p, tail)) + _rematcher('(?:%s)' % _regex(k, p, tail)) except re.error: raise util.Abort(_("invalid pattern (%s): %s") % (k, p)) raise util.Abort(_("invalid pattern")) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/merge.py --- a/mercurial/merge.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/merge.py Wed Jul 18 19:08:25 2012 -0500 @@ -7,7 +7,7 @@ from node import nullid, nullrev, hex, bin from i18n import _ -import scmutil, util, filemerge, copies, subrepo +import error, scmutil, util, filemerge, copies, subrepo import errno, os, shutil class mergestate(object): @@ -198,9 +198,11 @@ elif pa == p2: # backwards pa = p1.p1() elif pa and repo.ui.configbool("merge", "followcopies", True): - copy, diverge = copies.mergecopies(repo, p1, p2, pa) + copy, diverge, renamedelete = copies.mergecopies(repo, p1, p2, pa) for of, fl in diverge.iteritems(): act("divergent renames", "dr", of, fl) + for of, fl in renamedelete.iteritems(): + act("rename and delete", "rd", of, fl) repo.ui.note(_("resolving manifests\n")) repo.ui.debug(" overwrite: %s, partial: %s\n" @@ -363,7 +365,8 @@ removed += 1 elif m == "m": # merge if f == '.hgsubstate': # subrepo states need updating - subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite) + subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), + overwrite) continue f2, fd, flags, move = a[2:] repo.wopener.audit(fd) @@ -408,6 +411,12 @@ "multiple times to:\n") % f) for nf in fl: repo.ui.warn(" %s\n" % nf) + elif m == "rd": # rename and delete + fl = a[2] + repo.ui.warn(_("note: possible conflict - %s was deleted " + "and renamed to:\n") % f) + for nf in fl: + repo.ui.warn(" %s\n" % nf) elif m == "e": # exec flags = a[2] repo.wopener.audit(f) @@ -479,7 +488,8 @@ if f: repo.dirstate.drop(f) -def update(repo, node, branchmerge, force, partial, ancestor=None): +def update(repo, node, branchmerge, force, partial, ancestor=None, + mergeancestor=False): """ Perform a merge between the working directory and the given node @@ -487,6 +497,10 @@ branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) + mergeancestor = if false, merging with an ancestor (fast-forward) + is only allowed between different named branches. This flag + is used by rebase extension as a temporary fix and should be + avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory @@ -523,8 +537,8 @@ if node is None: # tip of current branch try: - node = repo.branchtags()[wc.branch()] - except KeyError: + node = repo.branchtip(wc.branch()) + except error.RepoLookupError: if wc.branch() == "default": # no default branch! node = repo.lookup("tip") # update to tip else: @@ -547,7 +561,7 @@ raise util.Abort(_("merging with a working directory ancestor" " has no effect")) elif pa == p1: - if p1.branch() == p2.branch(): + if not mergeancestor and p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge"), hint=_("use 'hg update' " "or check 'hg heads'")) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/minirst.py --- a/mercurial/minirst.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/minirst.py Wed Jul 18 19:08:25 2012 -0500 @@ -658,7 +658,7 @@ return lines def maketable(data, indent=0, header=False): - '''Generate an RST table for the given table data''' + '''Generate an RST table for the given table data as a list of lines''' widths = [max(encoding.colwidth(e) for e in c) for c in zip(*data)] indent = ' ' * indent @@ -674,4 +674,4 @@ if header and len(data) > 1: out.insert(2, div) out.append(div) - return ''.join(out) + return out diff -r 98823bd0d697 -r a06e2681dd17 mercurial/mpatch.c --- a/mercurial/mpatch.c Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/mpatch.c Wed Jul 18 19:08:25 2012 -0500 @@ -20,6 +20,7 @@ of the GNU General Public License, incorporated herein by reference. */ +#define PY_SSIZE_T_CLEAN #include #include #include @@ -38,7 +39,7 @@ struct frag *base, *head, *tail; }; -static struct flist *lalloc(int size) +static struct flist *lalloc(Py_ssize_t size) { struct flist *a = NULL; @@ -68,7 +69,7 @@ } } -static int lsize(struct flist *a) +static Py_ssize_t lsize(struct flist *a) { return a->tail - a->head; } @@ -197,7 +198,7 @@ } /* decode a binary patch into a hunk list */ -static struct flist *decode(const char *bin, int len) +static struct flist *decode(const char *bin, Py_ssize_t len) { struct flist *l; struct frag *lt; @@ -236,9 +237,9 @@ } /* calculate the size of resultant text */ -static int calcsize(int len, struct flist *l) +static Py_ssize_t calcsize(Py_ssize_t len, struct flist *l) { - int outlen = 0, last = 0; + Py_ssize_t outlen = 0, last = 0; struct frag *f = l->head; while (f != l->tail) { @@ -258,7 +259,7 @@ return outlen; } -static int apply(char *buf, const char *orig, int len, struct flist *l) +static int apply(char *buf, const char *orig, Py_ssize_t len, struct flist *l) { struct frag *f = l->head; int last = 0; @@ -283,10 +284,9 @@ } /* recursively generate a patch of all bins between start and end */ -static struct flist *fold(PyObject *bins, int start, int end) +static struct flist *fold(PyObject *bins, Py_ssize_t start, Py_ssize_t end) { - int len; - Py_ssize_t blen; + Py_ssize_t len, blen; const char *buffer; if (start + 1 == end) { @@ -312,8 +312,7 @@ struct flist *patch; const char *in; char *out; - int len, outlen; - Py_ssize_t inlen; + Py_ssize_t len, outlen, inlen; if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins)) return NULL; @@ -357,7 +356,7 @@ patchedsize(PyObject *self, PyObject *args) { long orig, start, end, len, outlen = 0, last = 0; - int patchlen; + Py_ssize_t patchlen; char *bin, *binend, *data; if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen)) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/obsolete.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/obsolete.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,301 @@ +# obsolete.py - obsolete markers handling +# +# Copyright 2012 Pierre-Yves David +# Logilab SA +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +"""Obsolete markers handling + +An obsolete marker maps an old changeset to a list of new +changesets. If the list of new changesets is empty, the old changeset +is said to be "killed". Otherwise, the old changeset is being +"replaced" by the new changesets. + +Obsolete markers can be used to record and distribute changeset graph +transformations performed by history rewriting operations, and help +building new tools to reconciliate conflicting rewriting actions. To +facilitate conflicts resolution, markers include various annotations +besides old and news changeset identifiers, such as creation date or +author name. + + +Format +------ + +Markers are stored in an append-only file stored in +'.hg/store/obsstore'. + +The file starts with a version header: + +- 1 unsigned byte: version number, starting at zero. + + +The header is followed by the markers. Each marker is made of: + +- 1 unsigned byte: number of new changesets "R", could be zero. + +- 1 unsigned 32-bits integer: metadata size "M" in bytes. + +- 1 byte: a bit field. It is reserved for flags used in obsolete + markers common operations, to avoid repeated decoding of metadata + entries. + +- 20 bytes: obsoleted changeset identifier. + +- N*20 bytes: new changesets identifiers. + +- M bytes: metadata as a sequence of nul-terminated strings. Each + string contains a key and a value, separated by a color ':', without + additional encoding. Keys cannot contain '\0' or ':' and values + cannot contain '\0'. +""" +import struct +from mercurial import util, base85 +from i18n import _ + +_pack = struct.pack +_unpack = struct.unpack + + + +# data used for parsing and writing +_fmversion = 0 +_fmfixed = '>BIB20s' +_fmnode = '20s' +_fmfsize = struct.calcsize(_fmfixed) +_fnodesize = struct.calcsize(_fmnode) + +def _readmarkers(data): + """Read and enumerate markers from raw data""" + off = 0 + diskversion = _unpack('>B', data[off:off + 1])[0] + off += 1 + if diskversion != _fmversion: + raise util.Abort(_('parsing obsolete marker: unknown version %r') + % diskversion) + + # Loop on markers + l = len(data) + while off + _fmfsize <= l: + # read fixed part + cur = data[off:off + _fmfsize] + off += _fmfsize + nbsuc, mdsize, flags, pre = _unpack(_fmfixed, cur) + # read replacement + sucs = () + if nbsuc: + s = (_fnodesize * nbsuc) + cur = data[off:off + s] + sucs = _unpack(_fmnode * nbsuc, cur) + off += s + # read metadata + # (metadata will be decoded on demand) + metadata = data[off:off + mdsize] + if len(metadata) != mdsize: + raise util.Abort(_('parsing obsolete marker: metadata is too ' + 'short, %d bytes expected, got %d') + % (len(metadata), mdsize)) + off += mdsize + yield (pre, sucs, flags, metadata) + +def encodemeta(meta): + """Return encoded metadata string to string mapping. + + Assume no ':' in key and no '\0' in both key and value.""" + for key, value in meta.iteritems(): + if ':' in key or '\0' in key: + raise ValueError("':' and '\0' are forbidden in metadata key'") + if '\0' in value: + raise ValueError("':' are forbidden in metadata value'") + return '\0'.join(['%s:%s' % (k, meta[k]) for k in sorted(meta)]) + +def decodemeta(data): + """Return string to string dictionary from encoded version.""" + d = {} + for l in data.split('\0'): + if l: + key, value = l.split(':') + d[key] = value + return d + +class marker(object): + """Wrap obsolete marker raw data""" + + def __init__(self, repo, data): + # the repo argument will be used to create changectx in later version + self._repo = repo + self._data = data + self._decodedmeta = None + + def precnode(self): + """Precursor changeset node identifier""" + return self._data[0] + + def succnodes(self): + """List of successor changesets node identifiers""" + return self._data[1] + + def metadata(self): + """Decoded metadata dictionary""" + if self._decodedmeta is None: + self._decodedmeta = decodemeta(self._data[3]) + return self._decodedmeta + + def date(self): + """Creation date as (unixtime, offset)""" + parts = self.metadata()['date'].split(' ') + return (float(parts[0]), int(parts[1])) + +class obsstore(object): + """Store obsolete markers + + Markers can be accessed with two mappings: + - precursors: old -> set(new) + - successors: new -> set(old) + """ + + def __init__(self, sopener): + self._all = [] + # new markers to serialize + self.precursors = {} + self.successors = {} + self.sopener = sopener + data = sopener.tryread('obsstore') + if data: + self._load(_readmarkers(data)) + + def __iter__(self): + return iter(self._all) + + def __nonzero__(self): + return bool(self._all) + + def create(self, transaction, prec, succs=(), flag=0, metadata=None): + """obsolete: add a new obsolete marker + + * ensuring it is hashable + * check mandatory metadata + * encode metadata + """ + if metadata is None: + metadata = {} + if len(prec) != 20: + raise ValueError(prec) + for succ in succs: + if len(succ) != 20: + raise ValueError(succ) + marker = (str(prec), tuple(succs), int(flag), encodemeta(metadata)) + self.add(transaction, [marker]) + + def add(self, transaction, markers): + """Add new markers to the store + + Take care of filtering duplicate. + Return the number of new marker.""" + new = [m for m in markers if m not in self._all] + if new: + f = self.sopener('obsstore', 'ab') + try: + # Whether the file's current position is at the begin or at + # the end after opening a file for appending is implementation + # defined. So we must seek to the end before calling tell(), + # or we may get a zero offset for non-zero sized files on + # some platforms (issue3543). + f.seek(0, 2) # os.SEEK_END + offset = f.tell() + transaction.add('obsstore', offset) + # offset == 0: new file - add the version header + for bytes in _encodemarkers(new, offset == 0): + f.write(bytes) + finally: + # XXX: f.close() == filecache invalidation == obsstore rebuilt. + # call 'filecacheentry.refresh()' here + f.close() + self._load(new) + return len(new) + + def mergemarkers(self, transation, data): + markers = _readmarkers(data) + self.add(transation, markers) + + def _load(self, markers): + for mark in markers: + self._all.append(mark) + pre, sucs = mark[:2] + self.precursors.setdefault(pre, set()).add(mark) + for suc in sucs: + self.successors.setdefault(suc, set()).add(mark) + +def _encodemarkers(markers, addheader=False): + # Kept separate from flushmarkers(), it will be reused for + # markers exchange. + if addheader: + yield _pack('>B', _fmversion) + for marker in markers: + pre, sucs, flags, metadata = marker + nbsuc = len(sucs) + format = _fmfixed + (_fmnode * nbsuc) + data = [nbsuc, len(metadata), flags, pre] + data.extend(sucs) + yield _pack(format, *data) + yield metadata + +def listmarkers(repo): + """List markers over pushkey""" + if not repo.obsstore: + return {} + markers = _encodemarkers(repo.obsstore, True) + return {'dump': base85.b85encode(''.join(markers))} + +def pushmarker(repo, key, old, new): + """Push markers over pushkey""" + if key != 'dump': + repo.ui.warn(_('unknown key: %r') % key) + return 0 + if old: + repo.ui.warn(_('unexpected old value') % key) + return 0 + data = base85.b85decode(new) + lock = repo.lock() + try: + tr = repo.transaction('pushkey: obsolete markers') + try: + repo.obsstore.mergemarkers(tr, data) + tr.close() + return 1 + finally: + tr.release() + finally: + lock.release() + +def allmarkers(repo): + """all obsolete markers known in a repository""" + for markerdata in repo.obsstore: + yield marker(repo, markerdata) + +def precursormarkers(ctx): + """obsolete marker making this changeset obsolete""" + for data in ctx._repo.obsstore.precursors.get(ctx.node(), ()): + yield marker(ctx._repo, data) + +def successormarkers(ctx): + """obsolete marker marking this changeset as a successors""" + for data in ctx._repo.obsstore.successors.get(ctx.node(), ()): + yield marker(ctx._repo, data) + +def anysuccessors(obsstore, node): + """Yield every successor of + + This this a linear yield unsuitable to detect splitted changeset.""" + remaining = set([node]) + seen = set(remaining) + while remaining: + current = remaining.pop() + yield current + for mark in obsstore.precursors.get(current, ()): + for suc in mark[1]: + if suc not in seen: + seen.add(suc) + remaining.add(suc) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/parsers.c --- a/mercurial/parsers.c Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/parsers.c Wed Jul 18 19:08:25 2012 -0500 @@ -13,8 +13,10 @@ #include "util.h" -static int hexdigit(char c) +static inline int hexdigit(const char *p, Py_ssize_t off) { + char c = p[off]; + if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') @@ -32,8 +34,8 @@ static PyObject *unhexlify(const char *str, int len) { PyObject *ret; - const char *c; char *d; + int i; ret = PyBytes_FromStringAndSize(NULL, len / 2); @@ -42,9 +44,9 @@ d = PyBytes_AsString(ret); - for (c = str; c < str + len;) { - int hi = hexdigit(*c++); - int lo = hexdigit(*c++); + for (i = 0; i < len;) { + int hi = hexdigit(str, i++); + int lo = hexdigit(str, i++); *d++ = (hi << 4) | lo; } @@ -212,6 +214,154 @@ return ret; } +static inline int getintat(PyObject *tuple, int off, uint32_t *v) +{ + PyObject *o = PyTuple_GET_ITEM(tuple, off); + long val; + + if (PyInt_Check(o)) + val = PyInt_AS_LONG(o); + else if (PyLong_Check(o)) { + val = PyLong_AsLong(o); + if (val == -1 && PyErr_Occurred()) + return -1; + } else { + PyErr_SetString(PyExc_TypeError, "expected an int or long"); + return -1; + } + if (LONG_MAX > INT_MAX && (val > INT_MAX || val < INT_MIN)) { + PyErr_SetString(PyExc_OverflowError, + "Python value to large to convert to uint32_t"); + return -1; + } + *v = (uint32_t)val; + return 0; +} + +static PyObject *dirstate_unset; + +/* + * Efficiently pack a dirstate object into its on-disk format. + */ +static PyObject *pack_dirstate(PyObject *self, PyObject *args) +{ + PyObject *packobj = NULL; + PyObject *map, *copymap, *pl; + Py_ssize_t nbytes, pos, l; + PyObject *k, *v, *pn; + char *p, *s; + double now; + + if (!PyArg_ParseTuple(args, "O!O!Od:pack_dirstate", + &PyDict_Type, &map, &PyDict_Type, ©map, + &pl, &now)) + return NULL; + + if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) { + PyErr_SetString(PyExc_TypeError, "expected 2-element sequence"); + return NULL; + } + + /* Figure out how much we need to allocate. */ + for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) { + PyObject *c; + if (!PyString_Check(k)) { + PyErr_SetString(PyExc_TypeError, "expected string key"); + goto bail; + } + nbytes += PyString_GET_SIZE(k) + 17; + c = PyDict_GetItem(copymap, k); + if (c) { + if (!PyString_Check(c)) { + PyErr_SetString(PyExc_TypeError, + "expected string key"); + goto bail; + } + nbytes += PyString_GET_SIZE(c) + 1; + } + } + + packobj = PyString_FromStringAndSize(NULL, nbytes); + if (packobj == NULL) + goto bail; + + p = PyString_AS_STRING(packobj); + + pn = PySequence_ITEM(pl, 0); + if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { + PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash"); + goto bail; + } + memcpy(p, s, l); + p += 20; + pn = PySequence_ITEM(pl, 1); + if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { + PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash"); + goto bail; + } + memcpy(p, s, l); + p += 20; + + for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) { + uint32_t mode, size, mtime; + Py_ssize_t len, l; + PyObject *o; + char *s, *t; + + if (!PyTuple_Check(v) || PyTuple_GET_SIZE(v) != 4) { + PyErr_SetString(PyExc_TypeError, "expected a 4-tuple"); + goto bail; + } + o = PyTuple_GET_ITEM(v, 0); + if (PyString_AsStringAndSize(o, &s, &l) == -1 || l != 1) { + PyErr_SetString(PyExc_TypeError, "expected one byte"); + goto bail; + } + *p++ = *s; + if (getintat(v, 1, &mode) == -1) + goto bail; + if (getintat(v, 2, &size) == -1) + goto bail; + if (getintat(v, 3, &mtime) == -1) + goto bail; + if (*s == 'n' && mtime == (uint32_t)now) { + /* See dirstate.py:write for why we do this. */ + if (PyDict_SetItem(map, k, dirstate_unset) == -1) + goto bail; + mode = 0, size = -1, mtime = -1; + } + putbe32(mode, p); + putbe32(size, p + 4); + putbe32(mtime, p + 8); + t = p + 12; + p += 16; + len = PyString_GET_SIZE(k); + memcpy(p, PyString_AS_STRING(k), len); + p += len; + o = PyDict_GetItem(copymap, k); + if (o) { + *p++ = '\0'; + l = PyString_GET_SIZE(o); + memcpy(p, PyString_AS_STRING(o), l); + p += l; + len += l + 1; + } + putbe32((uint32_t)len, t); + } + + pos = p - PyString_AS_STRING(packobj); + if (pos != nbytes) { + PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld", + (long)pos, (long)nbytes); + goto bail; + } + + return packobj; +bail: + Py_XDECREF(packobj); + return NULL; +} + /* * A base-16 trie for fast node->rev mapping. * @@ -244,6 +394,7 @@ Py_ssize_t raw_length; /* original number of elements */ Py_ssize_t length; /* current number of elements */ PyObject *added; /* populated on demand */ + PyObject *headrevs; /* cache, invalidated on changes */ nodetree *nt; /* base-16 trie */ int ntlength; /* # nodes in use */ int ntcapacity; /* # nodes allocated */ @@ -273,6 +424,9 @@ static char *tuple_format = "kiiiiiis#"; #endif +/* A RevlogNG v1 index entry is 64 bytes long. */ +static const long v1_hdrsize = 64; + /* * Return a pointer to the beginning of a RevlogNG record. */ @@ -289,7 +443,7 @@ return self->offsets[pos]; } - return PyString_AS_STRING(self->data) + pos * 64; + return PyString_AS_STRING(self->data) + pos * v1_hdrsize; } /* @@ -385,7 +539,7 @@ Py_ssize_t length = index_length(self); const char *data; - if (pos == length - 1) + if (pos == length - 1 || pos == INT_MAX) return nullid; if (pos >= length) @@ -461,6 +615,7 @@ if (self->nt) nt_insert(self, node, (int)offset); + Py_CLEAR(self->headrevs); Py_RETURN_NONE; } @@ -469,12 +624,8 @@ if (self->cache) { Py_ssize_t i; - for (i = 0; i < self->raw_length; i++) { - if (self->cache[i]) { - Py_DECREF(self->cache[i]); - self->cache[i] = NULL; - } - } + for (i = 0; i < self->raw_length; i++) + Py_CLEAR(self->cache[i]); free(self->cache); self->cache = NULL; } @@ -486,6 +637,7 @@ free(self->nt); self->nt = NULL; } + Py_CLEAR(self->headrevs); } static PyObject *index_clearcaches(indexObject *self) @@ -506,13 +658,13 @@ return NULL; #define istat(__n, __d) \ - if (PyDict_SetItemString(obj, __d, PyInt_FromLong(self->__n)) == -1) \ + if (PyDict_SetItemString(obj, __d, PyInt_FromSsize_t(self->__n)) == -1) \ goto bail; if (self->added) { Py_ssize_t len = PyList_GET_SIZE(self->added); if (PyDict_SetItemString(obj, "index entries added", - PyInt_FromLong(len)) == -1) + PyInt_FromSsize_t(len)) == -1) goto bail; } @@ -536,7 +688,108 @@ return NULL; } -static inline int nt_level(const char *node, int level) +/* + * When we cache a list, we want to be sure the caller can't mutate + * the cached copy. + */ +static PyObject *list_copy(PyObject *list) +{ + Py_ssize_t len = PyList_GET_SIZE(list); + PyObject *newlist = PyList_New(len); + Py_ssize_t i; + + if (newlist == NULL) + return NULL; + + for (i = 0; i < len; i++) { + PyObject *obj = PyList_GET_ITEM(list, i); + Py_INCREF(obj); + PyList_SET_ITEM(newlist, i, obj); + } + + return newlist; +} + +static PyObject *index_headrevs(indexObject *self) +{ + Py_ssize_t i, len, addlen; + char *nothead = NULL; + PyObject *heads; + + if (self->headrevs) + return list_copy(self->headrevs); + + len = index_length(self) - 1; + heads = PyList_New(0); + if (heads == NULL) + goto bail; + if (len == 0) { + PyObject *nullid = PyInt_FromLong(-1); + if (nullid == NULL || PyList_Append(heads, nullid) == -1) { + Py_XDECREF(nullid); + goto bail; + } + goto done; + } + + nothead = calloc(len, 1); + if (nothead == NULL) + goto bail; + + for (i = 0; i < self->raw_length; i++) { + const char *data = index_deref(self, i); + int parent_1 = getbe32(data + 24); + int parent_2 = getbe32(data + 28); + if (parent_1 >= 0) + nothead[parent_1] = 1; + if (parent_2 >= 0) + nothead[parent_2] = 1; + } + + addlen = self->added ? PyList_GET_SIZE(self->added) : 0; + + for (i = 0; i < addlen; i++) { + PyObject *rev = PyList_GET_ITEM(self->added, i); + PyObject *p1 = PyTuple_GET_ITEM(rev, 5); + PyObject *p2 = PyTuple_GET_ITEM(rev, 6); + long parent_1, parent_2; + + if (!PyInt_Check(p1) || !PyInt_Check(p2)) { + PyErr_SetString(PyExc_TypeError, + "revlog parents are invalid"); + goto bail; + } + parent_1 = PyInt_AS_LONG(p1); + parent_2 = PyInt_AS_LONG(p2); + if (parent_1 >= 0) + nothead[parent_1] = 1; + if (parent_2 >= 0) + nothead[parent_2] = 1; + } + + for (i = 0; i < len; i++) { + PyObject *head; + + if (nothead[i]) + continue; + head = PyInt_FromLong(i); + if (head == NULL || PyList_Append(heads, head) == -1) { + Py_XDECREF(head); + goto bail; + } + } + +done: + self->headrevs = heads; + free(nothead); + return list_copy(self->headrevs); +bail: + Py_XDECREF(heads); + free(nothead); + return NULL; +} + +static inline int nt_level(const char *node, Py_ssize_t level) { int v = node[level>>1]; if (!(level & 1)) @@ -544,8 +797,17 @@ return v & 0xf; } -static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen) +/* + * Return values: + * + * -4: match is ambiguous (multiple candidates) + * -2: not found + * rest: valid rev + */ +static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen, + int hex) { + int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level; int level, maxlevel, off; if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0) @@ -554,27 +816,35 @@ if (self->nt == NULL) return -2; - maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2); + if (hex) + maxlevel = nodelen > 40 ? 40 : (int)nodelen; + else + maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2); for (level = off = 0; level < maxlevel; level++) { - int k = nt_level(node, level); + int k = getnybble(node, level); nodetree *n = &self->nt[off]; int v = n->children[k]; if (v < 0) { const char *n; + Py_ssize_t i; + v = -v - 1; n = index_node(self, v); if (n == NULL) return -2; - return memcmp(node, n, nodelen > 20 ? 20 : nodelen) - ? -2 : v; + for (i = level; i < maxlevel; i++) + if (getnybble(node, i) != nt_level(n, i)) + return -2; + return v; } if (v == 0) return -2; off = v; } - return -2; + /* multiple matches against an ambiguous prefix */ + return -4; } static int nt_new(indexObject *self) @@ -638,6 +908,26 @@ return -1; } +static int nt_init(indexObject *self) +{ + if (self->nt == NULL) { + self->ntcapacity = self->raw_length < 4 + ? 4 : self->raw_length / 2; + self->nt = calloc(self->ntcapacity, sizeof(nodetree)); + if (self->nt == NULL) { + PyErr_NoMemory(); + return -1; + } + self->ntlength = 1; + self->ntrev = (int)index_length(self) - 1; + self->ntlookups = 1; + self->ntmisses = 0; + if (nt_insert(self, nullid, INT_MAX) == -1) + return -1; + } + return 0; +} + /* * Return values: * @@ -651,23 +941,12 @@ int rev; self->ntlookups++; - rev = nt_find(self, node, nodelen); + rev = nt_find(self, node, nodelen, 0); if (rev >= -1) return rev; - if (self->nt == NULL) { - self->ntcapacity = self->raw_length < 4 - ? 4 : self->raw_length / 2; - self->nt = calloc(self->ntcapacity, sizeof(nodetree)); - if (self->nt == NULL) { - PyErr_SetString(PyExc_MemoryError, "out of memory"); - return -3; - } - self->ntlength = 1; - self->ntrev = (int)index_length(self) - 1; - self->ntlookups = 1; - self->ntmisses = 0; - } + if (nt_init(self) == -1) + return -3; /* * For the first handful of lookups, we scan the entire index, @@ -692,10 +971,14 @@ } else { for (rev = self->ntrev - 1; rev >= 0; rev--) { const char *n = index_node(self, rev); - if (n == NULL) + if (n == NULL) { + self->ntrev = rev + 1; return -2; - if (nt_insert(self, n, rev) == -1) + } + if (nt_insert(self, n, rev) == -1) { + self->ntrev = rev + 1; return -3; + } if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) { break; } @@ -763,6 +1046,77 @@ return NULL; } +static int nt_partialmatch(indexObject *self, const char *node, + Py_ssize_t nodelen) +{ + int rev; + + if (nt_init(self) == -1) + return -3; + + if (self->ntrev > 0) { + /* ensure that the radix tree is fully populated */ + for (rev = self->ntrev - 1; rev >= 0; rev--) { + const char *n = index_node(self, rev); + if (n == NULL) + return -2; + if (nt_insert(self, n, rev) == -1) + return -3; + } + self->ntrev = rev; + } + + return nt_find(self, node, nodelen, 1); +} + +static PyObject *index_partialmatch(indexObject *self, PyObject *args) +{ + const char *fullnode; + int nodelen; + char *node; + int rev, i; + + if (!PyArg_ParseTuple(args, "s#", &node, &nodelen)) + return NULL; + + if (nodelen < 4) { + PyErr_SetString(PyExc_ValueError, "key too short"); + return NULL; + } + + if (nodelen > 40) + nodelen = 40; + + for (i = 0; i < nodelen; i++) + hexdigit(node, i); + if (PyErr_Occurred()) { + /* input contains non-hex characters */ + PyErr_Clear(); + Py_RETURN_NONE; + } + + rev = nt_partialmatch(self, node, nodelen); + + switch (rev) { + case -4: + raise_revlog_error(); + case -3: + return NULL; + case -2: + Py_RETURN_NONE; + case -1: + return PyString_FromStringAndSize(nullid, 20); + } + + fullnode = index_node(self, rev); + if (fullnode == NULL) { + PyErr_Format(PyExc_IndexError, + "could not access rev %d", rev); + return NULL; + } + return PyString_FromStringAndSize(fullnode, 20); +} + static PyObject *index_m_get(indexObject *self, PyObject *args) { Py_ssize_t nodelen; @@ -819,10 +1173,8 @@ nt_insert(self, PyString_AS_STRING(node), -1); } - if (start == 0) { - Py_DECREF(self->added); - self->added = NULL; - } + if (start == 0) + Py_CLEAR(self->added); } /* @@ -833,6 +1185,7 @@ { Py_ssize_t start, stop, step, slicelength; Py_ssize_t length = index_length(self); + int ret = 0; if (PySlice_GetIndicesEx((PySliceObject*)item, length, &start, &stop, &step, &slicelength) < 0) @@ -878,7 +1231,9 @@ self->ntrev = (int)start; } self->length = start + 1; - return 0; + if (start < self->raw_length) + self->raw_length = start; + goto done; } if (self->nt) { @@ -886,10 +1241,12 @@ if (self->ntrev > start) self->ntrev = (int)start; } - return self->added - ? PyList_SetSlice(self->added, start - self->length + 1, - PyList_GET_SIZE(self->added), NULL) - : 0; + if (self->added) + ret = PyList_SetSlice(self->added, start - self->length + 1, + PyList_GET_SIZE(self->added), NULL); +done: + Py_CLEAR(self->headrevs); + return ret; } /* @@ -931,17 +1288,16 @@ { const char *data = PyString_AS_STRING(self->data); const char *end = data + PyString_GET_SIZE(self->data); - const long hdrsize = 64; - long incr = hdrsize; + long incr = v1_hdrsize; Py_ssize_t len = 0; - while (data + hdrsize <= end) { + while (data + v1_hdrsize <= end) { uint32_t comp_len; const char *old_data; /* 3rd element of header is length of compressed inline data */ comp_len = getbe32(data + 8); - incr = hdrsize + comp_len; - if (incr < hdrsize) + incr = v1_hdrsize + comp_len; + if (incr < v1_hdrsize) break; if (offsets) offsets[len] = data; @@ -952,7 +1308,7 @@ break; } - if (data != end && data + hdrsize != end) { + if (data != end && data + v1_hdrsize != end) { if (!PyErr_Occurred()) PyErr_SetString(PyExc_ValueError, "corrupt index file"); return -1; @@ -979,6 +1335,7 @@ self->cache = NULL; self->added = NULL; + self->headrevs = NULL; self->offsets = NULL; self->nt = NULL; self->ntlength = self->ntcapacity = 0; @@ -994,11 +1351,11 @@ self->raw_length = len; self->length = len + 1; } else { - if (size % 64) { + if (size % v1_hdrsize) { PyErr_SetString(PyExc_ValueError, "corrupt index file"); goto bail; } - self->raw_length = size / 64; + self->raw_length = size / v1_hdrsize; self->length = self->raw_length + 1; } @@ -1043,8 +1400,12 @@ "clear the index caches"}, {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"}, + {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS, + "get head revisions"}, {"insert", (PyCFunction)index_insert, METH_VARARGS, "insert an index entry"}, + {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS, + "match a potentially ambiguous node ID"}, {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"}, {NULL} /* Sentinel */ @@ -1143,6 +1504,7 @@ static char parsers_doc[] = "Efficient content parsing."; static PyMethodDef methods[] = { + {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"}, {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"}, {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"}, {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"}, @@ -1162,6 +1524,8 @@ -1, -1, -1, -1, nullid, 20); if (nullentry) PyObject_GC_UnTrack(nullentry); + + dirstate_unset = Py_BuildValue("ciii", 'n', 0, -1, -1); } #ifdef IS_PY3K diff -r 98823bd0d697 -r a06e2681dd17 mercurial/patch.py --- a/mercurial/patch.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/patch.py Wed Jul 18 19:08:25 2012 -0500 @@ -245,7 +245,7 @@ tmpfp.write('\n') elif not diffs_seen and message and content_type == 'text/plain': message += '\n' + payload - except: + except: # re-raises tmpfp.close() os.unlink(tmpname) raise @@ -534,7 +534,7 @@ if fname in self.data: return self.data[fname] if not self.opener or fname not in self.files: - raise IOError() + raise IOError fn, mode, copied = self.files[fname] return self.opener.read(fn), mode, copied @@ -560,7 +560,7 @@ try: fctx = self.ctx[fname] except error.LookupError: - raise IOError() + raise IOError flags = fctx.flags() return fctx.data(), ('l' in flags, 'x' in flags) @@ -858,7 +858,8 @@ self.lenb = int(self.lenb) self.starta = int(self.starta) self.startb = int(self.startb) - diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b) + diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, + self.b) # if we hit eof before finishing out the hunk, the last line will # be zero length. Lets try to fix it up. while len(self.hunk[-1]) == 0: @@ -1040,12 +1041,13 @@ hunk.append(l) return l.rstrip('\r\n') - line = getline(lr, self.hunk) - while line and not line.startswith('literal '): + while True: line = getline(lr, self.hunk) - if not line: - raise PatchError(_('could not extract "%s" binary data') - % self._fname) + if not line: + raise PatchError(_('could not extract "%s" binary data') + % self._fname) + if line.startswith('literal '): + break size = int(line[8:].rstrip()) dec = [] line = getline(lr, self.hunk) @@ -1595,12 +1597,12 @@ def lrugetfilectx(): cache = {} - order = [] + order = util.deque() def getfilectx(f, ctx): fctx = ctx.filectx(f, filelog=cache.get(f)) if f not in cache: if len(cache) > 20: - del cache[order.pop(0)] + del cache[order.popleft()] cache[f] = fctx.filelog() else: order.remove(f) @@ -1628,13 +1630,14 @@ if opts.git or opts.upgrade: copy = copies.pathcopies(ctx1, ctx2) - difffn = lambda opts, losedata: trydiff(repo, revs, ctx1, ctx2, - modified, added, removed, copy, getfilectx, opts, losedata, prefix) + difffn = (lambda opts, losedata: + trydiff(repo, revs, ctx1, ctx2, modified, added, removed, + copy, getfilectx, opts, losedata, prefix)) if opts.upgrade and not opts.git: try: def losedata(fn): if not losedatafn or not losedatafn(fn=fn): - raise GitDiffRequired() + raise GitDiffRequired # Buffer the whole output until we are sure it can be generated return list(difffn(opts.copy(git=False), losedata)) except GitDiffRequired: @@ -1665,7 +1668,7 @@ if line.startswith('@'): head = False else: - if line and not line[0] in ' +-@\\': + if line and line[0] not in ' +-@\\': head = True stripline = line if not head and line and line[0] in '+-': @@ -1870,7 +1873,8 @@ countwidth, count, pluses, minuses)) if stats: - output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n') + output.append(_(' %d files changed, %d insertions(+), ' + '%d deletions(-)\n') % (len(stats), totaladds, totalremoves)) return ''.join(output) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/peer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/peer.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,49 @@ +# peer.py - repository base classes for mercurial +# +# Copyright 2005, 2006 Matt Mackall +# Copyright 2006 Vadim Gelfer +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from i18n import _ +import error + +class peerrepository(object): + + def capable(self, name): + '''tell whether repo supports named capability. + return False if not supported. + if boolean capability, return True. + if string capability, return string.''' + caps = self._capabilities() + if name in caps: + return True + name_eq = name + '=' + for cap in caps: + if cap.startswith(name_eq): + return cap[len(name_eq):] + return False + + def requirecap(self, name, purpose): + '''raise an exception if the given capability is not present''' + if not self.capable(name): + raise error.CapabilityError( + _('cannot %s; remote repository does not ' + 'support the %r capability') % (purpose, name)) + + def local(self): + '''return peer as a localrepo, or None''' + return None + + def peer(self): + return self + + def peer(self): + return self + + def canpush(self): + return True + + def close(self): + pass diff -r 98823bd0d697 -r a06e2681dd17 mercurial/phases.py --- a/mercurial/phases.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/phases.py Wed Jul 18 19:08:25 2012 -0500 @@ -6,8 +6,8 @@ Logilab SA Augie Fackler - This software may be used and distributed according to the terms of the - GNU General Public License version 2 or any later version. + This software may be used and distributed according to the terms + of the GNU General Public License version 2 or any later version. --- @@ -17,17 +17,17 @@ Basic Concept ============= -A 'changeset phases' is an indicator that tells us how a changeset is -manipulated and communicated. The details of each phase is described below, -here we describe the properties they have in common. +A 'changeset phase' is an indicator that tells us how a changeset is +manipulated and communicated. The details of each phase is described +below, here we describe the properties they have in common. -Like bookmarks, phases are not stored in history and thus are not permanent and -leave no audit trail. +Like bookmarks, phases are not stored in history and thus are not +permanent and leave no audit trail. -First, no changeset can be in two phases at once. Phases are ordered, so they -can be considered from lowest to highest. The default, lowest phase is 'public' -- this is the normal phase of existing changesets. A child changeset can not be -in a lower phase than its parents. +First, no changeset can be in two phases at once. Phases are ordered, +so they can be considered from lowest to highest. The default, lowest +phase is 'public' - this is the normal phase of existing changesets. A +child changeset can not be in a lower phase than its parents. These phases share a hierarchy of traits: @@ -36,25 +36,26 @@ draft: X secret: -local commits are draft by default +Local commits are draft by default. + +Phase Movement and Exchange +=========================== -Phase movement and exchange -============================ - -Phase data are exchanged by pushkey on pull and push. Some server have a -publish option set, we call them publishing server. Pushing to such server make -draft changeset publish. +Phase data is exchanged by pushkey on pull and push. Some servers have +a publish option set, we call such a server a "publishing server". +Pushing a draft changeset to a publishing server changes the phase to +public. A small list of fact/rules define the exchange of phase: * old client never changes server states * pull never changes server states -* publish and old server csets are seen as public by client +* publish and old server changesets are seen as public by client +* any secret changeset seen in another repository is lowered to at + least draft -* Any secret changeset seens in another repository is lowered to at least draft - - -Here is the final table summing up the 49 possible usecase of phase exchange: +Here is the final table summing up the 49 possible use cases of phase +exchange: server old publish non-publish @@ -81,152 +82,232 @@ * N = new/not present, * P = public, * D = draft, - * X = not tracked (ie: the old client or server has no internal way of - recording the phase.) + * X = not tracked (i.e., the old client or server has no internal + way of recording the phase.) passive = only pushes A cell here can be read like this: - "When a new client pushes a draft changeset (D) to a publishing server - where it's not present (N), it's marked public on both sides (P/P)." + "When a new client pushes a draft changeset (D) to a publishing + server where it's not present (N), it's marked public on both + sides (P/P)." -Note: old client behave as publish server with Draft only content +Note: old client behave as a publishing server with draft only content - other people see it as public - content is pushed as draft """ import errno -from node import nullid, bin, hex, short +from node import nullid, nullrev, bin, hex, short from i18n import _ +import util allphases = public, draft, secret = range(3) trackedphases = allphases[1:] phasenames = ['public', 'draft', 'secret'] -def readroots(repo): - """Read phase roots from disk""" +def _filterunknown(ui, changelog, phaseroots): + """remove unknown nodes from the phase boundary + + Nothing is lost as unknown nodes only hold data for their descendants. + """ + updated = False + nodemap = changelog.nodemap # to filter unknown nodes + for phase, nodes in enumerate(phaseroots): + missing = [node for node in nodes if node not in nodemap] + if missing: + for mnode in missing: + ui.debug( + 'removing unknown node %s from %i-phase boundary\n' + % (short(mnode), phase)) + nodes.symmetric_difference_update(missing) + updated = True + return updated + +def _readroots(repo, phasedefaults=None): + """Read phase roots from disk + + phasedefaults is a list of fn(repo, roots) callable, which are + executed if the phase roots file does not exist. When phases are + being initialized on an existing repository, this could be used to + set selected changesets phase to something else than public. + + Return (roots, dirty) where dirty is true if roots differ from + what is being stored. + """ + dirty = False roots = [set() for i in allphases] try: f = repo.sopener('phaseroots') try: for line in f: - phase, nh = line.strip().split() + phase, nh = line.split() roots[int(phase)].add(bin(nh)) finally: f.close() except IOError, inst: if inst.errno != errno.ENOENT: raise - for f in repo._phasedefaults: - roots = f(repo, roots) - repo._dirtyphases = True - return roots + if phasedefaults: + for f in phasedefaults: + roots = f(repo, roots) + dirty = True + if _filterunknown(repo.ui, repo.changelog, roots): + dirty = True + return roots, dirty + +class phasecache(object): + def __init__(self, repo, phasedefaults, _load=True): + if _load: + # Cheap trick to allow shallow-copy without copy module + self.phaseroots, self.dirty = _readroots(repo, phasedefaults) + self.opener = repo.sopener + self._phaserevs = None + + def copy(self): + # Shallow copy meant to ensure isolation in + # advance/retractboundary(), nothing more. + ph = phasecache(None, None, _load=False) + ph.phaseroots = self.phaseroots[:] + ph.dirty = self.dirty + ph.opener = self.opener + ph._phaserevs = self._phaserevs + return ph -def writeroots(repo): - """Write phase roots from disk""" - f = repo.sopener('phaseroots', 'w', atomictemp=True) - try: - for phase, roots in enumerate(repo._phaseroots): - for h in roots: - f.write('%i %s\n' % (phase, hex(h))) - repo._dirtyphases = False - finally: - f.close() + def replace(self, phcache): + for a in 'phaseroots dirty opener _phaserevs'.split(): + setattr(self, a, getattr(phcache, a)) + + def getphaserevs(self, repo, rebuild=False): + if rebuild or self._phaserevs is None: + revs = [public] * len(repo.changelog) + for phase in trackedphases: + roots = map(repo.changelog.rev, self.phaseroots[phase]) + if roots: + for rev in roots: + revs[rev] = phase + for rev in repo.changelog.descendants(roots): + revs[rev] = phase + self._phaserevs = revs + return self._phaserevs + + def phase(self, repo, rev): + # We need a repo argument here to be able to build _phaserev + # if necessary. The repository instance is not stored in + # phasecache to avoid reference cycles. The changelog instance + # is not stored because it is a filecache() property and can + # be replaced without us being notified. + if rev == nullrev: + return public + if self._phaserevs is None or rev >= len(self._phaserevs): + self._phaserevs = self.getphaserevs(repo, rebuild=True) + return self._phaserevs[rev] -def filterunknown(repo, phaseroots=None): - """remove unknown nodes from the phase boundary + def write(self): + if not self.dirty: + return + f = self.opener('phaseroots', 'w', atomictemp=True) + try: + for phase, roots in enumerate(self.phaseroots): + for h in roots: + f.write('%i %s\n' % (phase, hex(h))) + finally: + f.close() + self.dirty = False + + def _updateroots(self, phase, newroots): + self.phaseroots[phase] = newroots + self._phaserevs = None + self.dirty = True + + def advanceboundary(self, repo, targetphase, nodes): + # Be careful to preserve shallow-copied values: do not update + # phaseroots values, replace them. - no data is lost as unknown node only old data for their descentants - """ - if phaseroots is None: - phaseroots = repo._phaseroots - nodemap = repo.changelog.nodemap # to filter unknown nodes - for phase, nodes in enumerate(phaseroots): - missing = [node for node in nodes if node not in nodemap] - if missing: - for mnode in missing: - repo.ui.debug( - 'removing unknown node %s from %i-phase boundary\n' - % (short(mnode), phase)) - nodes.symmetric_difference_update(missing) - repo._dirtyphases = True + delroots = [] # set of root deleted by this path + for phase in xrange(targetphase + 1, len(allphases)): + # filter nodes that are not in a compatible phase already + nodes = [n for n in nodes + if self.phase(repo, repo[n].rev()) >= phase] + if not nodes: + break # no roots to move anymore + olds = self.phaseroots[phase] + roots = set(ctx.node() for ctx in repo.set( + 'roots((%ln::) - (%ln::%ln))', olds, olds, nodes)) + if olds != roots: + self._updateroots(phase, roots) + # some roots may need to be declared for lower phases + delroots.extend(olds - roots) + # declare deleted root in the target phase + if targetphase != 0: + self.retractboundary(repo, targetphase, delroots) + + def retractboundary(self, repo, targetphase, nodes): + # Be careful to preserve shallow-copied values: do not update + # phaseroots values, replace them. + + currentroots = self.phaseroots[targetphase] + newroots = [n for n in nodes + if self.phase(repo, repo[n].rev()) < targetphase] + if newroots: + if nullid in newroots: + raise util.Abort(_('cannot change null revision phase')) + currentroots = currentroots.copy() + currentroots.update(newroots) + ctxs = repo.set('roots(%ln::)', currentroots) + currentroots.intersection_update(ctx.node() for ctx in ctxs) + self._updateroots(targetphase, currentroots) def advanceboundary(repo, targetphase, nodes): """Add nodes to a phase changing other nodes phases if necessary. - This function move boundary *forward* this means that all nodes are set - in the target phase or kept in a *lower* phase. + This function move boundary *forward* this means that all nodes + are set in the target phase or kept in a *lower* phase. Simplify boundary to contains phase roots only.""" - delroots = [] # set of root deleted by this path - for phase in xrange(targetphase + 1, len(allphases)): - # filter nodes that are not in a compatible phase already - # XXX rev phase cache might have been invalidated by a previous loop - # XXX we need to be smarter here - nodes = [n for n in nodes if repo[n].phase() >= phase] - if not nodes: - break # no roots to move anymore - roots = repo._phaseroots[phase] - olds = roots.copy() - ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes)) - roots.clear() - roots.update(ctx.node() for ctx in ctxs) - if olds != roots: - # invalidate cache (we probably could be smarter here - if '_phaserev' in vars(repo): - del repo._phaserev - repo._dirtyphases = True - # some roots may need to be declared for lower phases - delroots.extend(olds - roots) - # declare deleted root in the target phase - if targetphase != 0: - retractboundary(repo, targetphase, delroots) - + phcache = repo._phasecache.copy() + phcache.advanceboundary(repo, targetphase, nodes) + repo._phasecache.replace(phcache) def retractboundary(repo, targetphase, nodes): - """Set nodes back to a phase changing other nodes phases if necessary. + """Set nodes back to a phase changing other nodes phases if + necessary. - This function move boundary *backward* this means that all nodes are set - in the target phase or kept in a *higher* phase. + This function move boundary *backward* this means that all nodes + are set in the target phase or kept in a *higher* phase. Simplify boundary to contains phase roots only.""" - currentroots = repo._phaseroots[targetphase] - newroots = [n for n in nodes if repo[n].phase() < targetphase] - if newroots: - currentroots.update(newroots) - ctxs = repo.set('roots(%ln::)', currentroots) - currentroots.intersection_update(ctx.node() for ctx in ctxs) - if '_phaserev' in vars(repo): - del repo._phaserev - repo._dirtyphases = True - + phcache = repo._phasecache.copy() + phcache.retractboundary(repo, targetphase, nodes) + repo._phasecache.replace(phcache) def listphases(repo): - """List phases root for serialisation over pushkey""" + """List phases root for serialization over pushkey""" keys = {} value = '%i' % draft - for root in repo._phaseroots[draft]: + for root in repo._phasecache.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 - # a publishing repo, the client still need to push phase boundary + # 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 a publishing repo, the client still need to + # push phase boundary # - # Push do not only push changeset. It also push phase data. New - # phase data may apply to common changeset which won't be push (as they - # are common). Here is a very simple example: + # Push do not only push changeset. It also push phase data. + # New phase data may apply to common changeset which won't be + # push (as they are common). Here is a very simple example: # # 1) repo A push changeset X as draft to repo B # 2) repo B make changeset X public - # 3) repo B push to repo A. X is not pushed but the data that X as now - # public should + # 3) repo B push to repo A. X is not pushed but the data that + # X as now public should # - # The server can't handle it on it's own as it has no idea of client - # phase data. + # The server can't handle it on it's own as it has no idea of + # client phase data. keys['publishing'] = 'True' return keys @@ -248,43 +329,6 @@ finally: lock.release() -def visibleheads(repo): - """return the set of visible head of this repo""" - # XXX we want a cache on this - sroots = repo._phaseroots[secret] - if sroots: - # XXX very slow revset. storing heads or secret "boundary" would help. - revset = repo.set('heads(not (%ln::))', sroots) - - vheads = [ctx.node() for ctx in revset] - if not vheads: - vheads.append(nullid) - else: - vheads = repo.heads() - return vheads - -def visiblebranchmap(repo): - """return a branchmap for the visible set""" - # XXX Recomputing this data on the fly is very slow. We should build a - # XXX cached version while computin the standard branchmap version. - sroots = repo._phaseroots[secret] - if sroots: - vbranchmap = {} - for branch, nodes in repo.branchmap().iteritems(): - # search for secret heads. - for n in nodes: - if repo[n].phase() >= secret: - nodes = None - break - # if secreat heads where found we must compute them again - if nodes is None: - s = repo.set('heads(branch(%s) - secret())', branch) - nodes = [c.node() for c in s] - vbranchmap[branch] = nodes - else: - vbranchmap = repo.branchmap() - return vbranchmap - def analyzeremotephases(repo, subset, roots): """Compute phases heads and root in a subset of node from root dict diff -r 98823bd0d697 -r a06e2681dd17 mercurial/posix.py --- a/mercurial/posix.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/posix.py Wed Jul 18 19:08:25 2012 -0500 @@ -6,6 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ +import encoding import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata posixfile = open @@ -164,9 +165,6 @@ st2 = os.lstat(fpath2) return st1.st_dev == st2.st_dev -encodinglower = None -encodingupper = None - # os.path.normcase is a no-op, which doesn't help us on non-native filesystems def normcase(path): return path.lower() @@ -255,7 +253,7 @@ pathlen = len(path) if (pathlen == 0) or (path[0] != os.sep): # treat as relative - return encodingupper(path) + return encoding.upper(path) # to preserve case of mountpoint part for mp in cygwinmountpoints: @@ -266,9 +264,9 @@ if mplen == pathlen: # mount point itself return mp if path[mplen] == os.sep: - return mp + encodingupper(path[mplen:]) + return mp + encoding.upper(path[mplen:]) - return encodingupper(path) + return encoding.upper(path) # Cygwin translates native ACLs to POSIX permissions, # but these translations are not supported by native diff -r 98823bd0d697 -r a06e2681dd17 mercurial/pure/mpatch.py --- a/mercurial/pure/mpatch.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/pure/mpatch.py Wed Jul 18 19:08:25 2012 -0500 @@ -85,10 +85,10 @@ p1, p2, l = struct.unpack(">lll", m.read(12)) pull(new, frags, p1 - last) # what didn't change pull([], frags, p2 - p1) # what got deleted - new.append((l, pos + 12)) # what got added + new.append((l, pos + 12)) # what got added pos += l + 12 last = p2 - frags.extend(reversed(new)) # what was left at the end + frags.extend(reversed(new)) # what was left at the end t = collect(b2, frags) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/pure/osutil.py --- a/mercurial/pure/osutil.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/pure/osutil.py Wed Jul 18 19:08:25 2012 -0500 @@ -119,7 +119,7 @@ flags = _O_TEXT m0 = mode[0] - if m0 == 'r' and not '+' in mode: + if m0 == 'r' and '+' not in mode: flags |= _O_RDONLY access = _GENERIC_READ else: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/pushkey.py --- a/mercurial/pushkey.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/pushkey.py Wed Jul 18 19:08:25 2012 -0500 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import bookmarks, phases +import bookmarks, phases, obsolete def _nslist(repo): n = {} @@ -16,6 +16,7 @@ _namespaces = {"namespaces": (lambda *x: False, _nslist), "bookmarks": (bookmarks.pushbookmark, bookmarks.listbookmarks), "phases": (phases.pushphase, phases.listphases), + "obsolete": (obsolete.pushmarker, obsolete.listmarkers), } def register(namespace, pushkey, listkeys): diff -r 98823bd0d697 -r a06e2681dd17 mercurial/repair.py --- a/mercurial/repair.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/repair.py Wed Jul 18 19:08:25 2012 -0500 @@ -6,7 +6,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import changegroup, bookmarks, phases +from mercurial import changegroup, bookmarks from mercurial.node import short from mercurial.i18n import _ import os @@ -38,14 +38,14 @@ """return the changesets which will be broken by the truncation""" s = set() def collectone(revlog): - links = (revlog.linkrev(i) for i in revlog) + linkgen = (revlog.linkrev(i) for i in revlog) # find the truncation point of the revlog - for lrev in links: + for lrev in linkgen: if lrev >= striprev: break # see if any revision after this point has a linkrev # less than striprev (those will be broken by strip) - for lrev in links: + for lrev in linkgen: if lrev < striprev: s.add(lrev) @@ -56,6 +56,11 @@ return s def strip(ui, repo, nodelist, backup="all", topic='backup'): + # It simplifies the logic around updating the branchheads cache if we only + # have to consider the effect of the stripped revisions and not revisions + # missing because the cache is out-of-date. + repo.updatebranchcache() + cl = repo.changelog # TODO handle undo of merge sets if isinstance(nodelist, str): @@ -63,6 +68,17 @@ striplist = [cl.rev(node) for node in nodelist] striprev = min(striplist) + # Generate set of branches who will have nodes stripped. + striprevs = repo.revs("%ld::", striplist) + stripbranches = set([repo[rev].branch() for rev in striprevs]) + + # Set of potential new heads resulting from the strip. The parents of any + # node removed could be a new head because the node to be removed could have + # been the only child of the parent. + newheadrevs = repo.revs("parents(%ld::) - %ld::", striprevs, striprevs) + newheadnodes = set([cl.node(rev) for rev in newheadrevs]) + newheadbranches = set([repo[rev].branch() for rev in newheadrevs]) + keeppartialbundle = backup == 'strip' # Some revisions with rev > striprev may not be descendants of striprev. @@ -74,7 +90,7 @@ # base = revision in the set that has no ancestor in the set) tostrip = set(striplist) for rev in striplist: - for desc in cl.descendants(rev): + for desc in cl.descendants([rev]): tostrip.add(desc) files = _collectfiles(repo, striprev) @@ -91,7 +107,7 @@ # compute base nodes if saverevs: - descendants = set(cl.descendants(*saverevs)) + descendants = set(cl.descendants(saverevs)) saverevs.difference_update(descendants) savebases = [cl.node(r) for r in saverevs] stripbases = [cl.node(r) for r in tostrip] @@ -131,7 +147,7 @@ file, troffset, ignore = tr.entries[i] repo.sopener(file, 'a').truncate(troffset) tr.close() - except: + except: # re-raises tr.abort() raise @@ -160,7 +176,7 @@ for m in updatebm: bm[m] = repo['.'].node() bookmarks.write(repo) - except: + except: # re-raises if backupfile: ui.warn(_("strip failed, full bundle stored in '%s'\n") % backupfile) @@ -169,8 +185,11 @@ % chgrpfile) raise - repo.destroyed() + if len(stripbranches) == 1 and len(newheadbranches) == 1 \ + and stripbranches == newheadbranches: + repo.destroyed(newheadnodes) + else: + # Multiple branches involved in strip. Will allow branchcache to become + # invalid and later on rebuilt from scratch + repo.destroyed() - # remove potential unknown phase - # XXX using to_strip data would be faster - phases.filterunknown(repo) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/repo.py --- a/mercurial/repo.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -# repo.py - repository base classes for mercurial -# -# Copyright 2005, 2006 Matt Mackall -# Copyright 2006 Vadim Gelfer -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -from i18n import _ -import error - -class repository(object): - def capable(self, name): - '''tell whether repo supports named capability. - return False if not supported. - if boolean capability, return True. - if string capability, return string.''' - if name in self.capabilities: - return True - name_eq = name + '=' - for cap in self.capabilities: - if cap.startswith(name_eq): - return cap[len(name_eq):] - return False - - def requirecap(self, name, purpose): - '''raise an exception if the given capability is not present''' - if not self.capable(name): - raise error.CapabilityError( - _('cannot %s; remote repository does not ' - 'support the %r capability') % (purpose, name)) - - def local(self): - return False - - def cancopy(self): - return self.local() - - def close(self): - pass diff -r 98823bd0d697 -r a06e2681dd17 mercurial/revlog.py --- a/mercurial/revlog.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/revlog.py Wed Jul 18 19:08:25 2012 -0500 @@ -75,35 +75,6 @@ s.update(text) return s.digest() -def compress(text): - """ generate a possibly-compressed representation of text """ - if not text: - return ("", text) - l = len(text) - bin = None - if l < 44: - pass - elif l > 1000000: - # zlib makes an internal copy, thus doubling memory usage for - # large files, so lets do this in pieces - z = zlib.compressobj() - p = [] - pos = 0 - while pos < l: - pos2 = pos + 2**20 - p.append(z.compress(text[pos:pos2])) - pos = pos2 - p.append(z.flush()) - if sum(map(len, p)) < l: - bin = "".join(p) - else: - bin = _compress(text) - if bin is None or len(bin) > l: - if text[0] == '\0': - return ("", text) - return ('u', text) - return ("", bin) - def decompress(bin): """ decompress the given input """ if not bin: @@ -112,7 +83,10 @@ if t == '\0': return bin if t == 'x': - return _decompress(bin) + try: + return _decompress(bin) + except zlib.error, e: + raise RevlogError(_("revlog decompress error: %s") % str(e)) if t == 'u': return bin[1:] raise RevlogError(_("unknown compression type %r") % t) @@ -358,47 +332,35 @@ return len(t) size = rawsize - def reachable(self, node, stop=None): - """return the set of all nodes ancestral to a given node, including - the node itself, stopping when stop is matched""" - reachable = set((node,)) - visit = [node] - if stop: - stopn = self.rev(stop) - else: - stopn = 0 - while visit: - n = visit.pop(0) - if n == stop: - continue - if n == nullid: - continue - for p in self.parents(n): - if self.rev(p) < stopn: - continue - if p not in reachable: - reachable.add(p) - visit.append(p) - return reachable - - def ancestors(self, *revs): + def ancestors(self, revs, stoprev=0): """Generate the ancestors of 'revs' in reverse topological order. + Does not generate revs lower than stoprev. Yield a sequence of revision numbers starting with the parents of each revision in revs, i.e., each revision is *not* considered an ancestor of itself. Results are in breadth-first order: parents of each rev in revs, then parents of those, etc. Result does not include the null revision.""" - visit = list(revs) + visit = util.deque(revs) seen = set([nullrev]) while visit: - for parent in self.parentrevs(visit.pop(0)): + for parent in self.parentrevs(visit.popleft()): + if parent < stoprev: + continue if parent not in seen: visit.append(parent) seen.add(parent) yield parent - def descendants(self, *revs): + def incancestors(self, revs, stoprev=0): + """Identical to ancestors() except it also generates the + revisions, 'revs'""" + for rev in revs: + yield rev + for rev in self.ancestors(revs, stoprev): + yield rev + + def descendants(self, revs): """Generate the descendants of 'revs' in revision order. Yield a sequence of revision numbers starting with a child of @@ -441,15 +403,15 @@ heads = [self.rev(n) for n in heads] # we want the ancestors, but inclusive - has = set(self.ancestors(*common)) + has = set(self.ancestors(common)) has.add(nullrev) has.update(common) # take all ancestors from heads that aren't in has missing = set() - visit = [r for r in heads if r not in has] + visit = util.deque(r for r in heads if r not in has) while visit: - r = visit.pop(0) + r = visit.popleft() if r in missing: continue else: @@ -635,6 +597,10 @@ return (orderedout, roots, heads) def headrevs(self): + try: + return self.index.headrevs() + except AttributeError: + pass count = len(self) if not count: return [nullrev] @@ -696,7 +662,7 @@ def descendant(self, start, end): if start == nullrev: return True - for i in self.descendants(start): + for i in self.descendants([start]): if i == end: return True elif i > end: @@ -722,7 +688,7 @@ return self.node(c) def _match(self, id): - if isinstance(id, (long, int)): + if isinstance(id, int): # rev return self.node(id) if len(id) == 20: @@ -756,6 +722,15 @@ pass def _partialmatch(self, id): + try: + return self.index.partialmatch(id) + except RevlogError: + # parsers.c radix tree lookup gave multiple matches + raise LookupError(id, self.indexfile, _("ambiguous identifier")) + except (AttributeError, ValueError): + # we are pure python, or key was too short to search radix tree + pass + if id in self._pcache: return self._pcache[id] @@ -1004,6 +979,35 @@ dfh.close() ifh.close() + def compress(self, text): + """ generate a possibly-compressed representation of text """ + if not text: + return ("", text) + l = len(text) + bin = None + if l < 44: + pass + elif l > 1000000: + # zlib makes an internal copy, thus doubling memory usage for + # large files, so lets do this in pieces + z = zlib.compressobj() + p = [] + pos = 0 + while pos < l: + pos2 = pos + 2**20 + p.append(z.compress(text[pos:pos2])) + pos = pos2 + p.append(z.flush()) + if sum(map(len, p)) < l: + bin = "".join(p) + else: + bin = _compress(text) + if bin is None or len(bin) > l: + if text[0] == '\0': + return ("", text) + return ('u', text) + return ("", bin) + def _addrevision(self, node, text, transaction, link, p1, p2, cachedelta, ifh, dfh): """internal function to add revisions to the log @@ -1036,7 +1040,7 @@ t = buildtext() ptext = self.revision(self.node(rev)) delta = mdiff.textdiff(ptext, t) - data = compress(delta) + data = self.compress(delta) l = len(data[1]) + len(data[0]) if basecache[0] == rev: chainbase = basecache[1] @@ -1080,7 +1084,7 @@ textlen = len(text) if d is None or dist > textlen * 2: text = buildtext() - data = compress(text) + data = self.compress(text) l = len(data[1]) + len(data[0]) base = chainbase = curr @@ -1199,7 +1203,7 @@ continue for p in (p1, p2): - if not p in self.nodemap: + if p not in self.nodemap: raise LookupError(p, self.indexfile, _('unknown parent')) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/revset.py --- a/mercurial/revset.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/revset.py Wed Jul 18 19:08:25 2012 -0500 @@ -17,10 +17,10 @@ """Like revlog.ancestors(), but supports followfirst.""" cut = followfirst and 1 or None cl = repo.changelog - visit = list(revs) + visit = util.deque(revs) seen = set([node.nullrev]) while visit: - for parent in cl.parentrevs(visit.pop(0))[:cut]: + for parent in cl.parentrevs(visit.popleft())[:cut]: if parent not in seen: visit.append(parent) seen.add(parent) @@ -47,6 +47,36 @@ yield i break +def _revsbetween(repo, roots, heads): + """Return all paths between roots and heads, inclusive of both endpoint + sets.""" + if not roots: + return [] + parentrevs = repo.changelog.parentrevs + visit = heads[:] + reachable = set() + seen = {} + minroot = min(roots) + roots = set(roots) + # open-code the post-order traversal due to the tiny size of + # sys.getrecursionlimit() + while visit: + rev = visit.pop() + if rev in roots: + reachable.add(rev) + parents = parentrevs(rev) + seen[rev] = parents + for parent in parents: + if parent >= minroot and parent not in seen: + visit.append(parent) + if not reachable: + return [] + for rev in sorted(seen): + for parent in seen[rev]: + if parent in reachable: + reachable.add(rev) + return sorted(reachable) + elements = { "(": (20, ("group", 1, ")"), ("func", 1, ")")), "~": (18, None, ("ancestor", 18)), @@ -108,7 +138,8 @@ pos += 1 else: raise error.ParseError(_("unterminated string"), s) - elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword + # gather up a symbol/keyword + elif c.isalnum() or c in '._' or ord(c) > 127: s = pos pos += 1 while pos < l: # find end of symbol @@ -155,6 +186,16 @@ raise error.ParseError(_("missing argument")) return methods[x[0]](repo, subset, *x[1:]) +def _getrevsource(repo, r): + extra = repo[r].extra() + for label in ('source', 'transplant_source', 'rebase_source'): + if label in extra: + try: + return repo[extra[label]].rev() + except error.RepoLookupError: + pass + return None + # operator methods def stringset(repo, subset, x): @@ -190,6 +231,14 @@ s = set(subset) return [x for x in r if x in s] +def dagrange(repo, subset, x, y): + if subset: + r = range(len(repo)) + xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y)) + s = set(subset) + return [r for r in xs if r in s] + return [] + def andset(repo, subset, x, y): return getset(repo, getset(repo, subset, x), y) @@ -257,7 +306,8 @@ def ancestorspec(repo, subset, x, n): """``set~n`` - Changesets that are the Nth ancestor (first parents only) of a changeset in set. + Changesets that are the Nth ancestor (first parents only) of a changeset + in set. """ try: n = int(n[1]) @@ -277,7 +327,8 @@ """ # i18n: "author" is a keyword n = encoding.lower(getstring(x, _("author requires a string"))) - return [r for r in subset if n in encoding.lower(repo[r].user())] + kind, pattern, matcher = _substringmatcher(n) + return [r for r in subset if matcher(encoding.lower(repo[r].user()))] def bisect(repo, subset, x): """``bisect(string)`` @@ -289,6 +340,7 @@ - ``pruned`` : csets that are goods, bads or skipped - ``untested`` : csets whose fate is yet unknown - ``ignored`` : csets ignored due to DAG topology + - ``current`` : the cset currently being bisected """ status = getstring(x, _("bisect requires a string")).lower() state = set(hbisect.get(repo, status)) @@ -302,6 +354,10 @@ def bookmark(repo, subset, x): """``bookmark([name])`` The named bookmark or all bookmarks. + + If `name` starts with `re:`, the remainder of the name is treated as + a regular expression. To match a bookmark that actually starts with `re:`, + use the prefix `literal:`. """ # i18n: "bookmark" is a keyword args = getargs(x, 0, 1, _('bookmark takes one or no arguments')) @@ -309,11 +365,26 @@ bm = getstring(args[0], # i18n: "bookmark" is a keyword _('the argument to bookmark must be a string')) - bmrev = bookmarksmod.listbookmarks(repo).get(bm, None) - if not bmrev: - raise util.Abort(_("bookmark '%s' does not exist") % bm) - bmrev = repo[bmrev].rev() - return [r for r in subset if r == bmrev] + kind, pattern, matcher = _stringmatcher(bm) + if kind == 'literal': + bmrev = bookmarksmod.listbookmarks(repo).get(bm, None) + if not bmrev: + raise util.Abort(_("bookmark '%s' does not exist") % bm) + bmrev = repo[bmrev].rev() + return [r for r in subset if r == bmrev] + else: + matchrevs = set() + for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems(): + if matcher(name): + matchrevs.add(bmrev) + if not matchrevs: + raise util.Abort(_("no bookmarks exist that match '%s'") + % pattern) + bmrevs = set() + for bmrev in matchrevs: + bmrevs.add(repo[bmrev].rev()) + return [r for r in subset if r in bmrevs] + bms = set([repo[r].rev() for r in bookmarksmod.listbookmarks(repo).values()]) return [r for r in subset if r in bms] @@ -322,14 +393,25 @@ """``branch(string or set)`` All changesets belonging to the given branch or the branches of the given changesets. + + If `string` starts with `re:`, the remainder of the name is treated as + a regular expression. To match a branch that actually starts with `re:`, + use the prefix `literal:`. """ try: b = getstring(x, '') - if b in repo.branchmap(): - return [r for r in subset if repo[r].branch() == b] except error.ParseError: # not a string, but another revspec, e.g. tip() pass + else: + kind, pattern, matcher = _stringmatcher(b) + if kind == 'literal': + # note: falls through to the revspec case if no branch with + # this name exists + if pattern in repo.branchmap(): + return [r for r in subset if matcher(repo[r].branch())] + else: + return [r for r in subset if matcher(repo[r].branch())] s = getset(repo, range(len(repo)), x) b = set() @@ -392,7 +474,7 @@ """ # i18n: "closed" is a keyword getargs(x, 0, 0, _("closed takes no arguments")) - return [r for r in subset if repo[r].extra().get('close')] + return [r for r in subset if repo[r].closesbranch()] def contains(repo, subset, x): """``contains(pattern)`` @@ -418,6 +500,27 @@ break return s +def converted(repo, subset, x): + """``converted([id])`` + Changesets converted from the given identifier in the old repository if + present, or all converted changesets if no identifier is specified. + """ + + # There is exactly no chance of resolving the revision, so do a simple + # string compare and hope for the best + + # i18n: "converted" is a keyword + rev = None + l = getargs(x, 0, 1, _('converted takes one or no arguments')) + if l: + rev = getstring(l[0], _('converted requires a revision')) + + def _matchvalue(r): + source = repo[r].extra().get('convert_revision', None) + return source is not None and (rev is None or source.startswith(rev)) + + return [r for r in subset if _matchvalue(r)] + def date(repo, subset, x): """``date(interval)`` Changesets within the interval, see :hg:`help dates`. @@ -458,11 +561,87 @@ # Like ``descendants(set)`` but follows only the first parents. return _descendants(repo, subset, x, followfirst=True) +def destination(repo, subset, x): + """``destination([set])`` + Changesets that were created by a graft, transplant or rebase operation, + with the given revisions specified as the source. Omitting the optional set + is the same as passing all(). + """ + if x is not None: + args = set(getset(repo, range(len(repo)), x)) + else: + args = set(getall(repo, range(len(repo)), x)) + + dests = set() + + # subset contains all of the possible destinations that can be returned, so + # iterate over them and see if their source(s) were provided in the args. + # Even if the immediate src of r is not in the args, src's source (or + # further back) may be. Scanning back further than the immediate src allows + # transitive transplants and rebases to yield the same results as transitive + # grafts. + for r in subset: + src = _getrevsource(repo, r) + lineage = None + + while src is not None: + if lineage is None: + lineage = list() + + lineage.append(r) + + # The visited lineage is a match if the current source is in the arg + # set. Since every candidate dest is visited by way of iterating + # subset, any dests futher back in the lineage will be tested by a + # different iteration over subset. Likewise, if the src was already + # selected, the current lineage can be selected without going back + # further. + if src in args or src in dests: + dests.update(lineage) + break + + r = src + src = _getrevsource(repo, r) + + return [r for r in subset if r in dests] + def draft(repo, subset, x): """``draft()`` Changeset in draft phase.""" getargs(x, 0, 0, _("draft takes no arguments")) - return [r for r in subset if repo._phaserev[r] == phases.draft] + pc = repo._phasecache + return [r for r in subset if pc.phase(repo, r) == phases.draft] + +def extinct(repo, subset, x): + """``extinct()`` + obsolete changeset with obsolete descendant only.""" + getargs(x, 0, 0, _("obsolete takes no arguments")) + extinctset = set(repo.revs('(obsolete()::) - (::(not obsolete()))')) + return [r for r in subset if r in extinctset] + +def extra(repo, subset, x): + """``extra(label, [value])`` + Changesets with the given label in the extra metadata, with the given + optional value. + + If `value` starts with `re:`, the remainder of the value is treated as + a regular expression. To match a value that actually starts with `re:`, + use the prefix `literal:`. + """ + + l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments')) + label = getstring(l[0], _('first argument to extra must be a string')) + value = None + + if len(l) > 1: + value = getstring(l[1], _('second argument to extra must be a string')) + kind, value, matcher = _stringmatcher(value) + + def _matchvalue(r): + extra = repo[r].extra() + return label in extra and (value is None or matcher(extra[label])) + + return [r for r in subset if _matchvalue(r)] def filelog(repo, subset, x): """``filelog(pattern)`` @@ -747,6 +926,40 @@ return [r for r in subset if r == rn] +def obsolete(repo, subset, x): + """``obsolete()`` + Mutable changeset with a newer version.""" + getargs(x, 0, 0, _("obsolete takes no arguments")) + return [r for r in subset if repo[r].obsolete()] + +def origin(repo, subset, x): + """``origin([set])`` + Changesets that were specified as a source for the grafts, transplants or + rebases that created the given revisions. Omitting the optional set is the + same as passing all(). If a changeset created by these operations is itself + specified as a source for one of these operations, only the source changeset + for the first operation is selected. + """ + if x is not None: + args = set(getset(repo, range(len(repo)), x)) + else: + args = set(getall(repo, range(len(repo)), x)) + + def _firstsrc(rev): + src = _getrevsource(repo, rev) + if src is None: + return None + + while True: + prev = _getrevsource(repo, src) + + if prev is None: + return src + src = prev + + o = set([_firstsrc(r) for r in args]) + return [r for r in subset if r in o] + def outgoing(repo, subset, x): """``outgoing([path])`` Changesets not found in the specified destination repository, or the @@ -859,7 +1072,8 @@ """``public()`` Changeset in public phase.""" getargs(x, 0, 0, _("public takes no arguments")) - return [r for r in subset if repo._phaserev[r] == phases.public] + pc = repo._phasecache + return [r for r in subset if pc.phase(repo, r) == phases.public] def remote(repo, subset, x): """``remote([id [,path]])`` @@ -929,8 +1143,11 @@ Valid fields are most regular revision fields and some special fields. Regular revision fields are ``description``, ``author``, ``branch``, - ``date``, ``files``, ``phase``, ``parents``, ``substate`` and ``user``. - Note that ``author`` and ``user`` are synonyms. + ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user`` + and ``diff``. + Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the + contents of the revision. Two revisions matching their ``diff`` will + also match their ``files``. Special fields are ``summary`` and ``metadata``: ``summary`` matches the first line of the description. @@ -950,12 +1167,18 @@ _("matching requires a string " "as its second argument")).split() - # Make sure that there are no repeated fields, and expand the - # 'special' 'metadata' field type + # Make sure that there are no repeated fields, + # expand the 'special' 'metadata' field type + # and check the 'files' whenever we check the 'diff' fields = [] for field in fieldlist: if field == 'metadata': fields += ['user', 'description', 'date'] + elif field == 'diff': + # a revision matching the diff must also match the files + # since matching the diff is very costly, make sure to + # also match the files first + fields += ['files', 'diff'] else: if field == 'author': field = 'user' @@ -969,7 +1192,7 @@ # Not all fields take the same amount of time to be matched # Sort the selected fields in order of increasing matching cost fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary', - 'files', 'description', 'substate'] + 'files', 'description', 'substate', 'diff'] def fieldkeyfunc(f): try: return fieldorder.index(f) @@ -992,6 +1215,7 @@ 'phase': lambda r: repo[r].phase(), 'substate': lambda r: repo[r].substate, 'summary': lambda r: repo[r].description().splitlines()[0], + 'diff': lambda r: list(repo[r].diff(git=True),) } for info in fields: getfield = _funcs.get(info, None) @@ -1022,6 +1246,8 @@ Reverse order of set. """ l = getset(repo, subset, x) + if not isinstance(l, list): + l = list(l) l.reverse() return l @@ -1038,7 +1264,8 @@ """``secret()`` Changeset in secret phase.""" getargs(x, 0, 0, _("secret takes no arguments")) - return [r for r in subset if repo._phaserev[r] == phases.secret] + pc = repo._phasecache + return [r for r in subset if pc.phase(repo, r) == phases.secret] def sort(repo, subset, x): """``sort(set[, [-]key...])`` @@ -1095,6 +1322,51 @@ l.sort() return [e[-1] for e in l] +def _stringmatcher(pattern): + """ + accepts a string, possibly starting with 're:' or 'literal:' prefix. + returns the matcher name, pattern, and matcher function. + missing or unknown prefixes are treated as literal matches. + + helper for tests: + >>> def test(pattern, *tests): + ... kind, pattern, matcher = _stringmatcher(pattern) + ... return (kind, pattern, [bool(matcher(t)) for t in tests]) + + exact matching (no prefix): + >>> test('abcdefg', 'abc', 'def', 'abcdefg') + ('literal', 'abcdefg', [False, False, True]) + + regex matching ('re:' prefix) + >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar') + ('re', 'a.+b', [False, False, True]) + + force exact matches ('literal:' prefix) + >>> test('literal:re:foobar', 'foobar', 're:foobar') + ('literal', 're:foobar', [False, True]) + + unknown prefixes are ignored and treated as literals + >>> test('foo:bar', 'foo', 'bar', 'foo:bar') + ('literal', 'foo:bar', [False, False, True]) + """ + if pattern.startswith('re:'): + pattern = pattern[3:] + try: + regex = re.compile(pattern) + except re.error, e: + raise error.ParseError(_('invalid regular expression: %s') + % e) + return 're', pattern, regex.search + elif pattern.startswith('literal:'): + pattern = pattern[8:] + return 'literal', pattern, pattern.__eq__ + +def _substringmatcher(pattern): + kind, pattern, matcher = _stringmatcher(pattern) + if kind == 'literal': + matcher = lambda s: pattern in s + return kind, pattern, matcher + def tag(repo, subset, x): """``tag([name])`` The specified tag by name, or all tagged revisions if no name is given. @@ -1103,12 +1375,20 @@ args = getargs(x, 0, 1, _("tag takes one or no arguments")) cl = repo.changelog if args: - tn = getstring(args[0], - # i18n: "tag" is a keyword - _('the argument to tag must be a string')) - if not repo.tags().get(tn, None): - raise util.Abort(_("tag '%s' does not exist") % tn) - s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn]) + pattern = getstring(args[0], + # i18n: "tag" is a keyword + _('the argument to tag must be a string')) + kind, pattern, matcher = _stringmatcher(pattern) + if kind == 'literal': + # avoid resolving all tags + tn = repo._tagscache.tags.get(pattern, None) + if tn is None: + raise util.Abort(_("tag '%s' does not exist") % pattern) + s = set([repo[tn].rev()]) + else: + s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)]) + if not s: + raise util.Abort(_("no tags exist that match '%s'") % pattern) else: s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip']) return [r for r in subset if r in s] @@ -1116,9 +1396,21 @@ def tagged(repo, subset, x): return tag(repo, subset, x) +def unstable(repo, subset, x): + """``unstable()`` + Unstable changesets are non-obsolete with obsolete descendants.""" + getargs(x, 0, 0, _("obsolete takes no arguments")) + unstableset = set(repo.revs('(obsolete()::) - obsolete()')) + return [r for r in subset if r in unstableset] + + def user(repo, subset, x): """``user(string)`` User name contains string. The match is case-insensitive. + + If `string` starts with `re:`, the remainder of the string is treated as + a regular expression. To match a user that actually contains `re:`, use + the prefix `literal:`. """ return author(repo, subset, x) @@ -1146,11 +1438,15 @@ "children": children, "closed": closed, "contains": contains, + "converted": converted, "date": date, "desc": desc, "descendants": descendants, "_firstdescendants": _firstdescendants, + "destination": destination, "draft": draft, + "extinct": extinct, + "extra": extra, "file": hasfile, "filelog": filelog, "first": first, @@ -1168,6 +1464,8 @@ "merge": merge, "min": minrev, "modifies": modifies, + "obsolete": obsolete, + "origin": origin, "outgoing": outgoing, "p1": p1, "p2": p2, @@ -1185,11 +1483,13 @@ "tag": tag, "tagged": tagged, "user": user, + "unstable": unstable, "_list": _list, } methods = { "range": rangeset, + "dagrange": dagrange, "string": stringset, "symbol": symbolset, "and": andset, @@ -1213,9 +1513,6 @@ op = x[0] if op == 'minus': return optimize(('and', x[1], ('not', x[2])), small) - elif op == 'dagrange': - return optimize(('and', ('func', ('symbol', 'descendants'), x[1]), - ('func', ('symbol', 'ancestors'), x[2])), small) elif op == 'dagrangepre': return optimize(('func', ('symbol', 'ancestors'), x[1]), small) elif op == 'dagrangepost': @@ -1229,7 +1526,7 @@ '-' + getstring(x[1], _("can't negate that"))), small) elif op in 'string symbol negate': return smallbonus, x # single revisions are small - elif op == 'and' or op == 'dagrange': + elif op == 'and': wa, ta = optimize(x[1], True) wb, tb = optimize(x[2], True) w = min(wa, wb) @@ -1250,7 +1547,7 @@ return o[0], (op, o[1]) elif op == 'group': return optimize(x[1], small) - elif op in 'range list parent ancestorspec': + elif op in 'dagrange range list parent ancestorspec': if op == 'parent': # x^:y means (x^) : y, not x ^ (:y) post = ('parentpost', x[1]) @@ -1362,7 +1659,7 @@ return args[arg] return tuple(_expandargs(t, args) for t in tree) -def _expandaliases(aliases, tree, expanding): +def _expandaliases(aliases, tree, expanding, cache): """Expand aliases in tree, recursively. 'aliases' is a dictionary mapping user defined aliases to @@ -1377,17 +1674,20 @@ raise error.ParseError(_('infinite expansion of revset alias "%s" ' 'detected') % alias.name) expanding.append(alias) - result = _expandaliases(aliases, alias.replacement, expanding) + if alias.name not in cache: + cache[alias.name] = _expandaliases(aliases, alias.replacement, + expanding, cache) + result = cache[alias.name] expanding.pop() if alias.args is not None: l = getlist(tree[2]) if len(l) != len(alias.args): raise error.ParseError( _('invalid number of arguments: %s') % len(l)) - l = [_expandaliases(aliases, a, []) for a in l] + l = [_expandaliases(aliases, a, [], cache) for a in l] result = _expandargs(result, dict(zip(alias.args, l))) else: - result = tuple(_expandaliases(aliases, t, expanding) + result = tuple(_expandaliases(aliases, t, expanding, cache) for t in tree) return result @@ -1397,7 +1697,7 @@ for k, v in ui.configitems('revsetalias'): alias = revsetalias(k, v) aliases[alias.name] = alias - return _expandaliases(aliases, tree, []) + return _expandaliases(aliases, tree, [], {}) parse = parser.parser(tokenize, elements).parse diff -r 98823bd0d697 -r a06e2681dd17 mercurial/scmutil.py --- a/mercurial/scmutil.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/scmutil.py Wed Jul 18 19:08:25 2012 -0500 @@ -49,22 +49,27 @@ return abort, warn class casecollisionauditor(object): - def __init__(self, ui, abort, existingiter): + def __init__(self, ui, abort, dirstate): self._ui = ui self._abort = abort - self._map = {} - for f in existingiter: - self._map[encoding.lower(f)] = f + allfiles = '\0'.join(dirstate._map) + self._loweredfiles = set(encoding.lower(allfiles).split('\0')) + self._dirstate = dirstate + # The purpose of _newfiles is so that we don't complain about + # case collisions if someone were to call this object with the + # same filename twice. + self._newfiles = set() def __call__(self, f): fl = encoding.lower(f) - map = self._map - if fl in map and map[fl] != f: + if (fl in self._loweredfiles and f not in self._dirstate and + f not in self._newfiles): msg = _('possible case-folding collision for %s') % f if self._abort: raise util.Abort(msg) self._ui.warn(_("warning: %s\n") % msg) - map[fl] = f + self._loweredfiles.add(fl) + self._newfiles.add(f) class pathauditor(object): '''ensure that a filesystem path contains no banned components. @@ -141,8 +146,9 @@ elif (stat.S_ISDIR(st.st_mode) and os.path.isdir(os.path.join(curpath, '.hg'))): if not self.callback or not self.callback(curpath): - raise util.Abort(_("path '%s' is inside nested repo %r") % - (path, prefix)) + raise util.Abort(_("path '%s' is inside nested " + "repo %r") + % (path, prefix)) prefixes.append(normprefix) parts.pop() normparts.pop() @@ -189,13 +195,30 @@ finally: fp.close() + def mkdir(self, path=None): + return os.mkdir(self.join(path)) + + def exists(self, path=None): + return os.path.exists(self.join(path)) + + def isdir(self, path=None): + return os.path.isdir(self.join(path)) + + def makedir(self, path=None, notindexed=True): + return util.makedir(self.join(path), notindexed) + + def makedirs(self, path=None, mode=None): + return util.makedirs(self.join(path), mode) + class opener(abstractopener): '''Open files relative to a base directory This class is used to hide the details of COW semantics and remote file access from higher level code. ''' - def __init__(self, base, audit=True): + def __init__(self, base, audit=True, expand=False): + if expand: + base = os.path.realpath(util.expandpath(base)) self.base = base self._audit = audit if audit: @@ -290,7 +313,10 @@ self.auditor(path) def join(self, path): - return os.path.join(self.base, path) + if path: + return os.path.join(self.base, path) + else: + return self.base class filteropener(abstractopener): '''Wrapper opener for filtering filenames with a function.''' @@ -323,18 +349,16 @@ else: # Determine whether `name' is in the hierarchy at or beneath `root', # by iterating name=dirname(name) until that causes no change (can't - # check name == '/', because that doesn't work on windows). For each - # `name', compare dev/inode numbers. If they match, the list `rel' - # holds the reversed list of components making up the relative file - # name we want. - root_st = os.stat(root) + # check name == '/', because that doesn't work on windows). The list + # `rel' holds the reversed list of components making up the relative + # file name we want. rel = [] while True: try: - name_st = os.stat(name) + s = util.samefile(name, root) except OSError: - name_st = None - if name_st and util.samestat(name_st, root_st): + s = False + if s: if not rel: # name was actually the same as root (maybe a symlink) return '' @@ -351,7 +375,8 @@ raise util.Abort('%s not under root' % myname) def walkrepos(path, followsym=False, seen_dirs=None, recurse=False): - '''yield every hg repository under path, recursively.''' + '''yield every hg repository under path, always recursively. + The recurse flag will only control recursion into repo working dirs''' def errhandler(err): if err.filename == path: raise err @@ -464,7 +489,7 @@ else: - _HKEY_LOCAL_MACHINE = 0x80000002L + import _winreg def systemrcpath(): '''return default os-specific hgrc search path''' @@ -484,7 +509,7 @@ return rcpath # else look for a system rcpath in the registry value = util.lookupreg('SOFTWARE\\Mercurial', None, - _HKEY_LOCAL_MACHINE) + _winreg.HKEY_LOCAL_MACHINE) if not isinstance(value, str) or not value: return rcpath value = util.localpath(value) @@ -585,10 +610,9 @@ # fall through to new-style queries if old-style fails m = revset.match(repo.ui, spec) - for r in m(repo, range(len(repo))): - if r not in seen: - l.append(r) - seen.update(l) + dl = [r for r in m(repo, xrange(len(repo))) if r not in seen] + l.extend(dl) + seen.update(dl) return l @@ -656,8 +680,9 @@ unknown.append(abs) if repo.ui.verbose or not exact: repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) - elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target) - or (os.path.isdir(target) and not os.path.islink(target))): + elif (repo.dirstate[abs] != 'r' and + (not good or not os.path.lexists(target) or + (os.path.isdir(target) and not os.path.islink(target)))): deleted.append(abs) if repo.ui.verbose or not exact: repo.ui.status(_('removing %s\n') % ((pats and rel) or abs)) @@ -766,8 +791,9 @@ missings.append(r) missings.sort() if missings: - raise error.RequirementError(_("unknown repository format: " - "requires features '%s' (upgrade Mercurial)") % "', '".join(missings)) + raise error.RequirementError( + _("unknown repository format: requires features '%s' (upgrade " + "Mercurial)") % "', '".join(missings)) return requirements class filecacheentry(object): diff -r 98823bd0d697 -r a06e2681dd17 mercurial/setdiscovery.py --- a/mercurial/setdiscovery.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/setdiscovery.py Wed Jul 18 19:08:25 2012 -0500 @@ -8,8 +8,7 @@ from node import nullid from i18n import _ -import random, collections, util, dagutil -import phases +import random, util, dagutil def _updatesample(dag, nodes, sample, always, quicksamplesize=0): # if nodes is empty we scan the entire graph @@ -18,7 +17,7 @@ else: heads = dag.heads() dist = {} - visit = collections.deque(heads) + visit = util.deque(heads) seen = set() factor = 1 while visit: @@ -100,7 +99,7 @@ sample = ownheads if remote.local(): # stopgap until we have a proper localpeer that supports batch() - srvheadhashes = phases.visibleheads(remote) + srvheadhashes = remote.heads() yesno = remote.known(dag.externalizeall(sample)) elif remote.capable('batch'): batch = remote.batch() @@ -134,11 +133,16 @@ return (ownheadhashes, True, srvheadhashes,) # full blown discovery - undecided = dag.nodeset() # own nodes where I don't know if remote knows them - common = set() # own nodes I know we both know - missing = set() # own nodes I know remote lacks - # treat remote heads (and maybe own heads) as a first implicit sample response + # own nodes where I don't know if remote knows them + undecided = dag.nodeset() + # own nodes I know we both know + common = set() + # own nodes I know remote lacks + missing = set() + + # treat remote heads (and maybe own heads) as a first implicit sample + # response common.update(dag.ancestorset(srvheads)) undecided.difference_update(common) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/similar.py --- a/mercurial/similar.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/similar.py Wed Jul 18 19:08:25 2012 -0500 @@ -44,7 +44,8 @@ ''' copies = {} for i, r in enumerate(removed): - repo.ui.progress(_('searching for similar files'), i, total=len(removed)) + repo.ui.progress(_('searching for similar files'), i, + total=len(removed)) # lazily load text @util.cachefunc diff -r 98823bd0d697 -r a06e2681dd17 mercurial/simplemerge.py --- a/mercurial/simplemerge.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/simplemerge.py Wed Jul 18 19:08:25 2012 -0500 @@ -94,7 +94,7 @@ elif self.a[0].endswith('\r'): newline = '\r' if base_marker and reprocess: - raise CantReprocessAndShowBase() + raise CantReprocessAndShowBase if name_a: start_marker = start_marker + ' ' + name_a if name_b: @@ -222,7 +222,8 @@ # section a[0:ia] has been disposed of, etc iz = ia = ib = 0 - for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions(): + for region in self.find_sync_regions(): + zmatch, zend, amatch, aend, bmatch, bend = region #print 'match base [%d:%d]' % (zmatch, zend) matchlen = zend - zmatch diff -r 98823bd0d697 -r a06e2681dd17 mercurial/sshpeer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/sshpeer.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,239 @@ +# sshpeer.py - ssh repository proxy class for mercurial +# +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +import re +from i18n import _ +import util, error, wireproto + +class remotelock(object): + def __init__(self, repo): + self.repo = repo + def release(self): + self.repo.unlock() + self.repo = None + def __del__(self): + if self.repo: + self.release() + +def _serverquote(s): + '''quote a string for the remote shell ... which we assume is sh''' + if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s): + return s + return "'%s'" % s.replace("'", "'\\''") + +class sshpeer(wireproto.wirepeer): + def __init__(self, ui, path, create=False): + self._url = path + self.ui = ui + self.pipeo = self.pipei = self.pipee = None + + u = util.url(path, parsequery=False, parsefragment=False) + if u.scheme != 'ssh' or not u.host or u.path is None: + self._abort(error.RepoError(_("couldn't parse location %s") % path)) + + self.user = u.user + if u.passwd is not None: + self._abort(error.RepoError(_("password in URL not supported"))) + self.host = u.host + self.port = u.port + self.path = u.path or "." + + sshcmd = self.ui.config("ui", "ssh", "ssh") + remotecmd = self.ui.config("ui", "remotecmd", "hg") + + args = util.sshargs(sshcmd, self.host, self.user, self.port) + + if create: + cmd = '%s %s %s' % (sshcmd, args, + util.shellquote("%s init %s" % + (_serverquote(remotecmd), _serverquote(self.path)))) + ui.note(_('running %s\n') % cmd) + res = util.system(cmd) + if res != 0: + self._abort(error.RepoError(_("could not create remote repo"))) + + self.validate_repo(ui, sshcmd, args, remotecmd) + + def url(self): + return self._url + + def validate_repo(self, ui, sshcmd, args, remotecmd): + # cleanup up previous run + self.cleanup() + + cmd = '%s %s %s' % (sshcmd, args, + util.shellquote("%s -R %s serve --stdio" % + (_serverquote(remotecmd), _serverquote(self.path)))) + ui.note(_('running %s\n') % cmd) + cmd = util.quotecommand(cmd) + self.pipeo, self.pipei, self.pipee = util.popen3(cmd) + + # skip any noise generated by remote shell + self._callstream("hello") + r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40))) + lines = ["", "dummy"] + max_noise = 500 + while lines[-1] and max_noise: + l = r.readline() + self.readerr() + if lines[-1] == "1\n" and l == "\n": + break + if l: + ui.debug("remote: ", l) + lines.append(l) + max_noise -= 1 + else: + self._abort(error.RepoError(_('no suitable response from ' + 'remote hg'))) + + self._caps = set() + for l in reversed(lines): + if l.startswith("capabilities:"): + self._caps.update(l[:-1].split(":")[1].split()) + break + + def _capabilities(self): + return self._caps + + def readerr(self): + while True: + size = util.fstat(self.pipee).st_size + if size == 0: + break + s = self.pipee.read(size) + if not s: + break + for l in s.splitlines(): + self.ui.status(_("remote: "), l, '\n') + + def _abort(self, exception): + self.cleanup() + raise exception + + def cleanup(self): + if self.pipeo is None: + return + self.pipeo.close() + self.pipei.close() + try: + # read the error descriptor until EOF + for l in self.pipee: + self.ui.status(_("remote: "), l) + except (IOError, ValueError): + pass + self.pipee.close() + + __del__ = cleanup + + def _callstream(self, cmd, **args): + self.ui.debug("sending %s command\n" % cmd) + self.pipeo.write("%s\n" % cmd) + _func, names = wireproto.commands[cmd] + keys = names.split() + wireargs = {} + for k in keys: + if k == '*': + wireargs['*'] = args + break + else: + wireargs[k] = args[k] + del args[k] + for k, v in sorted(wireargs.iteritems()): + self.pipeo.write("%s %d\n" % (k, len(v))) + if isinstance(v, dict): + for dk, dv in v.iteritems(): + self.pipeo.write("%s %d\n" % (dk, len(dv))) + self.pipeo.write(dv) + else: + self.pipeo.write(v) + self.pipeo.flush() + + return self.pipei + + def _call(self, cmd, **args): + self._callstream(cmd, **args) + return self._recv() + + def _callpush(self, cmd, fp, **args): + r = self._call(cmd, **args) + if r: + return '', r + while True: + d = fp.read(4096) + if not d: + break + self._send(d) + self._send("", flush=True) + r = self._recv() + if r: + return '', r + return self._recv(), '' + + def _decompress(self, stream): + return stream + + def _recv(self): + l = self.pipei.readline() + if l == '\n': + err = [] + while True: + line = self.pipee.readline() + if line == '-\n': + break + err.extend([line]) + if len(err) > 0: + # strip the trailing newline added to the last line server-side + err[-1] = err[-1][:-1] + self._abort(error.OutOfBandError(*err)) + self.readerr() + try: + l = int(l) + except ValueError: + self._abort(error.ResponseError(_("unexpected response:"), l)) + return self.pipei.read(l) + + def _send(self, data, flush=False): + self.pipeo.write("%d\n" % len(data)) + if data: + self.pipeo.write(data) + if flush: + self.pipeo.flush() + self.readerr() + + def lock(self): + self._call("lock") + return remotelock(self) + + def unlock(self): + self._call("unlock") + + def addchangegroup(self, cg, source, url, lock=None): + '''Send a changegroup to the remote server. Return an integer + similar to unbundle(). DEPRECATED, since it requires locking the + remote.''' + d = self._call("addchangegroup") + if d: + self._abort(error.RepoError(_("push refused: %s") % d)) + while True: + d = cg.read(4096) + if not d: + break + self.pipeo.write(d) + self.readerr() + + self.pipeo.flush() + + self.readerr() + r = self._recv() + if not r: + return 1 + try: + return int(r) + except ValueError: + self._abort(error.ResponseError(_("unexpected response:"), r)) + +instance = sshpeer diff -r 98823bd0d697 -r a06e2681dd17 mercurial/sshrepo.py --- a/mercurial/sshrepo.py Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,232 +0,0 @@ -# sshrepo.py - ssh repository proxy class for mercurial -# -# Copyright 2005, 2006 Matt Mackall -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -import re -from i18n import _ -import util, error, wireproto - -class remotelock(object): - def __init__(self, repo): - self.repo = repo - def release(self): - self.repo.unlock() - self.repo = None - def __del__(self): - if self.repo: - self.release() - -def _serverquote(s): - '''quote a string for the remote shell ... which we assume is sh''' - if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s): - return s - return "'%s'" % s.replace("'", "'\\''") - -class sshrepository(wireproto.wirerepository): - def __init__(self, ui, path, create=False): - self._url = path - self.ui = ui - - u = util.url(path, parsequery=False, parsefragment=False) - if u.scheme != 'ssh' or not u.host or u.path is None: - self._abort(error.RepoError(_("couldn't parse location %s") % path)) - - self.user = u.user - if u.passwd is not None: - self._abort(error.RepoError(_("password in URL not supported"))) - self.host = u.host - self.port = u.port - self.path = u.path or "." - - sshcmd = self.ui.config("ui", "ssh", "ssh") - remotecmd = self.ui.config("ui", "remotecmd", "hg") - - args = util.sshargs(sshcmd, self.host, self.user, self.port) - - if create: - cmd = '%s %s %s' % (sshcmd, args, - util.shellquote("%s init %s" % - (_serverquote(remotecmd), _serverquote(self.path)))) - ui.note(_('running %s\n') % cmd) - res = util.system(cmd) - if res != 0: - self._abort(error.RepoError(_("could not create remote repo"))) - - self.validate_repo(ui, sshcmd, args, remotecmd) - - def url(self): - return self._url - - def validate_repo(self, ui, sshcmd, args, remotecmd): - # cleanup up previous run - self.cleanup() - - cmd = '%s %s %s' % (sshcmd, args, - util.shellquote("%s -R %s serve --stdio" % - (_serverquote(remotecmd), _serverquote(self.path)))) - ui.note(_('running %s\n') % cmd) - cmd = util.quotecommand(cmd) - self.pipeo, self.pipei, self.pipee = util.popen3(cmd) - - # skip any noise generated by remote shell - self._callstream("hello") - r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40))) - lines = ["", "dummy"] - max_noise = 500 - while lines[-1] and max_noise: - l = r.readline() - self.readerr() - if lines[-1] == "1\n" and l == "\n": - break - if l: - ui.debug("remote: ", l) - lines.append(l) - max_noise -= 1 - else: - self._abort(error.RepoError(_("no suitable response from remote hg"))) - - self.capabilities = set() - for l in reversed(lines): - if l.startswith("capabilities:"): - self.capabilities.update(l[:-1].split(":")[1].split()) - break - - def readerr(self): - while True: - size = util.fstat(self.pipee).st_size - if size == 0: - break - s = self.pipee.read(size) - if not s: - break - for l in s.splitlines(): - self.ui.status(_("remote: "), l, '\n') - - def _abort(self, exception): - self.cleanup() - raise exception - - def cleanup(self): - try: - self.pipeo.close() - self.pipei.close() - # read the error descriptor until EOF - for l in self.pipee: - self.ui.status(_("remote: "), l) - self.pipee.close() - except: - pass - - __del__ = cleanup - - def _callstream(self, cmd, **args): - self.ui.debug("sending %s command\n" % cmd) - self.pipeo.write("%s\n" % cmd) - _func, names = wireproto.commands[cmd] - keys = names.split() - wireargs = {} - for k in keys: - if k == '*': - wireargs['*'] = args - break - else: - wireargs[k] = args[k] - del args[k] - for k, v in sorted(wireargs.iteritems()): - self.pipeo.write("%s %d\n" % (k, len(v))) - if isinstance(v, dict): - for dk, dv in v.iteritems(): - self.pipeo.write("%s %d\n" % (dk, len(dv))) - self.pipeo.write(dv) - else: - self.pipeo.write(v) - self.pipeo.flush() - - return self.pipei - - def _call(self, cmd, **args): - self._callstream(cmd, **args) - return self._recv() - - def _callpush(self, cmd, fp, **args): - r = self._call(cmd, **args) - if r: - return '', r - while True: - d = fp.read(4096) - if not d: - break - self._send(d) - self._send("", flush=True) - r = self._recv() - if r: - return '', r - return self._recv(), '' - - def _decompress(self, stream): - return stream - - def _recv(self): - l = self.pipei.readline() - if l == '\n': - err = [] - while True: - line = self.pipee.readline() - if line == '-\n': - break - err.extend([line]) - if len(err) > 0: - # strip the trailing newline added to the last line server-side - err[-1] = err[-1][:-1] - self._abort(error.OutOfBandError(*err)) - self.readerr() - try: - l = int(l) - except ValueError: - self._abort(error.ResponseError(_("unexpected response:"), l)) - return self.pipei.read(l) - - def _send(self, data, flush=False): - self.pipeo.write("%d\n" % len(data)) - if data: - self.pipeo.write(data) - if flush: - self.pipeo.flush() - self.readerr() - - def lock(self): - self._call("lock") - return remotelock(self) - - def unlock(self): - self._call("unlock") - - def addchangegroup(self, cg, source, url, lock=None): - '''Send a changegroup to the remote server. Return an integer - similar to unbundle(). DEPRECATED, since it requires locking the - remote.''' - d = self._call("addchangegroup") - if d: - self._abort(error.RepoError(_("push refused: %s") % d)) - while True: - d = cg.read(4096) - if not d: - break - self.pipeo.write(d) - self.readerr() - - self.pipeo.flush() - - self.readerr() - r = self._recv() - if not r: - return 1 - try: - return int(r) - except ValueError: - self._abort(error.ResponseError(_("unexpected response:"), r)) - -instance = sshrepository diff -r 98823bd0d697 -r a06e2681dd17 mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/statichttprepo.py Wed Jul 18 19:08:25 2012 -0500 @@ -76,6 +76,12 @@ return statichttpopener +class statichttppeer(localrepo.localpeer): + def local(self): + return None + def canpush(self): + return False + class statichttprepository(localrepo.localrepository): def __init__(self, ui, path): self._url = path @@ -87,6 +93,7 @@ opener = build_opener(ui, authinfo) self.opener = opener(self.path) + self.vfs = self.opener self._phasedefaults = [] try: @@ -112,8 +119,10 @@ self.store = store.store(requirements, self.path, opener) self.spath = self.store.path self.sopener = self.store.opener + self.svfs = self.sopener self.sjoin = self.store.join self._filecache = {} + self.requirements = requirements self.manifest = manifest.manifest(self.sopener) self.changelog = changelog.changelog(self.sopener) @@ -123,7 +132,9 @@ self._branchcachetip = None self.encodepats = None self.decodepats = None - self.capabilities.difference_update(["pushkey"]) + + def _restrictcapabilities(self, caps): + return caps.difference(["pushkey"]) def url(self): return self._url @@ -131,6 +142,9 @@ def local(self): return False + def peer(self): + return statichttppeer(self) + def lock(self, wait=True): raise util.Abort(_('cannot lock static-http repository')) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/store.py --- a/mercurial/store.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/store.py Wed Jul 18 19:08:25 2012 -0500 @@ -264,7 +264,8 @@ l.append((decodedir(n), n, st.st_size)) elif kind == stat.S_IFDIR and recurse: visit.append(fp) - return sorted(l) + l.sort() + return l def datafiles(self): return self._walk('data', True) @@ -390,15 +391,16 @@ def join(self, f): return self.path + '/' + self.encode(f) + def getsize(self, path): + return os.stat(self.path + '/' + path).st_size + def datafiles(self): rewrite = False existing = [] - spath = self.path for f in self.fncache: ef = self.encode(f) try: - st = os.stat(spath + '/' + ef) - yield f, ef, st.st_size + yield f, ef, self.getsize(ef) existing.append(f) except OSError: # nonexistent entry diff -r 98823bd0d697 -r a06e2681dd17 mercurial/subrepo.py --- a/mercurial/subrepo.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/subrepo.py Wed Jul 18 19:08:25 2012 -0500 @@ -8,7 +8,7 @@ import errno, os, re, xml.dom.minidom, shutil, posixpath import stat, subprocess, tarfile from i18n import _ -import config, scmutil, util, node, error, cmdutil, bookmarks +import config, scmutil, util, node, error, cmdutil, bookmarks, match as matchmod hg = None propertycache = util.propertycache @@ -200,7 +200,8 @@ 'use (l)ocal source (%s) or (r)emote source (%s)?\n') % (subrelpath(sub), local, remote)) else: - msg = (_(' subrepository sources for %s differ (in checked out version)\n' + msg = (_(' subrepository sources for %s differ (in checked out ' + 'version)\n' 'use (l)ocal source (%s) or (r)emote source (%s)?\n') % (subrelpath(sub), local, remote)) return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0) @@ -267,7 +268,7 @@ hg = h scmutil.pathauditor(ctx._repo.root)(path) - state = ctx.substate.get(path, nullstate) + state = ctx.substate[path] if state[2] not in types: raise util.Abort(_('unknown subrepo type %s') % state[2]) return types[state[2]](ctx, path, state[:2]) @@ -350,8 +351,11 @@ """return file flags""" return '' - def archive(self, ui, archiver, prefix): - files = self.files() + def archive(self, ui, archiver, prefix, match=None): + if match is not None: + files = [f for f in self.files() if match(f)] + else: + files = self.files() total = len(files) relpath = subrelpath(self) ui.progress(_('archiving (%s)') % relpath, 0, @@ -444,15 +448,16 @@ self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n') % (inst, subrelpath(self))) - def archive(self, ui, archiver, prefix): + def archive(self, ui, archiver, prefix, match=None): self._get(self._state + ('hg',)) - abstractsubrepo.archive(self, ui, archiver, prefix) + abstractsubrepo.archive(self, ui, archiver, prefix, match) rev = self._state[1] ctx = self._repo[rev] for subpath in ctx.substate: s = subrepo(ctx, subpath) - s.archive(ui, archiver, os.path.join(prefix, self._path)) + submatch = matchmod.narrowmatcher(subpath, match) + s.archive(ui, archiver, os.path.join(prefix, self._path), submatch) def dirty(self, ignoreupdate=False): r = self._state[1] @@ -498,8 +503,10 @@ % (subrelpath(self), srcurl)) parentrepo = self._repo._subparent shutil.rmtree(self._repo.path) - other, self._repo = hg.clone(self._repo._subparent.ui, {}, other, - self._repo.root, update=False) + other, cloned = hg.clone(self._repo._subparent.ui, {}, + other, self._repo.root, + update=False) + self._repo = cloned.local() self._initrepo(parentrepo, source, create=True) else: self._repo.ui.status(_('pulling subrepo %s from %s\n') @@ -1203,7 +1210,7 @@ else: os.remove(path) - def archive(self, ui, archiver, prefix): + def archive(self, ui, archiver, prefix, match=None): source, revision = self._state if not revision: return @@ -1219,6 +1226,8 @@ for i, info in enumerate(tar): if info.isdir(): continue + if match and not match(info.name): + continue if info.issym(): data = info.linkname else: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/tags.py --- a/mercurial/tags.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/tags.py Wed Jul 18 19:08:25 2012 -0500 @@ -181,7 +181,7 @@ for line in cachelines: if line == "\n": break - line = line.rstrip().split() + line = line.split() cacherevs.append(int(line[0])) headnode = bin(line[1]) cacheheads.append(headnode) @@ -228,6 +228,11 @@ # N.B. in case 4 (nodes destroyed), "new head" really means "newly # exposed". + if not len(repo.file('.hgtags')): + # No tags have ever been committed, so we can avoid a + # potentially expensive search. + return (repoheads, cachefnode, None, True) + newheads = [head for head in repoheads if head not in set(cacheheads)] diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templatefilters.py --- a/mercurial/templatefilters.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templatefilters.py Wed Jul 18 19:08:25 2012 -0500 @@ -260,7 +260,7 @@ >>> person('"Foo Bar ') 'Foo Bar' """ - if not '@' in author: + if '@' not in author: return author f = author.find('<') if f != -1: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templatekw.py --- a/mercurial/templatekw.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templatekw.py Wed Jul 18 19:08:25 2012 -0500 @@ -326,5 +326,16 @@ 'tags': showtags, } +def _showparents(**args): + """:parents: List of strings. The parents of the changeset in "rev:node" + format. If the changeset has only one "natural" parent (the predecessor + revision) nothing is shown.""" + pass + +dockeywords = { + 'parents': _showparents, +} +dockeywords.update(keywords) + # tell hggettext to extract docstrings from these functions: -i18nfunctions = keywords.values() +i18nfunctions = dockeywords.values() diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templater.py --- a/mercurial/templater.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templater.py Wed Jul 18 19:08:25 2012 -0500 @@ -312,7 +312,7 @@ def load(self, t): '''Get the template for the given template name. Use a local cache.''' - if not t in self.cache: + if t not in self.cache: try: self.cache[t] = util.readfile(self.map[t][1]) except KeyError, inst: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/coal/map --- a/mercurial/templates/coal/map Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/coal/map Wed Jul 18 19:08:25 2012 -0500 @@ -63,6 +63,7 @@ filerevision = ../paper/filerevision.tmpl fileannotate = ../paper/fileannotate.tmpl filediff = ../paper/filediff.tmpl +filecomparison = ../paper/filecomparison.tmpl filelog = ../paper/filelog.tmpl fileline = '
{linenumber} {line|escape}
' @@ -83,6 +84,26 @@ difflineat = '{linenumber} {line|escape}' diffline = '{linenumber} {line|escape}' +comparison = ' + + + + + + + + {blocks} +
{leftrev}:{leftnode|short}{rightrev}:{rightnode|short}
' +comparisonblock =' + + {lines} + ' +comparisonline = ' + + {leftlinenumber} {leftline|escape} + {rightlinenumber} {rightline|escape} + ' + changelogparent = ' parent {rev}: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/gitweb/fileannotate.tmpl --- a/mercurial/templates/gitweb/fileannotate.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/gitweb/fileannotate.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -26,6 +26,7 @@ revisions | annotate | diff | +comparison | raw | help
diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/gitweb/filecomparison.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/gitweb/filecomparison.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,63 @@ +{header} +{repo|escape}: comparison {file|escape} + + + + + + + + + +
{file|escape}
+ + +{branch%filerevbranch} + + + +{parent%filecompparent} +{child%filecompchild} +
changeset {rev}{node|short}
+ +
+ +
+ +
+ equal + deleted + inserted + replaced +
+ +
+{comparison} +
+ +
+ +{footer} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/gitweb/filediff.tmpl --- a/mercurial/templates/gitweb/filediff.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/gitweb/filediff.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -26,6 +26,7 @@ revisions | annotate | diff | +comparison | raw | help
diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/gitweb/filelog.tmpl --- a/mercurial/templates/gitweb/filelog.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/gitweb/filelog.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -23,6 +23,7 @@ revisions | annotate | diff | +comparison | rss | help
diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/gitweb/filerevision.tmpl --- a/mercurial/templates/gitweb/filerevision.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/gitweb/filerevision.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -26,6 +26,7 @@ revisions | annotate | diff | +comparison | raw | help
diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/gitweb/map --- a/mercurial/templates/gitweb/map Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/gitweb/map Wed Jul 18 19:08:25 2012 -0500 @@ -26,6 +26,7 @@ file | annotate | diff | + comparison | revisions ' @@ -37,6 +38,7 @@ file | annotate | diff | + comparison | revisions ' @@ -81,6 +83,7 @@ filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl filediff = filediff.tmpl +filecomparison = filecomparison.tmpl filelog = filelog.tmpl fileline = '
@@ -99,6 +102,27 @@ difflineminus = '{linenumber} {line|escape}' difflineat = '{linenumber} {line|escape}' diffline = '{linenumber} {line|escape}' + +comparison = ' + + + + + + + + {blocks} +
{leftrev}:{leftnode|short}{rightrev}:{rightnode|short}
' +comparisonblock =' + + {lines} + ' +comparisonline = ' + +
{leftlinenumber} {leftline|escape}
+
{rightlinenumber} {rightline|escape}
+ ' + changelogparent = ' parent {rev}: @@ -203,6 +227,15 @@ ' +filecompparent = ' + + parent {rev} + + + {node|short} + + + ' filelogparent = ' parent {rev}:  @@ -215,6 +248,13 @@ {node|short} ' +filecompchild = ' + + child {rev} + + {node|short} + + ' filelogchild = ' child {rev}:  diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/monoblue/fileannotate.tmpl --- a/mercurial/templates/monoblue/fileannotate.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/monoblue/fileannotate.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -35,6 +35,7 @@
  • revisions
  • annotate
  • diff
  • +
  • comparison
  • raw
  • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/monoblue/filecomparison.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/monoblue/filecomparison.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,64 @@ +{header} +{repo|escape}: comparison {file|escape} + + + + + +
    + + + + + +

    {file|escape}

    + +
    + {branch%filerevbranch} +
    changeset {rev}
    +
    {node|short}
    + {parent%filecompparent} + {child%filecompchild} +
    + +
    + equal + deleted + inserted + replaced +
    + +
    + {comparison} +
    + +{footer} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/monoblue/filediff.tmpl --- a/mercurial/templates/monoblue/filediff.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/monoblue/filediff.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -35,6 +35,7 @@
  • revisions
  • annotate
  • diff
  • +
  • comparison
  • raw
  • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/monoblue/filelog.tmpl --- a/mercurial/templates/monoblue/filelog.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/monoblue/filelog.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -35,6 +35,7 @@
  • revisions
  • annotate
  • diff
  • +
  • comparison
  • rss
  • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/monoblue/filerevision.tmpl --- a/mercurial/templates/monoblue/filerevision.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/monoblue/filerevision.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -35,6 +35,7 @@
  • revisions
  • annotate
  • diff
  • +
  • comparison
  • raw
  • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/monoblue/map --- a/mercurial/templates/monoblue/map Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/monoblue/map Wed Jul 18 19:08:25 2012 -0500 @@ -26,6 +26,7 @@ file | annotate | diff | + comparison | revisions ' @@ -37,6 +38,7 @@ file | annotate | diff | + comparison | revisions ' @@ -74,6 +76,7 @@ filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl filediff = filediff.tmpl +filecomparison = filecomparison.tmpl filelog = filelog.tmpl fileline = '
    @@ -94,6 +97,27 @@ difflineminus = '{linenumber} {line|escape}' difflineat = '{linenumber} {line|escape}' diffline = '{linenumber} {line|escape}' + +comparison = ' + + + + + + + + {blocks} +
    {leftrev}:{leftnode|short}{rightrev}:{rightnode|short}
    ' +comparisonblock =' + + {lines} + ' +comparisonline = ' + + {leftlinenumber} {leftline|escape} + {rightlinenumber} {rightline|escape} + ' + changelogparent = ' parent {rev}: @@ -176,6 +200,9 @@ filediffparent = '
    parent {rev}
    {node|short}
    ' +filecompparent = ' +
    parent {rev}
    +
    {node|short}
    ' filelogparent = ' parent {rev}:  @@ -184,6 +211,9 @@ filediffchild = '
    child {rev}
    {node|short}
    ' +filecompchild = ' +
    child {rev}
    +
    {node|short}
    ' filelogchild = ' child {rev}:  diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/fileannotate.tmpl --- a/mercurial/templates/paper/fileannotate.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/paper/fileannotate.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -25,6 +25,7 @@
  • file
  • latest
  • diff
  • +
  • comparison
  • annotate
  • file log
  • raw
  • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/filecomparison.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/paper/filecomparison.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,85 @@ +{header} +{repo|escape}: {file|escape} comparison + + + +
    + + +
    +

    {repo|escape}

    +

    comparison {file|escape} @ {rev}:{node|short}

    + + + +
    {desc|strip|escape|nonempty}
    + + + + + + + + + + + + + + + + + + +{changesettag} +
    author{author|obfuscate}
    date{date|rfc822date}
    parents{parent%filerevparent}
    children{child%filerevchild}
    + +
    +
    comparison
    +
    + equal + deleted + inserted + replaced +
    + +{comparison} + +
    +
    +
    + +{footer} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/filediff.tmpl --- a/mercurial/templates/paper/filediff.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/paper/filediff.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -24,6 +24,7 @@
  • file
  • latest
  • diff
  • +
  • comparison
  • annotate
  • file log
  • raw
  • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/filelog.tmpl --- a/mercurial/templates/paper/filelog.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/paper/filelog.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -27,6 +27,7 @@
    • file
    • diff
    • +
    • comparison
    • annotate
    • file log
    • raw
    • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/filerevision.tmpl --- a/mercurial/templates/paper/filerevision.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/paper/filerevision.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -23,6 +23,7 @@
    • file
    • latest
    • diff
    • +
    • comparison
    • annotate
    • file log
    • raw
    • diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/help.tmpl --- a/mercurial/templates/paper/help.tmpl Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/paper/help.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -21,7 +21,7 @@
    • branches
    diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/paper/map --- a/mercurial/templates/paper/map Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/paper/map Wed Jul 18 19:08:25 2012 -0500 @@ -62,6 +62,7 @@ filerevision = filerevision.tmpl fileannotate = fileannotate.tmpl filediff = filediff.tmpl +filecomparison = filecomparison.tmpl filelog = filelog.tmpl fileline = '
    {linenumber} {line|escape}
    ' @@ -82,6 +83,26 @@ difflineat = '{linenumber} {line|escape}' diffline = '{linenumber} {line|escape}' +comparison = ' + + + + + + + + {blocks} +
    {leftrev}:{leftnode|short}{rightrev}:{rightnode|short}
    ' +comparisonblock =' + + {lines} + ' +comparisonline = ' + + {leftlinenumber} {leftline|escape} + {rightlinenumber} {rightline|escape} + ' + changelogparent = ' parent {rev}: diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/raw/graph.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/raw/graph.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,6 @@ +{header} +# HG graph +# Node ID {node} +# Rows shown {rows} + +{nodes%graphnode} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/raw/graphedge.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/raw/graphedge.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,1 @@ +edge: ({col}, {row}) -> ({nextcol}, {nextrow}) (color {color}) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/raw/graphnode.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/templates/raw/graphnode.tmpl Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,7 @@ +changeset: {node} +user: {user} +date: {age} +summary: {desc} +{branches%branchname}{tags%tagname}{bookmarks%bookmarkname} +node: ({col}, {row}) (color {color}) +{edges%graphedge} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/raw/map --- a/mercurial/templates/raw/map Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/raw/map Wed Jul 18 19:08:25 2012 -0500 @@ -28,3 +28,9 @@ bookmarkentry = '{bookmark} {node}\n' branches = '{entries%branchentry}' branchentry = '{branch} {node} {status}\n' +graph = graph.tmpl +graphnode = graphnode.tmpl +graphedge = graphedge.tmpl +bookmarkname = 'bookmark: {name}\n' +branchname = 'branch: {name}\n' +tagname = 'tag: {name}\n' diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/static/style-coal.css --- a/mercurial/templates/static/style-coal.css Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/static/style-coal.css Wed Jul 18 19:08:25 2012 -0500 @@ -286,3 +286,40 @@ position: relative; top: -3px; } + +/* Comparison */ +.legend { + padding: 1.5% 0 1.5% 0; +} + +.legendinfo { + border: 1px solid #999; + font-size: 80%; + text-align: center; + padding: 0.5%; +} + +.equal { + background-color: #ffffff; +} + +.delete { + background-color: black; + color: white; +} + +.insert { + background-color: #d0d0d0; +} + +.replace { + background-color: #f9f9f9; +} + +.header { + text-align: center; +} + +.block { + border-top: 1px solid #999; +} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/static/style-gitweb.css --- a/mercurial/templates/static/style-gitweb.css Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/static/style-gitweb.css Wed Jul 18 19:08:25 2012 -0500 @@ -126,3 +126,43 @@ top: -3px; font-style: italic; } + +/* Comparison */ +.legend { + padding: 1.5% 0 1.5% 0; +} + +.legendinfo { + border: 1px solid #d9d8d1; + font-size: 80%; + text-align: center; + padding: 0.5%; +} + +.equal { + background-color: #ffffff; +} + +.delete { + background-color: #ffc5ce; +} + +.insert { + background-color: #c5ffc4; +} + +.replace { + background-color: #ffff99; +} + +.comparison { + overflow-x: auto; +} + +.header th { + text-align: center; +} + +.block { + border-top: 1px solid #d9d8d1; +} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/static/style-monoblue.css --- a/mercurial/templates/static/style-monoblue.css Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/static/style-monoblue.css Wed Jul 18 19:08:25 2012 -0500 @@ -477,3 +477,49 @@ position: relative; } /** end of canvas **/ + +/** comparison **/ +.legend { + margin-left: 20px; + padding: 1.5% 0 1.5% 0; +} + +.legendinfo { + border: 1px solid #999; + font-size: 80%; + text-align: center; + padding: 0.5%; +} + +.equal { + background-color: #ffffff; +} + +.delete { + background-color: #ffc5ce; +} + +.insert { + background-color: #c5ffc4; +} + +.replace { + background-color: #ffff99; +} + +.comparison { + overflow-x: auto; +} + +.comparison table td { + padding: 0px 5px; +} + +.header th { + font-weight: bold; +} + +.block { + border-top: 1px solid #999; +} +/** end of comparison **/ diff -r 98823bd0d697 -r a06e2681dd17 mercurial/templates/static/style-paper.css --- a/mercurial/templates/static/style-paper.css Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/templates/static/style-paper.css Wed Jul 18 19:08:25 2012 -0500 @@ -275,3 +275,39 @@ position: relative; top: -3px; } + +/* Comparison */ +.legend { + padding: 1.5% 0 1.5% 0; +} + +.legendinfo { + border: 1px solid #999; + font-size: 80%; + text-align: center; + padding: 0.5%; +} + +.equal { + background-color: #ffffff; +} + +.delete { + background-color: #ffc5ce; +} + +.insert { + background-color: #c5ffc4; +} + +.replace { + background-color: #ffff99; +} + +.header { + text-align: center; +} + +.block { + border-top: 1px solid #999; +} diff -r 98823bd0d697 -r a06e2681dd17 mercurial/transaction.py --- a/mercurial/transaction.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/transaction.py Wed Jul 18 19:08:25 2012 -0500 @@ -164,7 +164,7 @@ _playback(self.journal, self.report, self.opener, self.entries, False) self.report(_("rollback completed\n")) - except: + except Exception: self.report(_("rollback failed - please run hg recover\n")) finally: self.journal = None diff -r 98823bd0d697 -r a06e2681dd17 mercurial/treediscovery.py --- a/mercurial/treediscovery.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/treediscovery.py Wed Jul 18 19:08:25 2012 -0500 @@ -56,11 +56,11 @@ # a 'branch' here is a linear segment of history, with four parts: # head, root, first parent, second parent # (a branch always has two parents (or none) by definition) - unknown = remote.branches(unknown) + unknown = util.deque(remote.branches(unknown)) while unknown: r = [] while unknown: - n = unknown.pop(0) + n = unknown.popleft() if n[0] in seen: continue diff -r 98823bd0d697 -r a06e2681dd17 mercurial/ui.py --- a/mercurial/ui.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/ui.py Wed Jul 18 19:08:25 2012 -0500 @@ -66,7 +66,7 @@ return True if self._reportuntrusted: - self.warn(_('Not trusting file %s from untrusted ' + self.warn(_('not trusting file %s from untrusted ' 'user %s, group %s\n') % (f, user, group)) return False @@ -88,7 +88,7 @@ except error.ConfigError, inst: if trusted: raise - self.warn(_("Ignored: %s\n") % str(inst)) + self.warn(_("ignored: %s\n") % str(inst)) if self.plain(): for k in ('debug', 'fallbackencoding', 'quiet', 'slash', @@ -411,7 +411,7 @@ if user is None and not self.interactive(): try: user = '%s@%s' % (util.getuser(), socket.getfqdn()) - self.warn(_("No username found, using '%s' instead\n") % user) + self.warn(_("no username found, using '%s' instead\n") % user) except KeyError: pass if not user: @@ -490,9 +490,14 @@ def flush(self): try: self.fout.flush() - except: pass + except (IOError, ValueError): pass try: self.ferr.flush() - except: pass + except (IOError, ValueError): pass + + def _isatty(self, fh): + if self.configbool('ui', 'nontty', False): + return False + return util.isatty(fh) def interactive(self): '''is interactive input allowed? @@ -512,7 +517,7 @@ if i is None: # some environments replace stdin without implementing isatty # usually those are non-interactive - return util.isatty(self.fin) + return self._isatty(self.fin) return i @@ -550,12 +555,12 @@ if i is None: # some environments replace stdout without implementing isatty # usually those are non-interactive - return util.isatty(self.fout) + return self._isatty(self.fout) return i def _readline(self, prompt=''): - if util.isatty(self.fin): + if self._isatty(self.fin): try: # magically add command line editing support, where # available @@ -682,7 +687,8 @@ printed.''' if self.tracebackflag: if exc: - traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr) + traceback.print_exception(exc[0], exc[1], exc[2], + file=self.ferr) else: traceback.print_exc(file=self.ferr) return self.tracebackflag diff -r 98823bd0d697 -r a06e2681dd17 mercurial/url.py --- a/mercurial/url.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/url.py Wed Jul 18 19:08:25 2012 -0500 @@ -377,7 +377,8 @@ keyfile = self.auth['key'] certfile = self.auth['cert'] - conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs) + conn = httpsconnection(host, port, keyfile, certfile, *args, + **kwargs) conn.ui = self.ui return conn diff -r 98823bd0d697 -r a06e2681dd17 mercurial/util.h --- a/mercurial/util.h Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/util.h Wed Jul 18 19:08:25 2012 -0500 @@ -109,6 +109,7 @@ typedef int Py_ssize_t; typedef Py_ssize_t (*lenfunc)(PyObject *); typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t); +#define PyInt_FromSsize_t PyInt_FromLong #if !defined(PY_SSIZE_T_MIN) #define PY_SSIZE_T_MAX INT_MAX diff -r 98823bd0d697 -r a06e2681dd17 mercurial/util.py --- a/mercurial/util.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/util.py Wed Jul 18 19:08:25 2012 -0500 @@ -14,7 +14,7 @@ """ from i18n import _ -import error, osutil, encoding +import error, osutil, encoding, collections import errno, re, shutil, sys, tempfile, traceback import os, time, datetime, calendar, textwrap, signal import imp, socket, urllib @@ -24,9 +24,6 @@ else: import posix as platform -platform.encodinglower = encoding.lower -platform.encodingupper = encoding.upper - cachestat = platform.cachestat checkexec = platform.checkexec checklink = platform.checklink @@ -202,15 +199,27 @@ return f +try: + collections.deque.remove + deque = collections.deque +except AttributeError: + # python 2.4 lacks deque.remove + class deque(collections.deque): + def remove(self, val): + for i, v in enumerate(self): + if v == val: + del self[i] + break + def lrucachefunc(func): '''cache most recent results of function calls''' cache = {} - order = [] + order = deque() if func.func_code.co_argcount == 1: def f(arg): if arg not in cache: if len(cache) > 20: - del cache[order.pop(0)] + del cache[order.popleft()] cache[arg] = func(arg) else: order.remove(arg) @@ -220,7 +229,7 @@ def f(*args): if args not in cache: if len(cache) > 20: - del cache[order.pop(0)] + del cache[order.popleft()] cache[args] = func(*args) else: order.remove(args) @@ -617,6 +626,30 @@ except OSError: return True +try: + import re2 + _re2 = None +except ImportError: + _re2 = False + +def compilere(pat): + '''Compile a regular expression, using re2 if possible + + For best performance, use only re2-compatible regexp features.''' + global _re2 + if _re2 is None: + try: + re2.compile + _re2 = True + except ImportError: + _re2 = False + if _re2: + try: + return re2.compile(pat) + except re2.error: + pass + return re.compile(pat) + _fspathcache = {} def fspath(name, root): '''Get name in the case stored in the filesystem @@ -760,9 +793,9 @@ ofp.write(chunk) ifp.close() ofp.close() - except: + except: # re-raises try: os.unlink(temp) - except: pass + except OSError: pass raise return temp @@ -858,7 +891,7 @@ else: yield chunk self.iter = splitbig(in_iter) - self._queue = [] + self._queue = deque() def read(self, l): """Read L bytes of data from the iterator of chunks of data. @@ -878,10 +911,10 @@ if not queue: break - chunk = queue.pop(0) + chunk = queue.popleft() left -= len(chunk) if left < 0: - queue.insert(0, chunk[left:]) + queue.appendleft(chunk[left:]) buf += chunk[:left] else: buf += chunk @@ -1079,7 +1112,7 @@ try: d["d"] = days return parsedate(date, extendeddateformats, d)[0] - except: + except Abort: pass d["d"] = "28" return parsedate(date, extendeddateformats, d)[0] diff -r 98823bd0d697 -r a06e2681dd17 mercurial/verify.py --- a/mercurial/verify.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/verify.py Wed Jul 18 19:08:25 2012 -0500 @@ -87,7 +87,7 @@ # attempt to filter down to real linkrevs linkrevs = [l for l in linkrevs if lrugetctx(l)[f].filenode() == node] - except: + except Exception: pass warn(_(" (expected %s)") % " ".join(map(str, linkrevs))) lr = None # can't be trusted @@ -189,7 +189,7 @@ try: fl = repo.file(f) lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]]) - except: + except Exception: lr = None err(lr, _("in manifest but not in changeset"), f) diff -r 98823bd0d697 -r a06e2681dd17 mercurial/win32.py --- a/mercurial/win32.py Thu Jul 19 00:53:27 2012 +0200 +++ b/mercurial/win32.py Wed Jul 18 19:08:25 2012 -0500 @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import ctypes, errno, os, struct, subprocess, random +import ctypes, errno, os, subprocess, random _kernel32 = ctypes.windll.kernel32 _advapi32 = ctypes.windll.advapi32 @@ -59,6 +59,8 @@ _OPEN_EXISTING = 3 +_FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 + # SetFileAttributes _FILE_ATTRIBUTE_NORMAL = 0x80 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000 @@ -69,13 +71,6 @@ # GetExitCodeProcess _STILL_ACTIVE = 259 -# registry -_HKEY_CURRENT_USER = 0x80000001L -_HKEY_LOCAL_MACHINE = 0x80000002L -_KEY_READ = 0x20019 -_REG_SZ = 1 -_REG_DWORD = 4 - class _STARTUPINFO(ctypes.Structure): _fields_ = [('cb', _DWORD), ('lpReserved', _LPSTR), @@ -102,8 +97,7 @@ ('dwProcessId', _DWORD), ('dwThreadId', _DWORD)] -_DETACHED_PROCESS = 0x00000008 -_STARTF_USESHOWWINDOW = 0x00000001 +_CREATE_NO_WINDOW = 0x08000000 _SW_HIDE = 0 class _COORD(ctypes.Structure): @@ -179,17 +173,6 @@ _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p] _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL -_advapi32.RegOpenKeyExA.argtypes = [_HANDLE, _LPCSTR, _DWORD, _DWORD, - ctypes.c_void_p] -_advapi32.RegOpenKeyExA.restype = _LONG - -_advapi32.RegQueryValueExA.argtypes = [_HANDLE, _LPCSTR, ctypes.c_void_p, - ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] -_advapi32.RegQueryValueExA.restype = _LONG - -_advapi32.RegCloseKey.argtypes = [_HANDLE] -_advapi32.RegCloseKey.restype = _LONG - _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p] _advapi32.GetUserNameA.restype = _BOOL @@ -210,7 +193,7 @@ def _getfileinfo(name): fh = _kernel32.CreateFileA(name, 0, _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE, - None, _OPEN_EXISTING, 0, None) + None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None) if fh == _INVALID_HANDLE_VALUE: _raiseoserror(name) try: @@ -232,20 +215,18 @@ '''return number of hardlinks for the given file''' return _getfileinfo(name).nNumberOfLinks -def samefile(fpath1, fpath2): - '''Returns whether fpath1 and fpath2 refer to the same file. This is only - guaranteed to work for files, not directories.''' - res1 = _getfileinfo(fpath1) - res2 = _getfileinfo(fpath2) +def samefile(path1, path2): + '''Returns whether path1 and path2 refer to the same file or directory.''' + res1 = _getfileinfo(path1) + res2 = _getfileinfo(path2) return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber and res1.nFileIndexHigh == res2.nFileIndexHigh and res1.nFileIndexLow == res2.nFileIndexLow) -def samedevice(fpath1, fpath2): - '''Returns whether fpath1 and fpath2 are on the same device. This is only - guaranteed to work for files, not directories.''' - res1 = _getfileinfo(fpath1) - res2 = _getfileinfo(fpath2) +def samedevice(path1, path2): + '''Returns whether path1 and path2 are on the same device.''' + res1 = _getfileinfo(path1) + res2 = _getfileinfo(path2) return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber def testpid(pid): @@ -261,50 +242,13 @@ _kernel32.CloseHandle(h) return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER -def lookupreg(key, valname=None, scope=None): - ''' Look up a key/value name in the Windows registry. - - valname: value name. If unspecified, the default value for the key - is used. - scope: optionally specify scope for registry lookup, this can be - a sequence of scopes to look up in order. Default (CURRENT_USER, - LOCAL_MACHINE). - ''' - byref = ctypes.byref - if scope is None: - scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE) - elif not isinstance(scope, (list, tuple)): - scope = (scope,) - for s in scope: - kh = _HANDLE() - res = _advapi32.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh)) - if res != _ERROR_SUCCESS: - continue - try: - size = _DWORD(600) - type = _DWORD() - buf = ctypes.create_string_buffer(size.value + 1) - res = _advapi32.RegQueryValueExA(kh.value, valname, None, - byref(type), buf, byref(size)) - if res != _ERROR_SUCCESS: - continue - if type.value == _REG_SZ: - # string is in ANSI code page, aka local encoding - return buf.value - elif type.value == _REG_DWORD: - fmt = ' '2.6': # The inotify extension is only usable with Linux 2.6 kernels. # You also need a reasonably recent C library. diff -r 98823bd0d697 -r a06e2681dd17 tests/get-with-headers.py --- a/tests/get-with-headers.py Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/get-with-headers.py Wed Jul 18 19:08:25 2012 -0500 @@ -21,14 +21,14 @@ tag = None def request(host, path, show): - + assert not path.startswith('/'), path global tag headers = {} if tag: headers['If-None-Match'] = tag conn = httplib.HTTPConnection(host) - conn.request("GET", path, None, headers) + conn.request("GET", '/' + path, None, headers) response = conn.getresponse() print response.status, reasons.get(response.reason, response.reason) for h in [h.lower() for h in show]: diff -r 98823bd0d697 -r a06e2681dd17 tests/hghave --- a/tests/hghave Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/hghave Wed Jul 18 19:08:25 2012 -0500 @@ -4,277 +4,10 @@ prefixed with "no-", the absence of feature is tested. """ import optparse -import os, stat -import re import sys -import tempfile - -tempprefix = 'hg-hghave-' - -def matchoutput(cmd, regexp, ignorestatus=False): - """Return True if cmd executes successfully and its output - is matched by the supplied regular expression. - """ - r = re.compile(regexp) - fh = os.popen(cmd) - s = fh.read() - try: - ret = fh.close() - except IOError: - # Happen in Windows test environment - ret = 1 - return (ignorestatus or ret is None) and r.search(s) - -def has_baz(): - return matchoutput('baz --version 2>&1', r'baz Bazaar version') - -def has_bzr(): - try: - import bzrlib - return bzrlib.__doc__ != None - except ImportError: - return False - -def has_bzr114(): - try: - import bzrlib - return (bzrlib.__doc__ != None - and bzrlib.version_info[:2] >= (1, 14)) - except ImportError: - return False - -def has_cvs(): - re = r'Concurrent Versions System.*?server' - return matchoutput('cvs --version 2>&1', re) and not has_msys() - -def has_darcs(): - return matchoutput('darcs --version', r'2\.[2-9]', True) - -def has_mtn(): - return matchoutput('mtn --version', r'monotone', True) and not matchoutput( - 'mtn --version', r'monotone 0\.', True) - -def has_eol_in_paths(): - try: - fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r') - os.close(fd) - os.remove(path) - return True - except: - return False - -def has_executablebit(): - try: - EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH - fh, fn = tempfile.mkstemp(dir=".", prefix='hg-checkexec-') - try: - os.close(fh) - m = os.stat(fn).st_mode & 0777 - new_file_has_exec = m & EXECFLAGS - os.chmod(fn, m ^ EXECFLAGS) - exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m) - finally: - os.unlink(fn) - except (IOError, OSError): - # we don't care, the user probably won't be able to commit anyway - return False - return not (new_file_has_exec or exec_flags_cannot_flip) - -def has_icasefs(): - # Stolen from mercurial.util - fd, path = tempfile.mkstemp(prefix=tempprefix, dir='.') - os.close(fd) - try: - s1 = os.stat(path) - d, b = os.path.split(path) - p2 = os.path.join(d, b.upper()) - if path == p2: - p2 = os.path.join(d, b.lower()) - try: - s2 = os.stat(p2) - return s2 == s1 - except: - return False - finally: - os.remove(path) - -def has_inotify(): - try: - import hgext.inotify.linux.watcher - return True - except ImportError: - return False - -def has_fifo(): - return hasattr(os, "mkfifo") - -def has_cacheable_fs(): - from mercurial import util - - fd, path = tempfile.mkstemp(prefix=tempprefix) - os.close(fd) - try: - return util.cachestat(path).cacheable() - finally: - os.remove(path) - -def has_lsprof(): - try: - import _lsprof - return True - except ImportError: - return False - -def has_gettext(): - return matchoutput('msgfmt --version', 'GNU gettext-tools') - -def has_git(): - return matchoutput('git --version 2>&1', r'^git version') - -def has_docutils(): - try: - from docutils.core import publish_cmdline - return True - except ImportError: - return False +import hghave -def getsvnversion(): - m = matchoutput('svn --version 2>&1', r'^svn,\s+version\s+(\d+)\.(\d+)') - if not m: - return (0, 0) - return (int(m.group(1)), int(m.group(2))) - -def has_svn15(): - return getsvnversion() >= (1, 5) - -def has_svn13(): - return getsvnversion() >= (1, 3) - -def has_svn(): - return matchoutput('svn --version 2>&1', r'^svn, version') and \ - matchoutput('svnadmin --version 2>&1', r'^svnadmin, version') - -def has_svn_bindings(): - try: - import svn.core - version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR - if version < (1, 4): - return False - return True - except ImportError: - return False - -def has_p4(): - return matchoutput('p4 -V', r'Rev\. P4/') and matchoutput('p4d -V', r'Rev\. P4D/') - -def has_symlink(): - if not hasattr(os, "symlink"): - return False - name = tempfile.mktemp(dir=".", prefix='hg-checklink-') - try: - os.symlink(".", name) - os.unlink(name) - return True - except (OSError, AttributeError): - return False - -def has_tla(): - return matchoutput('tla --version 2>&1', r'The GNU Arch Revision') - -def has_gpg(): - return matchoutput('gpg --version 2>&1', r'GnuPG') - -def has_unix_permissions(): - d = tempfile.mkdtemp(prefix=tempprefix, dir=".") - try: - fname = os.path.join(d, 'foo') - for umask in (077, 007, 022): - os.umask(umask) - f = open(fname, 'w') - f.close() - mode = os.stat(fname).st_mode - os.unlink(fname) - if mode & 0777 != ~umask & 0666: - return False - return True - finally: - os.rmdir(d) - -def has_pyflakes(): - return matchoutput('echo "import re" 2>&1 | pyflakes', - r":1: 're' imported but unused", - True) - -def has_pygments(): - try: - import pygments - return True - except ImportError: - return False - -def has_outer_repo(): - return matchoutput('hg root 2>&1', r'') - -def has_ssl(): - try: - import ssl - import OpenSSL - OpenSSL.SSL.Context - return True - except ImportError: - return False - -def has_windows(): - return os.name == 'nt' - -def has_system_sh(): - return os.name != 'nt' - -def has_serve(): - return os.name != 'nt' # gross approximation - -def has_tic(): - return matchoutput('test -x "`which tic`"', '') - -def has_msys(): - return os.getenv('MSYSTEM') - -checks = { - "baz": (has_baz, "GNU Arch baz client"), - "bzr": (has_bzr, "Canonical's Bazaar client"), - "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"), - "cacheable": (has_cacheable_fs, "cacheable filesystem"), - "cvs": (has_cvs, "cvs client/server"), - "darcs": (has_darcs, "darcs client"), - "docutils": (has_docutils, "Docutils text processing library"), - "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"), - "execbit": (has_executablebit, "executable bit"), - "fifo": (has_fifo, "named pipes"), - "gettext": (has_gettext, "GNU Gettext (msgfmt)"), - "git": (has_git, "git command line client"), - "gpg": (has_gpg, "gpg client"), - "icasefs": (has_icasefs, "case insensitive file system"), - "inotify": (has_inotify, "inotify extension support"), - "lsprof": (has_lsprof, "python lsprof module"), - "mtn": (has_mtn, "monotone client (>= 1.0)"), - "outer-repo": (has_outer_repo, "outer repo"), - "p4": (has_p4, "Perforce server and client"), - "pyflakes": (has_pyflakes, "Pyflakes python linter"), - "pygments": (has_pygments, "Pygments source highlighting library"), - "serve": (has_serve, "platform and python can manage 'hg serve -d'"), - "ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"), - "svn": (has_svn, "subversion client and admin tools"), - "svn13": (has_svn13, "subversion client and admin tools >= 1.3"), - "svn15": (has_svn15, "subversion client and admin tools >= 1.5"), - "svn-bindings": (has_svn_bindings, "subversion python bindings"), - "symlink": (has_symlink, "symbolic links"), - "system-sh": (has_system_sh, "system() uses sh"), - "tic": (has_tic, "terminfo compiler"), - "tla": (has_tla, "GNU Arch tla client"), - "unix-permissions": (has_unix_permissions, "unix-style permissions"), - "windows": (has_windows, "Windows"), - "msys": (has_msys, "Windows with MSYS"), -} +checks = hghave.checks def list_features(): for name, feature in checks.iteritems(): diff -r 98823bd0d697 -r a06e2681dd17 tests/hghave.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/hghave.py Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,308 @@ +import os, stat, socket +import re +import sys +import tempfile + +tempprefix = 'hg-hghave-' + +def matchoutput(cmd, regexp, ignorestatus=False): + """Return True if cmd executes successfully and its output + is matched by the supplied regular expression. + """ + r = re.compile(regexp) + fh = os.popen(cmd) + s = fh.read() + try: + ret = fh.close() + except IOError: + # Happen in Windows test environment + ret = 1 + return (ignorestatus or ret is None) and r.search(s) + +def has_baz(): + return matchoutput('baz --version 2>&1', r'baz Bazaar version') + +def has_bzr(): + try: + import bzrlib + return bzrlib.__doc__ is not None + except ImportError: + return False + +def has_bzr114(): + try: + import bzrlib + return (bzrlib.__doc__ is not None + and bzrlib.version_info[:2] >= (1, 14)) + except ImportError: + return False + +def has_cvs(): + re = r'Concurrent Versions System.*?server' + return matchoutput('cvs --version 2>&1', re) and not has_msys() + +def has_darcs(): + return matchoutput('darcs --version', r'2\.[2-9]', True) + +def has_mtn(): + return matchoutput('mtn --version', r'monotone', True) and not matchoutput( + 'mtn --version', r'monotone 0\.', True) + +def has_eol_in_paths(): + try: + fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r') + os.close(fd) + os.remove(path) + return True + except (IOError, OSError): + return False + +def has_executablebit(): + try: + EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix) + try: + os.close(fh) + m = os.stat(fn).st_mode & 0777 + new_file_has_exec = m & EXECFLAGS + os.chmod(fn, m ^ EXECFLAGS) + exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m) + finally: + os.unlink(fn) + except (IOError, OSError): + # we don't care, the user probably won't be able to commit anyway + return False + return not (new_file_has_exec or exec_flags_cannot_flip) + +def has_icasefs(): + # Stolen from mercurial.util + fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix) + os.close(fd) + try: + s1 = os.stat(path) + d, b = os.path.split(path) + p2 = os.path.join(d, b.upper()) + if path == p2: + p2 = os.path.join(d, b.lower()) + try: + s2 = os.stat(p2) + return s2 == s1 + except OSError: + return False + finally: + os.remove(path) + +def has_inotify(): + try: + import hgext.inotify.linux.watcher + except ImportError: + return False + name = tempfile.mktemp(dir='.', prefix=tempprefix) + sock = socket.socket(socket.AF_UNIX) + try: + sock.bind(name) + except socket.error, err: + return False + sock.close() + os.unlink(name) + return True + +def has_fifo(): + if getattr(os, "mkfifo", None) is None: + return False + name = tempfile.mktemp(dir='.', prefix=tempprefix) + try: + os.mkfifo(name) + os.unlink(name) + return True + except OSError: + return False + +def has_cacheable_fs(): + from mercurial import util + + fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix) + os.close(fd) + try: + return util.cachestat(path).cacheable() + finally: + os.remove(path) + +def has_lsprof(): + try: + import _lsprof + return True + except ImportError: + return False + +def has_gettext(): + return matchoutput('msgfmt --version', 'GNU gettext-tools') + +def has_git(): + return matchoutput('git --version 2>&1', r'^git version') + +def has_docutils(): + try: + from docutils.core import publish_cmdline + return True + except ImportError: + return False + +def getsvnversion(): + m = matchoutput('svn --version 2>&1', r'^svn,\s+version\s+(\d+)\.(\d+)') + if not m: + return (0, 0) + return (int(m.group(1)), int(m.group(2))) + +def has_svn15(): + return getsvnversion() >= (1, 5) + +def has_svn13(): + return getsvnversion() >= (1, 3) + +def has_svn(): + return matchoutput('svn --version 2>&1', r'^svn, version') and \ + matchoutput('svnadmin --version 2>&1', r'^svnadmin, version') + +def has_svn_bindings(): + try: + import svn.core + version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR + if version < (1, 4): + return False + return True + except ImportError: + return False + +def has_p4(): + return (matchoutput('p4 -V', r'Rev\. P4/') and + matchoutput('p4d -V', r'Rev\. P4D/')) + +def has_symlink(): + if getattr(os, "symlink", None) is None: + return False + name = tempfile.mktemp(dir='.', prefix=tempprefix) + try: + os.symlink(".", name) + os.unlink(name) + return True + except (OSError, AttributeError): + return False + +def has_hardlink(): + from mercurial import util + fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix) + os.close(fh) + name = tempfile.mktemp(dir='.', prefix=tempprefix) + try: + try: + util.oslink(fn, name) + os.unlink(name) + return True + except OSError: + return False + finally: + os.unlink(fn) + +def has_tla(): + return matchoutput('tla --version 2>&1', r'The GNU Arch Revision') + +def has_gpg(): + return matchoutput('gpg --version 2>&1', r'GnuPG') + +def has_unix_permissions(): + d = tempfile.mkdtemp(dir='.', prefix=tempprefix) + try: + fname = os.path.join(d, 'foo') + for umask in (077, 007, 022): + os.umask(umask) + f = open(fname, 'w') + f.close() + mode = os.stat(fname).st_mode + os.unlink(fname) + if mode & 0777 != ~umask & 0666: + return False + return True + finally: + os.rmdir(d) + +def has_pyflakes(): + return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"", + r":1: 're' imported but unused", + True) + +def has_pygments(): + try: + import pygments + return True + except ImportError: + return False + +def has_outer_repo(): + # failing for other reasons than 'no repo' imply that there is a repo + return not matchoutput('hg root 2>&1', + r'abort: no repository found', True) + +def has_ssl(): + try: + import ssl + import OpenSSL + OpenSSL.SSL.Context + return True + except ImportError: + return False + +def has_windows(): + return os.name == 'nt' + +def has_system_sh(): + return os.name != 'nt' + +def has_serve(): + return os.name != 'nt' # gross approximation + +def has_tic(): + return matchoutput('test -x "`which tic`"', '') + +def has_msys(): + return os.getenv('MSYSTEM') + +checks = { + "true": (lambda: True, "yak shaving"), + "false": (lambda: False, "nail clipper"), + "baz": (has_baz, "GNU Arch baz client"), + "bzr": (has_bzr, "Canonical's Bazaar client"), + "bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"), + "cacheable": (has_cacheable_fs, "cacheable filesystem"), + "cvs": (has_cvs, "cvs client/server"), + "darcs": (has_darcs, "darcs client"), + "docutils": (has_docutils, "Docutils text processing library"), + "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"), + "execbit": (has_executablebit, "executable bit"), + "fifo": (has_fifo, "named pipes"), + "gettext": (has_gettext, "GNU Gettext (msgfmt)"), + "git": (has_git, "git command line client"), + "gpg": (has_gpg, "gpg client"), + "hardlink": (has_hardlink, "hardlinks"), + "icasefs": (has_icasefs, "case insensitive file system"), + "inotify": (has_inotify, "inotify extension support"), + "lsprof": (has_lsprof, "python lsprof module"), + "mtn": (has_mtn, "monotone client (>= 1.0)"), + "outer-repo": (has_outer_repo, "outer repo"), + "p4": (has_p4, "Perforce server and client"), + "pyflakes": (has_pyflakes, "Pyflakes python linter"), + "pygments": (has_pygments, "Pygments source highlighting library"), + "serve": (has_serve, "platform and python can manage 'hg serve -d'"), + "ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"), + "svn": (has_svn, "subversion client and admin tools"), + "svn13": (has_svn13, "subversion client and admin tools >= 1.3"), + "svn15": (has_svn15, "subversion client and admin tools >= 1.5"), + "svn-bindings": (has_svn_bindings, "subversion python bindings"), + "symlink": (has_symlink, "symbolic links"), + "system-sh": (has_system_sh, "system() uses sh"), + "tic": (has_tic, "terminfo compiler"), + "tla": (has_tla, "GNU Arch tla client"), + "unix-permissions": (has_unix_permissions, "unix-style permissions"), + "windows": (has_windows, "Windows"), + "msys": (has_msys, "Windows with MSYS"), +} diff -r 98823bd0d697 -r a06e2681dd17 tests/histedit-helpers.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/histedit-helpers.sh Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,6 @@ +fixbundle() { + grep -v 'saving bundle' | grep -v 'saved backup' | \ + grep -v added | grep -v adding | \ + grep -v "unable to find 'e' for patching" | \ + grep -v "e: No such file or directory" +} diff -r 98823bd0d697 -r a06e2681dd17 tests/missing-comment.hg Binary file tests/missing-comment.hg has changed diff -r 98823bd0d697 -r a06e2681dd17 tests/notcapable --- a/tests/notcapable Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/notcapable Wed Jul 18 19:08:25 2012 -0500 @@ -6,13 +6,18 @@ fi cat > notcapable-$CAP.py << EOF -from mercurial import extensions, repo +from mercurial import extensions, peer, localrepo def extsetup(): - extensions.wrapfunction(repo.repository, 'capable', wrapper) -def wrapper(orig, self, name, *args, **kwargs): + extensions.wrapfunction(peer.peerrepository, 'capable', wrapcapable) + extensions.wrapfunction(localrepo.localrepository, 'peer', wrappeer) +def wrapcapable(orig, self, name, *args, **kwargs): if name in '$CAP'.split(' '): return False return orig(self, name, *args, **kwargs) +def wrappeer(orig, self): + # Since we're disabling some newer features, we need to make sure local + # repos add in the legacy features again. + return localrepo.locallegacypeer(self) EOF echo '[extensions]' >> $HGRCPATH diff -r 98823bd0d697 -r a06e2681dd17 tests/printenv.py --- a/tests/printenv.py Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/printenv.py Wed Jul 18 19:08:25 2012 -0500 @@ -3,7 +3,7 @@ # put something like this in the repo .hg/hgrc: # # [hooks] -# changegroup = python "$TESTDIR"/printenv.py [exit] [output] +# changegroup = python "$TESTDIR/printenv.py" [exit] [output] # # - is a mandatory argument (e.g. "changegroup") # - [exit] is the exit code of the hook (default: 0) @@ -32,13 +32,17 @@ # variables with empty values may not exist on all platforms, filter # them now for portability sake. -env = [k for k, v in os.environ.iteritems() +env = [(k, v) for k, v in os.environ.iteritems() if k.startswith("HG_") and v] env.sort() out.write("%s hook: " % name) -for v in env: - out.write("%s=%s " % (v, os.environ[v])) +if os.name == 'nt': + filter = lambda x: x.replace('\\', '/') +else: + filter = lambda x: x +vars = ["%s=%s" % (k, filter(v)) for k, v in env] +out.write(" ".join(vars)) out.write("\n") out.close() diff -r 98823bd0d697 -r a06e2681dd17 tests/run-tests.py --- a/tests/run-tests.py Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/run-tests.py Wed Jul 18 19:08:25 2012 -0500 @@ -594,6 +594,20 @@ # can generate the surrounding doctest magic inpython = False + # True or False when in a true or false conditional section + skipping = None + + def hghave(reqs): + # TODO: do something smarter when all other uses of hghave is gone + tdir = TESTDIR.replace('\\', '/') + proc = Popen4('%s -c "%s/hghave %s"' % + (options.shell, tdir, ' '.join(reqs)), wd, 0) + proc.communicate() + ret = proc.wait() + if wifexited(ret): + ret = os.WEXITSTATUS(ret) + return ret == 0 + f = open(test) t = f.readlines() f.close() @@ -606,7 +620,24 @@ for n, l in enumerate(t): if not l.endswith('\n'): l += '\n' - if l.startswith(' >>> '): # python inlines + if l.startswith('#if'): + if skipping is not None: + after.setdefault(pos, []).append(' !!! nested #if\n') + skipping = not hghave(l.split()[1:]) + after.setdefault(pos, []).append(l) + elif l.startswith('#else'): + if skipping is None: + after.setdefault(pos, []).append(' !!! missing #if\n') + skipping = not skipping + after.setdefault(pos, []).append(l) + elif l.startswith('#endif'): + if skipping is None: + after.setdefault(pos, []).append(' !!! missing #if\n') + skipping = None + after.setdefault(pos, []).append(l) + elif skipping: + after.setdefault(pos, []).append(l) + elif l.startswith(' >>> '): # python inlines after.setdefault(pos, []).append(l) prepos = pos pos = n @@ -617,7 +648,7 @@ script.append('%s -m heredoctest < '): # continuations after.setdefault(prepos, []).append(l) @@ -644,6 +678,8 @@ if inpython: script.append("EOF\n") + if skipping is not None: + after.setdefault(pos, []).append(' !!! missing #endif\n') addsalt(n + 1, False) # Write out the script and execute it @@ -860,7 +896,7 @@ tf = open(testpath) firstline = tf.readline().rstrip() tf.close() - except: + except IOError: firstline = '' lctest = test.lower() @@ -877,7 +913,7 @@ # Make a tmp subdirectory to work in testtmp = os.environ["TESTTMP"] = os.environ["HOME"] = \ - os.path.join(HGTMP, os.path.basename(test)).replace('\\', '/') + os.path.join(HGTMP, os.path.basename(test)) replacements = [ (r':%s\b' % options.port, ':$HGPORT'), @@ -1187,6 +1223,7 @@ os.environ['http_proxy'] = '' os.environ['no_proxy'] = '' os.environ['NO_PROXY'] = '' + os.environ['TERM'] = 'xterm' # unset env related to hooks for k in os.environ.keys(): @@ -1213,7 +1250,12 @@ #shutil.rmtree(tmpdir) os.makedirs(tmpdir) else: - tmpdir = tempfile.mkdtemp('', 'hgtests.') + d = None + if os.name == 'nt': + # without this, we get the default temp dir location, but + # in all lowercase, which causes troubles with paths (issue3490) + d = os.getenv('TMP') + tmpdir = tempfile.mkdtemp('', 'hgtests.', d) HGTMP = os.environ['HGTMP'] = os.path.realpath(tmpdir) DAEMON_PIDS = None HGRCPATH = None diff -r 98823bd0d697 -r a06e2681dd17 tests/test-1102.t --- a/tests/test-1102.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-1102.t Wed Jul 18 19:08:25 2012 -0500 @@ -14,3 +14,4 @@ tip 3:a49829c4fc11 t1 0:f7b1eb17ad24 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-1993.t --- a/tests/test-1993.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-1993.t Wed Jul 18 19:08:25 2012 -0500 @@ -44,3 +44,5 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: Added tag default for changeset f7b1eb17ad24 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-586.t --- a/tests/test-586.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-586.t Wed Jul 18 19:08:25 2012 -0500 @@ -89,3 +89,4 @@ src/b tst/a + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-abort-checkin.t --- a/tests/test-abort-checkin.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-abort-checkin.t Wed Jul 18 19:08:25 2012 -0500 @@ -31,3 +31,5 @@ rollback completed abort: no commits allowed [255] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-acl.t --- a/tests/test-acl.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-acl.t Wed Jul 18 19:08:25 2012 -0500 @@ -70,9 +70,6 @@ updating to branch default 3 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ echo '[extensions]' >> $HGRCPATH - $ echo 'acl =' >> $HGRCPATH - $ config=b/.hg/hgrc Extension disabled for lack of a hook @@ -1922,3 +1919,155 @@ no rollback information available 2:fb35475503ef +User 'astro' must not be denied + + $ init_config + $ echo "[acl.deny.branches]" >> $config + $ echo "default = !astro" >> $config + $ do_push astro + Pushing as user astro + hgrc = """ + [acl] + sources = push + [extensions] + [acl.deny.branches] + default = !astro + """ + pushing to ../b + query 1; heads + searching for changes + all remote heads known locally + 4 changesets found + list of changesets: + ef1ea85a6374b77d6da9dcda9541f498f2d17df7 + f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd + 911600dab2ae7a9baff75958b84fe606851ce955 + e8fc755d4d8217ee5b0c2bb41558c40d43b92c01 + adding changesets + bundling: 1/4 changesets (25.00%) + bundling: 2/4 changesets (50.00%) + bundling: 3/4 changesets (75.00%) + bundling: 4/4 changesets (100.00%) + bundling: 1/4 manifests (25.00%) + bundling: 2/4 manifests (50.00%) + bundling: 3/4 manifests (75.00%) + bundling: 4/4 manifests (100.00%) + bundling: abc.txt 1/4 files (25.00%) + bundling: foo/Bar/file.txt 2/4 files (50.00%) + bundling: foo/file.txt 3/4 files (75.00%) + bundling: quux/file.py 4/4 files (100.00%) + changesets: 1 chunks + add changeset ef1ea85a6374 + changesets: 2 chunks + add changeset f9cafe1212c8 + changesets: 3 chunks + add changeset 911600dab2ae + changesets: 4 chunks + add changeset e8fc755d4d82 + adding manifests + manifests: 1/4 chunks (25.00%) + manifests: 2/4 chunks (50.00%) + manifests: 3/4 chunks (75.00%) + manifests: 4/4 chunks (100.00%) + adding file changes + adding abc.txt revisions + files: 1/4 chunks (25.00%) + adding foo/Bar/file.txt revisions + files: 2/4 chunks (50.00%) + adding foo/file.txt revisions + files: 3/4 chunks (75.00%) + adding quux/file.py revisions + files: 4/4 chunks (100.00%) + added 4 changesets with 4 changes to 4 files (+1 heads) + calling hook pretxnchangegroup.acl: hgext.acl.hook + acl: checking access for user "astro" + acl: acl.allow.branches not enabled + acl: acl.deny.branches enabled, 0 entries for user astro + acl: acl.allow not enabled + acl: acl.deny not enabled + acl: branch access granted: "ef1ea85a6374" on branch "default" + acl: path access granted: "ef1ea85a6374" + acl: branch access granted: "f9cafe1212c8" on branch "default" + acl: path access granted: "f9cafe1212c8" + acl: branch access granted: "911600dab2ae" on branch "default" + acl: path access granted: "911600dab2ae" + acl: branch access granted: "e8fc755d4d82" on branch "foobar" + acl: path access granted: "e8fc755d4d82" + updating the branch cache + checking for updated bookmarks + repository tip rolled back to revision 2 (undo push) + 2:fb35475503ef + + +Non-astro users must be denied + + $ do_push george + Pushing as user george + hgrc = """ + [acl] + sources = push + [extensions] + [acl.deny.branches] + default = !astro + """ + pushing to ../b + query 1; heads + searching for changes + all remote heads known locally + invalidating branch cache (tip differs) + 4 changesets found + list of changesets: + ef1ea85a6374b77d6da9dcda9541f498f2d17df7 + f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd + 911600dab2ae7a9baff75958b84fe606851ce955 + e8fc755d4d8217ee5b0c2bb41558c40d43b92c01 + adding changesets + bundling: 1/4 changesets (25.00%) + bundling: 2/4 changesets (50.00%) + bundling: 3/4 changesets (75.00%) + bundling: 4/4 changesets (100.00%) + bundling: 1/4 manifests (25.00%) + bundling: 2/4 manifests (50.00%) + bundling: 3/4 manifests (75.00%) + bundling: 4/4 manifests (100.00%) + bundling: abc.txt 1/4 files (25.00%) + bundling: foo/Bar/file.txt 2/4 files (50.00%) + bundling: foo/file.txt 3/4 files (75.00%) + bundling: quux/file.py 4/4 files (100.00%) + changesets: 1 chunks + add changeset ef1ea85a6374 + changesets: 2 chunks + add changeset f9cafe1212c8 + changesets: 3 chunks + add changeset 911600dab2ae + changesets: 4 chunks + add changeset e8fc755d4d82 + adding manifests + manifests: 1/4 chunks (25.00%) + manifests: 2/4 chunks (50.00%) + manifests: 3/4 chunks (75.00%) + manifests: 4/4 chunks (100.00%) + adding file changes + adding abc.txt revisions + files: 1/4 chunks (25.00%) + adding foo/Bar/file.txt revisions + files: 2/4 chunks (50.00%) + adding foo/file.txt revisions + files: 3/4 chunks (75.00%) + adding quux/file.py revisions + files: 4/4 chunks (100.00%) + added 4 changesets with 4 changes to 4 files (+1 heads) + calling hook pretxnchangegroup.acl: hgext.acl.hook + acl: checking access for user "george" + acl: acl.allow.branches not enabled + acl: acl.deny.branches enabled, 1 entries for user george + acl: acl.allow not enabled + acl: acl.deny not enabled + error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") + transaction abort! + rollback completed + abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") + no rollback information available + 2:fb35475503ef + + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-add.t --- a/tests/test-add.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-add.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" no-windows || exit 80 - $ hg init a $ cd a $ echo a > a @@ -35,6 +33,7 @@ A a A b +#if no-windows $ echo foo > con.xml $ hg --config ui.portablefilenames=jump add con.xml abort: ui.portablefilenames value is invalid ('jump') @@ -52,6 +51,11 @@ A a A b A con.xml + $ hg forget con.xml + $ rm con.xml +#endif + +#if eol-in-paths $ echo bla > 'hello:world' $ hg --config ui.portablefilenames=abort add adding hello:world @@ -60,15 +64,14 @@ $ hg st A a A b - A con.xml ? hello:world $ hg --config ui.portablefilenames=ignore add adding hello:world $ hg st A a A b - A con.xml A hello:world +#endif $ hg ci -m 0 --traceback @@ -134,3 +137,4 @@ A c ? a.orig + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-addremove-similar.t --- a/tests/test-addremove-similar.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-addremove-similar.t Wed Jul 18 19:08:25 2012 -0500 @@ -98,3 +98,5 @@ $ hg addremove -s80 adding c recording removal of d/a as rename to c (100% similar) (glob) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-addremove.t --- a/tests/test-addremove.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-addremove.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" no-windows || exit 80 - $ hg init rep $ cd rep $ mkdir dir @@ -12,19 +10,16 @@ foo committed changeset 0:6f7f953567a2 $ cd dir/ - $ touch ../foo_2 bar_2 con.xml + $ touch ../foo_2 bar_2 $ hg -v addremove adding dir/bar_2 - adding dir/con.xml adding foo_2 - warning: filename contains 'con', which is reserved on Windows: 'dir/con.xml' $ hg -v commit -m "add 2" dir/bar_2 - dir/con.xml foo_2 - committed changeset 1:6bb597da00f1 + committed changeset 1:e65414bf35c5 + $ cd ../.. - $ cd .. $ hg init sim $ cd sim $ echo a > a @@ -50,3 +45,4 @@ adding d recording removal of a as rename to b (100% similar) $ hg commit -mb + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-alias.t --- a/tests/test-alias.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-alias.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" system-sh || exit 80 - $ HGFOO=BAR; export HGFOO $ cat >> $HGRCPATH < [extensions] @@ -26,14 +24,14 @@ > dln = lognull --debug > nousage = rollback > put = export -r 0 -o "\$FOO/%R.diff" - > blank = !echo - > self = !echo '\$0' - > echo = !echo '\$@' - > echo1 = !echo '\$1' - > echo2 = !echo '\$2' - > echo13 = !echo '\$1' '\$3' - > count = !hg log -r '\$@' --template='.' | wc -c | sed -e 's/ //g' - > mcount = !hg log \$@ --template='.' | wc -c | sed -e 's/ //g' + > blank = !printf '\n' + > self = !printf '\$0\n' + > echoall = !printf '\$@\n' + > echo1 = !printf '\$1\n' + > echo2 = !printf '\$2\n' + > echo13 = !printf '\$1 \$3\n' + > count = !hg log -r "\$@" --template=. | wc -c | sed -e 's/ //g' + > mcount = !hg log \$@ --template=. | wc -c | sed -e 's/ //g' > rt = root > tglog = glog --template "{rev}:{node|short}: '{desc}' {branches}\n" > idalias = id @@ -41,10 +39,10 @@ > idaliasshell = !echo test > parentsshell1 = !echo one > parentsshell2 = !echo two - > escaped1 = !echo 'test\$\$test' - > escaped2 = !echo "HGFOO is \$\$HGFOO" - > escaped3 = !echo "\$1 is \$\$\$1" - > escaped4 = !echo '\$\$0' '\$\$@' + > escaped1 = !printf 'test\$\$test\n' + > escaped2 = !sh -c 'echo "HGFOO is \$\$HGFOO"' + > escaped3 = !sh -c 'echo "\$1 is \$\$\$1"' + > escaped4 = !printf '\$\$0 \$\$@\n' > > [defaults] > mylog = -q @@ -111,8 +109,10 @@ optional repository +#if no-outer-repo $ hg optionalrepo init +#endif $ cd alias $ cat > .hg/hgrc < [alias] @@ -200,11 +200,11 @@ $ hg self self - $ hg echo + $ hg echoall - $ hg echo foo + $ hg echoall foo foo - $ hg echo 'test $2' foo + $ hg echoall 'test $2' foo test $2 foo $ hg echo1 foo bar baz foo @@ -270,7 +270,7 @@ 0 $ hg --cwd .. count 'branch(default)' 2 - $ hg echo --cwd .. + $ hg echoall --cwd .. @@ -278,11 +278,11 @@ $ cat >> .hg/hgrc < [alias] - > subalias = !echo sub \$@ + > subalias = !echo sub > EOF $ cat >> ../.hg/hgrc < [alias] - > mainalias = !echo main \$@ + > mainalias = !echo main > EOF @@ -340,24 +340,24 @@ 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] @@ -367,24 +367,24 @@ 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] @@ -394,24 +394,24 @@ 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] @@ -424,3 +424,5 @@ This shouldn't: $ hg --config alias.log='id' history + + $ cd ../.. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-annotate.t --- a/tests/test-annotate.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-annotate.t Wed Jul 18 19:08:25 2012 -0500 @@ -319,3 +319,4 @@ 1: 1: b b + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-archive-symlinks.t --- a/tests/test-archive-symlinks.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-archive-symlinks.t Wed Jul 18 19:08:25 2012 -0500 @@ -36,3 +36,5 @@ $ cd zip $ "$TESTDIR/readlink.py" dangling dangling -> nothing + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-archive.t --- a/tests/test-archive.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-archive.t Wed Jul 18 19:08:25 2012 -0500 @@ -20,10 +20,10 @@ > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log > cat hg.pid >> $DAEMON_PIDS > echo % $1 allowed should give 200 - > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$2" | head -n 1 + > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$2" | head -n 1 > echo % $3 and $4 disallowed should both give 403 - > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$3" | head -n 1 - > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$4" | head -n 1 + > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$3" | head -n 1 + > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.$4" | head -n 1 > "$TESTDIR/killdaemons.py" > cat errors.log > cp .hg/hgrc-base .hg/hgrc @@ -56,7 +56,7 @@ invalid arch type should give 404 - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.invalid" | head -n 1 + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "archive/tip.invalid" | head -n 1 404 Unsupported archive type: None $ TIP=`hg id -v | cut -f1 -d' '` @@ -268,3 +268,5 @@ *0*80*00:00*old/old (glob) *-----* (glob) \s*147\s+2 files (re) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-audit-path.t --- a/tests/test-audit-path.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-audit-path.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,28 +1,24 @@ - $ "$TESTDIR/hghave" symlink || exit 80 - $ hg init -should fail +audit of .hg $ hg add .hg/00changelog.i abort: path contains illegal component: .hg/00changelog.i (glob) [255] +#if symlink + +Symlinks + $ mkdir a $ echo a > a/a $ hg ci -Ama adding a/a $ ln -s a b $ echo b > a/b - -should fail - $ hg add b/b abort: path 'b/b' traverses symbolic link 'b' (glob) [255] - -should succeed - $ hg add b should still fail - maybe @@ -31,6 +27,9 @@ abort: path 'b/b' traverses symbolic link 'b' (glob) [255] +#endif + + unbundle tampered bundle $ hg init target @@ -47,7 +46,7 @@ $ hg manifest -r0 .hg/test $ hg update -Cr0 - abort: path contains illegal component: .hg/test + abort: path contains illegal component: .hg/test (glob) [255] attack foo/.hg/test @@ -55,7 +54,7 @@ $ hg manifest -r1 foo/.hg/test $ hg update -Cr1 - abort: path 'foo/.hg/test' is inside nested repo 'foo' + abort: path 'foo/.hg/test' is inside nested repo 'foo' (glob) [255] attack back/test where back symlinks to .. @@ -63,16 +62,23 @@ $ hg manifest -r2 back back/test +#if symlink $ hg update -Cr2 abort: path 'back/test' traverses symbolic link 'back' [255] +#else +('back' will be a file and cause some other system specific error) + $ hg update -Cr2 + abort: * (glob) + [255] +#endif attack ../test $ hg manifest -r3 ../test $ hg update -Cr3 - abort: path contains illegal component: ../test + abort: path contains illegal component: ../test (glob) [255] attack /tmp/test @@ -82,3 +88,5 @@ $ hg update -Cr4 abort: *: $TESTTMP/target//tmp/test (glob) [255] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-backout.t --- a/tests/test-backout.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-backout.t Wed Jul 18 19:08:25 2012 -0500 @@ -117,6 +117,8 @@ line 2 line 3 + $ cd .. + backout should not back out subsequent changesets $ hg init onecs @@ -288,3 +290,5 @@ $ hg st -A C default C file1 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bad-pull.t --- a/tests/test-bad-pull.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bad-pull.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,8 +1,14 @@ $ "$TESTDIR/hghave" serve || exit 80 +#if windows + $ hg clone http://localhost:$HGPORT/ copy + abort: * (glob) + [255] +#else $ hg clone http://localhost:$HGPORT/ copy abort: error: Connection refused [255] +#endif $ test -d copy [1] diff -r 98823bd0d697 -r a06e2681dd17 tests/test-basic.t --- a/tests/test-basic.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-basic.t Wed Jul 18 19:08:25 2012 -0500 @@ -53,3 +53,5 @@ 1 files, 1 changesets, 1 total revisions At the end... + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bheads.t --- a/tests/test-bheads.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bheads.t Wed Jul 18 19:08:25 2012 -0500 @@ -372,3 +372,4 @@ $ hg merge -q 3 $ hg ci -m "b12 (HH): merging two heads" + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bisect.t --- a/tests/test-bisect.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bisect.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" system-sh || exit 80 - $ hg init @@ -224,6 +222,7 @@ Testing changeset 12:1941b52820a5 (23 changesets remaining, ~4 tests) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat .hg/bisect.state + current 1941b52820a544549596820a8ae006842b0e2c64 skip 9d7d07bc967ca98ad0600c24953fd289ad5fa991 skip ce8f0998e922c179e80819d5066fbe46e2998784 skip e7fa0811edb063f6319531f0d0a865882138e180 @@ -396,6 +395,12 @@ date: Thu Jan 01 00:00:06 1970 +0000 summary: msg 6 + $ hg log -r "bisect(current)" + changeset: 5:7874a09ea728 + user: test + date: Thu Jan 01 00:00:05 1970 +0000 + summary: msg 5 + $ hg log -r "bisect(skip)" changeset: 1:5cd978ea5149 user: test @@ -454,15 +459,52 @@ $ hg bisect --bad 0 Testing changeset 15:e7fa0811edb0 (31 changesets remaining, ~4 tests) 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg bisect --command "'`pwd`/script.py' and some parameters" - Changeset 15:e7fa0811edb0: good - Changeset 7:03750880c6b5: good - Changeset 3:b53bea5e2fcb: bad - Changeset 5:7874a09ea728: bad - Changeset 6:a3d5c6fdf0d3: good + $ hg bisect --command "python \"$TESTTMP/script.py\" and some parameters" + changeset 15:e7fa0811edb0: good + changeset 7:03750880c6b5: good + changeset 3:b53bea5e2fcb: bad + changeset 5:7874a09ea728: bad + changeset 6:a3d5c6fdf0d3: good The first good revision is: changeset: 6:a3d5c6fdf0d3 user: test date: Thu Jan 01 00:00:06 1970 +0000 summary: msg 6 + + +test bisecting via a command without updating the working dir, and +ensure that the bisect state file is updated before running a test +command + + $ hg update null + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ cat > script.sh <<'EOF' + > #!/bin/sh + > test -n "$HG_NODE" || (echo HG_NODE missing; exit 127) + > current="`hg log -r \"bisect(current)\" --template {node}`" + > test "$current" = "$HG_NODE" || (echo current is bad: $current; exit 127) + > rev="`hg log -r $HG_NODE --template {rev}`" + > test "$rev" -ge 6 + > EOF + $ chmod +x script.sh + $ hg bisect -r + $ hg bisect --good tip --noupdate + $ hg bisect --bad 0 --noupdate + Testing changeset 15:e7fa0811edb0 (31 changesets remaining, ~4 tests) + $ hg bisect --command "sh \"$TESTTMP/script.sh\" and some params" --noupdate + changeset 15:e7fa0811edb0: good + changeset 7:03750880c6b5: good + changeset 3:b53bea5e2fcb: bad + changeset 5:7874a09ea728: bad + changeset 6:a3d5c6fdf0d3: good + The first good revision is: + changeset: 6:a3d5c6fdf0d3 + user: test + date: Thu Jan 01 00:00:06 1970 +0000 + summary: msg 6 + + +ensure that we still don't have a working dir + + $ hg parents diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bookmarks-current.t --- a/tests/test-bookmarks-current.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bookmarks-current.t Wed Jul 18 19:08:25 2012 -0500 @@ -126,6 +126,23 @@ X 0:719295282060 Z 0:719295282060 +bare update moves the active bookmark forward + + $ echo a > a + $ hg ci -Am1 + adding a + $ hg update X + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg bookmarks + * X 0:719295282060 + Z 0:719295282060 + $ hg update + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + updating bookmark X + $ hg bookmarks + * X 1:cc586d725fbe + Z 0:719295282060 + test deleting .hg/bookmarks.current when explicitly updating to a revision diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bookmarks-merge.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-bookmarks-merge.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,93 @@ +# init + + $ hg init + $ echo a > a + $ hg add a + $ hg commit -m'a' + $ echo b > b + $ hg add b + $ hg commit -m'b' + $ hg up -C 0 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo c > c + $ hg add c + $ hg commit -m'c' + created new head + +# test merging of diverged bookmarks + $ hg bookmark -r 1 "c@diverge" + $ hg bookmark -r 1 b + $ hg bookmark c + $ hg bookmarks + b 1:d2ae7f538514 + * c 2:d36c0562f908 + c@diverge 1:d2ae7f538514 + $ hg merge "c@diverge" + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m'merge' + $ hg bookmarks + b 1:d2ae7f538514 + * c 3:b8f96cf4688b + + $ hg up -C 3 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo d > d + $ hg add d + $ hg commit -m'd' + + $ hg up -C 3 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo e > e + $ hg add e + $ hg commit -m'e' + created new head + $ hg up -C 5 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg bookmark e + $ hg bookmarks + b 1:d2ae7f538514 + c 3:b8f96cf4688b + * e 5:26bee9c5bcf3 + +# the picked side is bookmarked + + $ hg up -C 4 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge + abort: heads are bookmarked - please merge with an explicit rev + (run 'hg heads' to see all heads) + [255] + +# our revision is bookmarked + + $ hg up -C e + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge + abort: no matching bookmark to merge - please merge with an explicit rev or bookmark + (run 'hg heads' to see all heads) + [255] + +# merge bookmark heads + + $ hg up -C 4 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo f > f + $ hg commit -Am "f" + adding f + $ hg up -C e + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg bookmarks -r 4 "e@diverged" + $ hg bookmarks + b 1:d2ae7f538514 + c 3:b8f96cf4688b + * e 5:26bee9c5bcf3 + e@diverged 4:a0546fcfe0fb + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m'merge' + $ hg bookmarks + b 1:d2ae7f538514 + c 3:b8f96cf4688b + * e 7:ca784329f0ba diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bookmarks-pushpull.t --- a/tests/test-bookmarks-pushpull.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bookmarks-pushpull.t Wed Jul 18 19:08:25 2012 -0500 @@ -29,13 +29,18 @@ adding file changes added 1 changesets with 1 changes to 1 files updating bookmark Y + adding remote bookmark X + adding remote bookmark Z (run 'hg update' to get a working copy) $ hg bookmarks + X 0:4e3505fd9583 Y 0:4e3505fd9583 + Z 0:4e3505fd9583 $ hg debugpushkey ../a namespaces bookmarks phases namespaces + obsolete $ hg debugpushkey ../a bookmarks Y 4e3505fd95835d721066b76e75dbb8cc554d7f77 X 4e3505fd95835d721066b76e75dbb8cc554d7f77 @@ -47,6 +52,7 @@ $ hg bookmark X 0:4e3505fd9583 Y 0:4e3505fd9583 + Z 0:4e3505fd9583 export bookmark by name @@ -111,22 +117,25 @@ $ hg book * X 1:9b140be10808 Y 0:4e3505fd9583 + Z 0:4e3505fd9583 foo -1:000000000000 foobar 1:9b140be10808 $ hg pull --config paths.foo=../a foo - pulling from $TESTTMP/a + pulling from $TESTTMP/a (glob) searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) divergent bookmark X stored as X@foo + updating bookmark Z (run 'hg heads' to see heads, 'hg merge' to merge) $ hg book * X 1:9b140be10808 X@foo 2:0d2164f0ce0d Y 0:4e3505fd9583 + Z 2:0d2164f0ce0d foo -1:000000000000 foobar 1:9b140be10808 $ hg push -f ../a @@ -141,6 +150,55 @@ Y 0:4e3505fd9583 Z 1:0d2164f0ce0d +update a remote bookmark from a non-head to a head + + $ hg up -q Y + $ echo c3 > f2 + $ hg ci -Am3 + adding f2 + created new head + $ hg push ../a + pushing to ../a + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + updating bookmark Y + $ hg -R ../a book + * X 1:0d2164f0ce0d + Y 3:f6fc62dde3c0 + Z 1:0d2164f0ce0d + +diverging a remote bookmark fails + + $ hg up -q 4e3505fd9583 + $ echo c4 > f2 + $ hg ci -Am4 + adding f2 + created new head + $ hg book -f Y + + $ cat < ../a/.hg/hgrc + > [web] + > push_ssl = false + > allow_push = * + > EOF + + $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid + $ cat ../hg2.pid >> $DAEMON_PIDS + + $ hg push http://localhost:$HGPORT2/ + pushing to http://localhost:$HGPORT2/ + searching for changes + abort: push creates new remote head 4efff6d98829! + (did you forget to merge? use push -f to force) + [255] + $ hg -R ../a book + * X 1:0d2164f0ce0d + Y 3:f6fc62dde3c0 + Z 1:0d2164f0ce0d + hgweb $ cat < .hg/hgrc @@ -157,15 +215,18 @@ bookmarks phases namespaces + obsolete $ hg debugpushkey http://localhost:$HGPORT/ bookmarks - Y 4e3505fd95835d721066b76e75dbb8cc554d7f77 + Y 4efff6d98829d9c824c621afd6e3f01865f5439f + foobar 9b140be1080824d768c5a4691a564088eede71f9 + Z 0d2164f0ce0d8f1d6f94351eba04b794909be66c + foo 0000000000000000000000000000000000000000 X 9b140be1080824d768c5a4691a564088eede71f9 - foo 0000000000000000000000000000000000000000 - foobar 9b140be1080824d768c5a4691a564088eede71f9 $ hg out -B http://localhost:$HGPORT/ comparing with http://localhost:$HGPORT/ searching for changed bookmarks - Z 0d2164f0ce0d + no changed bookmarks found + [1] $ hg push -B Z http://localhost:$HGPORT/ pushing to http://localhost:$HGPORT/ searching for changes @@ -182,6 +243,9 @@ $ hg pull -B Z http://localhost:$HGPORT/ pulling from http://localhost:$HGPORT/ no changes found + adding remote bookmark foobar + adding remote bookmark Z + adding remote bookmark foo divergent bookmark X stored as X@1 importing bookmark Z $ hg clone http://localhost:$HGPORT/ cloned-bookmarks @@ -189,14 +253,48 @@ adding changesets adding manifests adding file changes - added 3 changesets with 3 changes to 3 files (+1 heads) + added 5 changesets with 5 changes to 3 files (+3 heads) updating to branch default 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R cloned-bookmarks bookmarks X 1:9b140be10808 - Y 0:4e3505fd9583 + Y 4:4efff6d98829 Z 2:0d2164f0ce0d foo -1:000000000000 foobar 1:9b140be10808 + + $ cd .. - $ kill `cat ../hg.pid` +Pushing a bookmark should only push the changes required by that +bookmark, not all outgoing changes: + $ hg clone http://localhost:$HGPORT/ addmarks + requesting all changes + adding changesets + adding manifests + adding file changes + added 5 changesets with 5 changes to 3 files (+3 heads) + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd addmarks + $ echo foo > foo + $ hg add foo + $ hg commit -m 'add foo' + $ echo bar > bar + $ hg add bar + $ hg commit -m 'add bar' + $ hg co "tip^" + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg book add-foo + $ hg book -r tip add-bar +Note: this push *must* push only a single changeset, as that's the point +of this test. + $ hg push -B add-foo + pushing to http://localhost:$HGPORT/ + searching for changes + remote: adding changesets + remote: adding manifests + remote: adding file changes + remote: added 1 changesets with 1 changes to 1 files + exporting bookmark add-foo + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bookmarks-rebase.t --- a/tests/test-bookmarks-rebase.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bookmarks-rebase.t Wed Jul 18 19:08:25 2012 -0500 @@ -39,11 +39,10 @@ saved backup bundle to $TESTTMP/.hg/strip-backup/*-backup.hg (glob) $ hg log - changeset: 3:9163974d1cb5 + changeset: 3:42e5ed2cdcf4 bookmark: two tag: tip parent: 1:925d80f479bb - parent: 2:db815d6d32e6 user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 3 diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bookmarks.t --- a/tests/test-bookmarks.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bookmarks.t Wed Jul 18 19:08:25 2012 -0500 @@ -84,6 +84,20 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: 1 + $ hg log -r 'bookmark("re:X")' + changeset: 0:f7b1eb17ad24 + bookmark: X + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 0 + + changeset: 1:925d80f479bb + bookmark: X2 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1 + $ hg log -r 'bookmark(unknown)' abort: bookmark 'unknown' does not exist [255] diff -r 98823bd0d697 -r a06e2681dd17 tests/test-branch-tag-confict.t --- a/tests/test-branch-tag-confict.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-branch-tag-confict.t Wed Jul 18 19:08:25 2012 -0500 @@ -61,3 +61,5 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: Create a branch with the same name as a tag. + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-branches.t --- a/tests/test-branches.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-branches.t Wed Jul 18 19:08:25 2012 -0500 @@ -241,6 +241,11 @@ default 0:19709c5a4e75 (inactive) $ hg branches -a a branch name much longer than the default justification used by branches 7:10ff5895aa57 + $ hg branches -q + a branch name much longer than the default justification used by branches + c + a + default $ hg heads b no open branch heads found on branches b [1] @@ -403,3 +408,5 @@ \x1b[0;34mc\x1b[0m \x1b[0;36m 14:f894c25619d3\x1b[0m (closed) (esc) \x1b[0;35ma\x1b[0m \x1b[0;36m 5:d8cbc61dbaa6\x1b[0m (inactive) (esc) \x1b[0;35mdefault\x1b[0m \x1b[0;36m 0:19709c5a4e75\x1b[0m (inactive) (esc) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bundle-r.t --- a/tests/test-bundle-r.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bundle-r.t Wed Jul 18 19:08:25 2012 -0500 @@ -324,3 +324,5 @@ crosschecking files in changesets and manifests checking files 4 files, 10 changesets, 7 total revisions + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bundle-vs-outgoing.t --- a/tests/test-bundle-vs-outgoing.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bundle-vs-outgoing.t Wed Jul 18 19:08:25 2012 -0500 @@ -142,3 +142,4 @@ $ hg bundle --base 3 foo.bundle 5 changesets found + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-bundle.t --- a/tests/test-bundle.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-bundle.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" system-sh || exit 80 - Setting up test $ hg init test @@ -207,8 +205,8 @@ Pull ../full.hg into empty (with hook) - $ echo '[hooks]' >> .hg/hgrc - $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc + $ echo "[hooks]" >> .hg/hgrc + $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc doesn't work (yet ?) @@ -221,7 +219,7 @@ adding manifests adding file changes added 9 changesets with 7 changes to 4 files (+1 heads) - changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg + changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg (run 'hg heads' to see heads, 'hg merge' to merge) Rollback empty @@ -244,7 +242,7 @@ adding manifests adding file changes added 9 changesets with 7 changes to 4 files (+1 heads) - changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg + changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg (run 'hg heads' to see heads, 'hg merge' to merge) Create partial clones @@ -386,10 +384,13 @@ Outgoing -R does-not-exist.hg vs partial2 in partial $ hg -R bundle://../does-not-exist.hg outgoing ../partial2 - abort: *: ../does-not-exist.hg (glob) + abort: *../does-not-exist.hg* (glob) [255] $ cd .. +hide outer repo + $ hg init + Direct clone from bundle (all-history) $ hg clone full.hg full-clone @@ -419,7 +420,7 @@ recurse infinitely (issue 2528) $ hg clone full.hg '' - abort: * (glob) + abort: empty destination path is not valid [255] test for http://mercurial.selenic.com/bts/issue216 @@ -598,3 +599,5 @@ crosschecking files in changesets and manifests checking files 4 files, 3 changesets, 5 total revisions + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-casecollision.t --- a/tests/test-casecollision.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-casecollision.t Wed Jul 18 19:08:25 2012 -0500 @@ -31,6 +31,37 @@ $ hg st A A A a + $ mkdir b + $ touch b/c b/D + $ hg add b + adding b/D + adding b/c + $ touch b/d b/C + $ hg add b/C + warning: possible case-folding collision for b/C + $ hg add b/d + warning: possible case-folding collision for b/d + $ touch b/a1 b/a2 + $ hg add b + adding b/a1 + adding b/a2 + $ touch b/A2 b/a1.1 + $ hg add b/a1.1 b/A2 + warning: possible case-folding collision for b/A2 + $ touch b/f b/F + $ hg add b/f b/F + warning: possible case-folding collision for b/f + $ touch g G + $ hg add g G + warning: possible case-folding collision for g + $ mkdir h H + $ touch h/x H/x + $ hg add h/x H/x + warning: possible case-folding collision for h/x + $ touch h/s H/s + $ hg add h/s + $ hg add H/s + warning: possible case-folding collision for H/s case changing rename must not warn or abort diff -r 98823bd0d697 -r a06e2681dd17 tests/test-changelog-exec.t --- a/tests/test-changelog-exec.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-changelog-exec.t Wed Jul 18 19:08:25 2012 -0500 @@ -49,5 +49,7 @@ $ hg debugindex bar - rev offset length base linkrev nodeid p1 p2 - 0 0 5 0 1 b004912a8510 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 5 ..... 1 b004912a8510 000000000000 000000000000 (re) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-check-code-hg.t --- a/tests/test-check-code-hg.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-check-code-hg.t Wed Jul 18 19:08:25 2012 -0500 @@ -8,63 +8,6 @@ $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!' $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true - contrib/check-code.py:0: - > # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=', "don't use underbars in identifiers"), - warning: line over 80 characters - contrib/perf.py:0: - > except: - warning: naked except clause - contrib/perf.py:0: - > #timer(lambda: sum(map(len, repo.dirstate.status(m, [], False, False, False)))) - warning: line over 80 characters - contrib/perf.py:0: - > except: - warning: naked except clause - contrib/setup3k.py:0: - > except: - warning: naked except clause - contrib/setup3k.py:0: - > except: - warning: naked except clause - contrib/setup3k.py:0: - > except: - warning: naked except clause - warning: naked except clause - warning: naked except clause - contrib/shrink-revlog.py:0: - > except: - warning: naked except clause - doc/gendoc.py:0: - > "together with Mercurial. Help for other extensions is available " - warning: line over 80 characters - hgext/bugzilla.py:0: - > raise util.Abort(_('cannot find bugzilla user id for %s or %s') % - warning: line over 80 characters - hgext/bugzilla.py:0: - > bzdir = self.ui.config('bugzilla', 'bzdir', '/var/www/html/bugzilla') - warning: line over 80 characters - hgext/convert/__init__.py:0: - > ('', 'ancestors', '', _('show current changeset in ancestor branches')), - warning: line over 80 characters - hgext/convert/bzr.py:0: - > except: - warning: naked except clause - hgext/convert/common.py:0: - > except: - warning: naked except clause - hgext/convert/common.py:0: - > except: - warning: naked except clause - warning: naked except clause - hgext/convert/convcmd.py:0: - > except: - warning: naked except clause - hgext/convert/cvs.py:0: - > # /1 :pserver:user@example.com:2401/cvsroot/foo Ah assert len(branches) == 1, 'unknown branch: %s' % e.mergepoint - warning: line over 80 characters hgext/convert/cvsps.py:0: > ui.write('Ancestors: %s\n' % (','.join(r))) warning: unwrapped ui message @@ -75,9 +18,6 @@ > ui.write('Parents: %s\n' % warning: unwrapped ui message hgext/convert/cvsps.py:0: - > except: - warning: naked except clause - hgext/convert/cvsps.py:0: > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints)) warning: unwrapped ui message hgext/convert/cvsps.py:0: @@ -101,59 +41,6 @@ hgext/convert/cvsps.py:0: > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1], warning: unwrapped ui message - hgext/convert/git.py:0: - > except: - warning: naked except clause - hgext/convert/git.py:0: - > fh = self.gitopen('git diff-tree --name-only --root -r %s "%s^%s" --' - warning: line over 80 characters - hgext/convert/hg.py:0: - > # detect missing revlogs and abort on errors or populate self.ignored - warning: line over 80 characters - hgext/convert/hg.py:0: - > except: - warning: naked except clause - warning: naked except clause - hgext/convert/hg.py:0: - > except: - warning: naked except clause - hgext/convert/monotone.py:0: - > except: - warning: naked except clause - hgext/convert/monotone.py:0: - > except: - warning: naked except clause - hgext/convert/subversion.py:0: - > raise util.Abort(_('svn: branch has no revision %s') % to_revnum) - warning: line over 80 characters - hgext/convert/subversion.py:0: - > except: - warning: naked except clause - hgext/convert/subversion.py:0: - > args = [self.baseurl, relpaths, start, end, limit, discover_changed_paths, - warning: line over 80 characters - hgext/convert/subversion.py:0: - > self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/') - warning: line over 80 characters - hgext/convert/subversion.py:0: - > except: - warning: naked except clause - hgext/convert/subversion.py:0: - > def get_log_child(fp, url, paths, start, end, limit=0, discover_changed_paths=True, - warning: line over 80 characters - hgext/eol.py:0: - > if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n': - warning: line over 80 characters - warning: line over 80 characters - hgext/gpg.py:0: - > except: - warning: naked except clause - hgext/hgcia.py:0: - > except: - warning: naked except clause - hgext/hgk.py:0: - > ui.write("%s%s\n" % (prefix, description.replace('\n', nlprefix).strip())) - warning: line over 80 characters hgext/hgk.py:0: > ui.write("parent %s\n" % p) warning: unwrapped ui message @@ -173,113 +60,17 @@ > ui.write("revision %d\n" % ctx.rev()) warning: unwrapped ui message hgext/hgk.py:0: - > ui.write("tree %s\n" % short(ctx.changeset()[0])) # use ctx.node() instead ?? - warning: line over 80 characters - warning: unwrapped ui message - hgext/highlight/__init__.py:0: - > extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight) - warning: line over 80 characters - hgext/highlight/__init__.py:0: - > return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')] - warning: line over 80 characters - hgext/inotify/__init__.py:0: - > if self._inotifyon and not ignored and not subrepos and not self._dirty: - warning: line over 80 characters - hgext/inotify/server.py:0: - > except: - warning: naked except clause - hgext/inotify/server.py:0: - > except: - warning: naked except clause - hgext/keyword.py:0: - > ui.note("hg ci -m '%s'\n" % msg) + > ui.write("tree %s\n" % short(ctx.changeset()[0])) warning: unwrapped ui message hgext/mq.py:0: - > raise util.Abort(_("cannot push --exact with applied patches")) - warning: line over 80 characters - hgext/mq.py:0: - > raise util.Abort(_("cannot use --exact and --move together")) - warning: line over 80 characters - hgext/mq.py:0: - > self.ui.warn(_('Tag %s overrides mq patch of the same name\n') - warning: line over 80 characters - hgext/mq.py:0: - > except: - warning: naked except clause - warning: naked except clause - hgext/mq.py:0: - > except: - warning: naked except clause - warning: naked except clause - warning: naked except clause - warning: naked except clause - hgext/mq.py:0: - > raise util.Abort(_('cannot mix -l/--list with options or arguments')) - warning: line over 80 characters - hgext/mq.py:0: - > raise util.Abort(_('qfold cannot fold already applied patch %s') % p) - warning: line over 80 characters - hgext/mq.py:0: - > ('U', 'noupdate', None, _('do not update the new working directories')), - warning: line over 80 characters - hgext/mq.py:0: - > ('e', 'exact', None, _('apply the target patch to its recorded parent')), - warning: line over 80 characters - hgext/mq.py:0: - > except: - warning: naked except clause - hgext/mq.py:0: > ui.write("mq: %s\n" % ', '.join(m)) warning: unwrapped ui message - hgext/mq.py:0: - > repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary')) - warning: line over 80 characters - hgext/notify.py:0: - > ui.note(_('notify: suppressing notification for merge %d:%s\n') % - warning: line over 80 characters - hgext/patchbomb.py:0: - > binnode, seqno=idx, total=total) - warning: line over 80 characters - hgext/patchbomb.py:0: - > except: - warning: naked except clause hgext/patchbomb.py:0: > ui.write('Subject: %s\n' % subj) warning: unwrapped ui message hgext/patchbomb.py:0: - > p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test')) - warning: line over 80 characters - hgext/patchbomb.py:0: > ui.write('From: %s\n' % sender) warning: unwrapped ui message - hgext/record.py:0: - > ignoreblanklines=opts.get('ignore_blank_lines')) - warning: line over 80 characters - hgext/record.py:0: - > ignorewsamount=opts.get('ignore_space_change'), - warning: line over 80 characters - hgext/zeroconf/__init__.py:0: - > publish(name, desc, path, util.getport(u.config("web", "port", 8000))) - warning: line over 80 characters - hgext/zeroconf/__init__.py:0: - > except: - warning: naked except clause - warning: naked except clause - mercurial/bundlerepo.py:0: - > is a bundlerepo for the obtained bundle when the original "other" is remote. - warning: line over 80 characters - mercurial/bundlerepo.py:0: - > "local" is a local repo from which to obtain the actual incoming changesets; it - warning: line over 80 characters - mercurial/bundlerepo.py:0: - > tmp = discovery.findcommonincoming(repo, other, heads=onlyheads, force=force) - warning: line over 80 characters - mercurial/commands.py:0: - > " size " + basehdr + " link p1 p2 nodeid\n") - warning: line over 80 characters - mercurial/commands.py:0: - > raise util.Abort('cannot use localheads with old style discovery') - warning: line over 80 characters mercurial/commands.py:0: > ui.note('branch %s\n' % data) warning: unwrapped ui message @@ -293,18 +84,6 @@ > ui.write("unpruned common: %s\n" % " ".join([short(n) warning: unwrapped ui message mercurial/commands.py:0: - > yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1))) - warning: line over 80 characters - mercurial/commands.py:0: - > yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1))) - warning: line over 80 characters - mercurial/commands.py:0: - > except: - warning: naked except clause - mercurial/commands.py:0: - > ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n")) - warning: line over 80 characters - mercurial/commands.py:0: > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n") warning: unwrapped ui message mercurial/commands.py:0: @@ -314,17 +93,7 @@ > ui.write("remote is subset\n") warning: unwrapped ui message mercurial/commands.py:0: - > ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev)) - warning: line over 80 characters - mercurial/commands.py:0: - > ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev)) - warning: line over 80 characters - mercurial/commands.py:0: - > ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev)) - warning: line over 80 characters - mercurial/commands.py:0: - > ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas)) - warning: line over 80 characters + > ui.write('deltas against other : ' + fmt % pcfmt(numother, warning: unwrapped ui message mercurial/commands.py:0: > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)) @@ -333,12 +102,6 @@ > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)) warning: unwrapped ui message mercurial/commands.py:0: - > except: - warning: naked except clause - mercurial/commands.py:0: - > revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev')) - warning: line over 80 characters - mercurial/commands.py:0: > ui.write("common heads: %s\n" % " ".join([short(n) for n in common])) warning: unwrapped ui message mercurial/commands.py:0: @@ -354,12 +117,6 @@ > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n' warning: unwrapped ui message mercurial/commands.py:0: - > Every ID must be a full-length hex node id string. Returns a list of 0s and 1s - warning: line over 80 characters - mercurial/commands.py:0: - > remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch')) - warning: line over 80 characters - mercurial/commands.py:0: > ui.write("digraph G {\n") warning: unwrapped ui message mercurial/commands.py:0: @@ -402,226 +159,25 @@ mercurial/commands.py:0: > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) warning: unwrapped ui message - mercurial/commandserver.py:0: - > # the ui here is really the repo ui so take its baseui so we don't end up - warning: line over 80 characters - mercurial/context.py:0: - > return self._manifestdelta[path], self._manifestdelta.flags(path) - warning: line over 80 characters - mercurial/dagparser.py:0: - > raise util.Abort(_("invalid character in dag description: %s...") % s) - warning: line over 80 characters - mercurial/dagparser.py:0: - > >>> dagtext([('n', (0, [-1])), ('C', 'my command line'), ('n', (1, [0]))]) - warning: line over 80 characters - mercurial/dirstate.py:0: - > if not st is None and not getkind(st.st_mode) in (regkind, lnkkind): - warning: line over 80 characters - mercurial/discovery.py:0: - > If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive) - warning: line over 80 characters - mercurial/dispatch.py:0: - > " (.hg not found)") % os.getcwd()) - warning: line over 80 characters - mercurial/dispatch.py:0: - > except: - warning: naked except clause - mercurial/dispatch.py:0: - > return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {}) - warning: line over 80 characters - mercurial/dispatch.py:0: - > def __init__(self, args, ui=None, repo=None, fin=None, fout=None, ferr=None): - warning: line over 80 characters - mercurial/dispatch.py:0: - > except: - warning: naked except clause - mercurial/hg.py:0: - > except: - warning: naked except clause - mercurial/hgweb/hgweb_mod.py:0: - > self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) - warning: line over 80 characters - mercurial/keepalive.py:0: - > except: - warning: naked except clause - mercurial/keepalive.py:0: - > except: - warning: naked except clause - mercurial/localrepo.py:0: - > # we return an integer indicating remote head count change - warning: line over 80 characters - mercurial/localrepo.py:0: - > raise util.Abort(_("empty or missing revlog for %s") % fname) - warning: line over 80 characters - warning: line over 80 characters - mercurial/localrepo.py:0: - > if self._tagscache.tagtypes and name in self._tagscache.tagtypes: - warning: line over 80 characters - mercurial/localrepo.py:0: - > self.hook("precommit", throw=True, parent1=hookp1, parent2=hookp2) - warning: line over 80 characters - mercurial/localrepo.py:0: - > # new requirements = old non-format requirements + new format-related - warning: line over 80 characters - mercurial/localrepo.py:0: - > except: - warning: naked except clause - mercurial/localrepo.py:0: - > """return status of files between two nodes or node and working directory - warning: line over 80 characters - mercurial/localrepo.py:0: - > '''Returns a tagscache object that contains various tags related caches.''' - warning: line over 80 characters - mercurial/manifest.py:0: - > return "".join(struct.pack(">lll", start, end, len(content)) + content - warning: line over 80 characters - mercurial/merge.py:0: - > subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite) - warning: line over 80 characters - mercurial/patch.py:0: - > modified, added, removed, copy, getfilectx, opts, losedata, prefix) - warning: line over 80 characters - mercurial/patch.py:0: - > diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b) - warning: line over 80 characters - mercurial/patch.py:0: - > output.append(_(' %d files changed, %d insertions(+), %d deletions(-)\n') - warning: line over 80 characters - mercurial/patch.py:0: - > except: - warning: naked except clause - mercurial/pure/mpatch.py:0: - > frags.extend(reversed(new)) # what was left at the end - warning: line over 80 characters - mercurial/repair.py:0: - > except: - warning: naked except clause - mercurial/repair.py:0: - > except: - warning: naked except clause - mercurial/revset.py:0: - > elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword - warning: line over 80 characters - mercurial/revset.py:0: - > Changesets that are the Nth ancestor (first parents only) of a changeset in set. - warning: line over 80 characters - mercurial/scmutil.py:0: - > raise util.Abort(_("path '%s' is inside nested repo %r") % - warning: line over 80 characters - mercurial/scmutil.py:0: - > "requires features '%s' (upgrade Mercurial)") % "', '".join(missings)) - warning: line over 80 characters - mercurial/scmutil.py:0: - > elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target) - warning: line over 80 characters - mercurial/setdiscovery.py:0: - > # treat remote heads (and maybe own heads) as a first implicit sample response - warning: line over 80 characters - mercurial/setdiscovery.py:0: - > undecided = dag.nodeset() # own nodes where I don't know if remote knows them - warning: line over 80 characters - mercurial/similar.py:0: - > repo.ui.progress(_('searching for similar files'), i, total=len(removed)) - warning: line over 80 characters - mercurial/simplemerge.py:0: - > for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions(): - warning: line over 80 characters - mercurial/sshrepo.py:0: - > self._abort(error.RepoError(_("no suitable response from remote hg"))) - warning: line over 80 characters - mercurial/sshrepo.py:0: - > except: - warning: naked except clause - mercurial/subrepo.py:0: - > other, self._repo = hg.clone(self._repo._subparent.ui, {}, other, - warning: line over 80 characters - mercurial/subrepo.py:0: - > msg = (_(' subrepository sources for %s differ (in checked out version)\n' - warning: line over 80 characters - mercurial/transaction.py:0: - > except: - warning: naked except clause - mercurial/ui.py:0: - > traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr) - warning: line over 80 characters - mercurial/url.py:0: - > conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs) - warning: line over 80 characters - mercurial/util.py:0: - > except: - warning: naked except clause - mercurial/util.py:0: - > except: - warning: naked except clause - mercurial/verify.py:0: - > except: - warning: naked except clause - mercurial/verify.py:0: - > except: - warning: naked except clause - mercurial/wireproto.py:0: - > # Assuming the future to be filled with the result from the batched request - warning: line over 80 characters - mercurial/wireproto.py:0: - > '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)''' - warning: line over 80 characters - mercurial/wireproto.py:0: - > All methods invoked on instances of this class are simply queued and return a - warning: line over 80 characters - mercurial/wireproto.py:0: - > The decorator returns a function which wraps this coroutine as a plain method, - warning: line over 80 characters - setup.py:0: - > raise SystemExit("Python headers are required to build Mercurial") - warning: line over 80 characters - setup.py:0: - > except: - warning: naked except clause - setup.py:0: - > # build_py), it will not find osutil & friends, thinking that those modules are - warning: line over 80 characters - setup.py:0: - > except: - warning: naked except clause - warning: naked except clause - setup.py:0: - > isironpython = platform.python_implementation().lower().find("ironpython") != -1 - warning: line over 80 characters - setup.py:0: - > except: - warning: naked except clause - warning: naked except clause - warning: naked except clause tests/autodiff.py:0: > ui.write('data lost for: %s\n' % fn) warning: unwrapped ui message - tests/run-tests.py:0: - > except: - warning: naked except clause - tests/test-commandserver.py:0: - > 'hooks.pre-identify=python:test-commandserver.hook', 'id'], - warning: line over 80 characters - tests/test-commandserver.py:0: - > # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it - warning: line over 80 characters - tests/test-commandserver.py:0: - > print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data)) - warning: line over 80 characters - tests/test-filecache.py:0: - > except: - warning: naked except clause - tests/test-filecache.py:0: - > if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']): - warning: line over 80 characters + tests/test-convert-mtn.t:0: + > > function get_passphrase(keypair_id) + don't use 'function', use old style + tests/test-import-git.t:0: + > > Mc\${NkU|\`?^000jF3jhEB + ^ must be quoted + tests/test-import.t:0: + > > diff -Naur proj-orig/foo proj-new/foo + don't use 'diff -N' + don't use 'diff -N' + tests/test-schemes.t:0: + > > z = file:\$PWD/ + don't use $PWD, use `pwd` tests/test-ui-color.py:0: > testui.warn('warning\n') warning: unwrapped ui message tests/test-ui-color.py:0: > testui.write('buffered\n') warning: unwrapped ui message - tests/test-walkrepo.py:0: - > print "Found %d repositories when I should have found 2" % (len(reposet),) - warning: line over 80 characters - tests/test-walkrepo.py:0: - > print "Found %d repositories when I should have found 3" % (len(reposet),) - warning: line over 80 characters diff -r 98823bd0d697 -r a06e2681dd17 tests/test-children.t --- a/tests/test-children.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-children.t Wed Jul 18 19:08:25 2012 -0500 @@ -121,3 +121,5 @@ date: Thu Jan 01 00:00:02 1970 +0000 summary: 2 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-churn.t --- a/tests/test-churn.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-churn.t Wed Jul 18 19:08:25 2012 -0500 @@ -158,3 +158,5 @@ user4@x.com 2 ***************************** user2 2 ***************************** with space 1 ************** + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-clone-failure.t --- a/tests/test-clone-failure.t Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - -No local source - - $ hg clone a b - abort: repository a not found! - [255] - -No remote source - - $ hg clone http://127.0.0.1:3121/a b - abort: error: Connection refused - [255] - $ rm -rf b # work around bug with http clone - -Inaccessible source - - $ mkdir a - $ chmod 000 a - $ hg clone a b - abort: repository a not found! - [255] - -Inaccessible destination - - $ hg init b - $ cd b - $ hg clone . ../a - abort: Permission denied: ../a - [255] - $ cd .. - $ chmod 700 a - $ rm -r a b - -Source of wrong type - - $ if "$TESTDIR/hghave" -q fifo; then - > mkfifo a - > hg clone a b - > rm a - > else - > echo "abort: repository a not found!" - > fi - abort: repository a not found! - -Default destination, same directory - - $ hg init q - $ hg clone q - destination directory: q - abort: destination 'q' is not empty - [255] - -destination directory not empty - - $ mkdir a - $ echo stuff > a/a - $ hg clone q a - abort: destination 'a' is not empty - [255] - -leave existing directory in place after clone failure - - $ hg init c - $ cd c - $ echo c > c - $ hg commit -A -m test - adding c - $ chmod -rx .hg/store/data - $ cd .. - $ mkdir d - $ hg clone c d 2> err - [255] - $ test -d d - $ test -d d/.hg - [1] - -reenable perm to allow deletion - - $ chmod +rx c/.hg/store/data diff -r 98823bd0d697 -r a06e2681dd17 tests/test-clone-pull-corruption.t --- a/tests/test-clone-pull-corruption.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-clone-pull-corruption.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" system-sh || exit 80 - Corrupt an hg repo with a pull started during an aborted commit Create two repos, so that one of them can pull from the other one. @@ -17,8 +15,8 @@ Add a hook to wait 5 seconds and then abort the commit $ cd ../corrupted - $ echo '[hooks]' >> .hg/hgrc - $ echo 'pretxncommit = sleep 5; exit 1' >> .hg/hgrc + $ echo "[hooks]" >> .hg/hgrc + $ echo "pretxncommit = sh -c 'sleep 5; exit 1'" >> .hg/hgrc start a commit... @@ -50,3 +48,5 @@ crosschecking files in changesets and manifests checking files 1 files, 2 changesets, 2 total revisions + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-clone-r.t --- a/tests/test-clone-r.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-clone-r.t Wed Jul 18 19:08:25 2012 -0500 @@ -38,33 +38,33 @@ $ hg commit -m "0.3m" $ hg debugindex -f 1 afile - rev flag offset length size base link p1 p2 nodeid - 0 0000 0 3 2 0 0 -1 -1 362fef284ce2 - 1 0000 3 5 4 1 1 0 -1 125144f7e028 - 2 0000 8 7 6 2 2 1 -1 4c982badb186 - 3 0000 15 9 8 3 3 2 -1 19b1fc555737 + rev flag offset length size ..... link p1 p2 nodeid (re) + 0 0000 0 3 2 ..... 0 -1 -1 362fef284ce2 (re) + 1 0000 3 5 4 ..... 1 0 -1 125144f7e028 (re) + 2 0000 8 7 6 ..... 2 1 -1 4c982badb186 (re) + 3 0000 15 9 8 ..... 3 2 -1 19b1fc555737 (re) $ hg debugindex adifferentfile - rev offset length base linkrev nodeid p1 p2 - 0 0 75 0 7 2565f3199a74 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 75 ..... 7 2565f3199a74 000000000000 000000000000 (re) $ hg debugindex anotherfile - rev offset length base linkrev nodeid p1 p2 - 0 0 75 0 8 2565f3199a74 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 75 ..... 8 2565f3199a74 000000000000 000000000000 (re) $ hg debugindex fred - rev offset length base linkrev nodeid p1 p2 - 0 0 8 0 6 12ab3bcc5ea4 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 8 ..... 6 12ab3bcc5ea4 000000000000 000000000000 (re) $ hg debugindex --manifest - rev offset length base linkrev nodeid p1 p2 - 0 0 48 0 0 43eadb1d2d06 000000000000 000000000000 - 1 48 48 1 1 8b89697eba2c 43eadb1d2d06 000000000000 - 2 96 48 2 2 626a32663c2f 8b89697eba2c 000000000000 - 3 144 48 3 3 f54c32f13478 626a32663c2f 000000000000 - 4 192 58 3 6 de68e904d169 626a32663c2f 000000000000 - 5 250 68 3 7 09bb521d218d de68e904d169 000000000000 - 6 318 54 6 8 1fde233dfb0f f54c32f13478 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 48 ..... 0 43eadb1d2d06 000000000000 000000000000 (re) + 1 48 48 ..... 1 8b89697eba2c 43eadb1d2d06 000000000000 (re) + 2 96 48 ..... 2 626a32663c2f 8b89697eba2c 000000000000 (re) + 3 144 48 ..... 3 f54c32f13478 626a32663c2f 000000000000 (re) + 4 192 .. ..... 6 de68e904d169 626a32663c2f 000000000000 (re) + 5 2.. 68 ..... 7 09bb521d218d de68e904d169 000000000000 (re) + 6 3.. 54 ..... 8 1fde233dfb0f f54c32f13478 000000000000 (re) $ hg verify checking changesets diff -r 98823bd0d697 -r a06e2681dd17 tests/test-clone.t --- a/tests/test-clone.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-clone.t Wed Jul 18 19:08:25 2012 -0500 @@ -43,13 +43,18 @@ Invalid dest '' must abort: $ hg clone . '' - abort: * (glob) + abort: empty destination path is not valid [255] No update, with debug option: +#if hardlink $ hg --debug clone -U . ../c linked 8 files +#else + $ hg --debug clone -U . ../c + copied 8 files +#endif $ cd ../c $ cat a 2>/dev/null || echo "a not present" a not present @@ -107,7 +112,7 @@ Invalid dest '' with --pull must abort (issue2528): $ hg clone --pull a '' - abort: * (glob) + abort: empty destination path is not valid [255] Clone to '.': @@ -444,8 +449,9 @@ $ rm -r ua $ cat < branchclone.py - > from mercurial import ui, hg + > from mercurial import ui, hg, extensions > myui = ui.ui() + > extensions.loadall(myui) > repo = hg.repository(myui, 'a') > hg.clone(myui, {}, repo, dest="ua", branch=["stable",]) > EOF @@ -458,3 +464,102 @@ updating to branch stable 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ rm -r ua + + +Testing failures: + + $ mkdir fail + $ cd fail + +No local source + + $ hg clone a b + abort: repository a not found! + [255] + +No remote source + + $ hg clone http://127.0.0.1:3121/a b + abort: error: *refused* (glob) + [255] + $ rm -rf b # work around bug with http clone + + +#if unix-permissions + +Inaccessible source + + $ mkdir a + $ chmod 000 a + $ hg clone a b + abort: repository a not found! + [255] + +Inaccessible destination + + $ hg init b + $ cd b + $ hg clone . ../a + abort: Permission denied: ../a + [255] + $ cd .. + $ chmod 700 a + $ rm -r a b + +#endif + + +#if fifo + +Source of wrong type + + $ mkfifo a + $ hg clone a b + abort: repository a not found! + [255] + $ rm a + +#endif + +Default destination, same directory + + $ hg init q + $ hg clone q + destination directory: q + abort: destination 'q' is not empty + [255] + +destination directory not empty + + $ mkdir a + $ echo stuff > a/a + $ hg clone q a + abort: destination 'a' is not empty + [255] + + +#if unix-permissions + +leave existing directory in place after clone failure + + $ hg init c + $ cd c + $ echo c > c + $ hg commit -A -m test + adding c + $ chmod -rx .hg/store/data + $ cd .. + $ mkdir d + $ hg clone c d 2> err + [255] + $ test -d d + $ test -d d/.hg + [1] + +reenable perm to allow deletion + + $ chmod +rx c/.hg/store/data + +#endif + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-command-template.t --- a/tests/test-command-template.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-command-template.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - $ hg init a $ cd a $ echo a > a @@ -444,11 +442,13 @@ Error if style not readable: +#if unix-permissions $ touch q $ chmod 0 q $ hg log --style ./q abort: Permission denied: ./q [255] +#endif Error if no style: @@ -466,13 +466,15 @@ Error if include fails: $ echo 'changeset = q' >> t +#if unix-permissions $ hg log --style ./t abort: template file ./q: Permission denied [255] + $ rm q +#endif Include works: - $ rm q $ echo '{rev}' > q $ hg log --style ./t 8 diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commandserver.py --- a/tests/test-commandserver.py Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-commandserver.py Wed Jul 18 19:08:25 2012 -0500 @@ -18,7 +18,7 @@ def readchannel(server): data = server.stdout.read(5) if not data: - raise EOFError() + raise EOFError channel, length = struct.unpack('>cI', data) if channel in 'IL': return channel, length @@ -71,7 +71,8 @@ def hellomessage(server): ch, data = readchannel(server) # escaping python tests output not supported - print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', data)) + print '%c, %r' % (ch, re.sub('encoding: [a-zA-Z0-9-]+', 'encoding: ***', + data)) # run an arbitrary command to make sure the next thing the server sends # isn't part of the hello message @@ -142,7 +143,8 @@ is used """ readchannel(server) - # the cached repo local hgrc contains ui.foo=bar, so showconfig should show it + # the cached repo local hgrc contains ui.foo=bar, so showconfig should + # show it runcommand(server, ['showconfig']) # but not for this repo @@ -157,7 +159,8 @@ def hookoutput(server): readchannel(server) runcommand(server, ['--config', - 'hooks.pre-identify=python:test-commandserver.hook', 'id'], + 'hooks.pre-identify=python:test-commandserver.hook', + 'id'], input=cStringIO.StringIO('some input')) def outsidechanges(server): diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commandserver.py.out --- a/tests/test-commandserver.py.out Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-commandserver.py.out Wed Jul 18 19:08:25 2012 -0500 @@ -16,24 +16,24 @@ 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 runcommand id --quiet diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commit-amend.t --- a/tests/test-commit-amend.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-commit-amend.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" execbit || exit 80 - $ hg init Setup: @@ -25,8 +23,10 @@ nothing changed [1] - $ echo '[hooks]' >> $HGRCPATH - $ echo 'pretxncommit.foo = echo "pretxncommit $HG_NODE"; hg id -r $HG_NODE' >> $HGRCPATH + $ cat >> $HGRCPATH < [hooks] + > pretxncommit.foo = sh -c "echo \"pretxncommit \$HG_NODE\"; hg id -r \$HG_NODE" + > EOF Amending changeset with changes in working dir: @@ -34,7 +34,7 @@ $ hg ci --amend -m 'amend base1' pretxncommit 9cd25b479c51be2f4ed2c38e7abdf7ce67d8e0dc 9cd25b479c51 tip - saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob) $ echo 'pretxncommit.foo = ' >> $HGRCPATH $ hg diff -c . diff -r ad120869acf0 -r 9cd25b479c51 a @@ -62,13 +62,13 @@ $ echo b > b $ hg ci --amend -Am 'amend base1 new file' adding b - saved backup bundle to $TESTTMP/.hg/strip-backup/9cd25b479c51-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/9cd25b479c51-amend-backup.hg (glob) Remove file that was added in amended commit: $ hg rm b $ hg ci --amend -m 'amend base1 remove new file' - saved backup bundle to $TESTTMP/.hg/strip-backup/e2bb3ecffd2f-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/e2bb3ecffd2f-amend-backup.hg (glob) $ hg cat b b: no such file in rev 664a9b2d60cd @@ -82,7 +82,7 @@ a stripping amended changeset 664a9b2d60cd 1 changesets found - saved backup bundle to $TESTTMP/.hg/strip-backup/664a9b2d60cd-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/664a9b2d60cd-amend-backup.hg (glob) 1 changesets found adding branch adding changesets @@ -119,10 +119,10 @@ Test -u/-d: $ hg ci --amend -u foo -d '1 0' - saved backup bundle to $TESTTMP/.hg/strip-backup/ea6e356ff2ad-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/ea6e356ff2ad-amend-backup.hg (glob) $ echo a >> a $ hg ci --amend -u foo -d '1 0' - saved backup bundle to $TESTTMP/.hg/strip-backup/377b91ce8b56-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/377b91ce8b56-amend-backup.hg (glob) $ hg log -r . changeset: 1:2c94e4a5756f tag: tip @@ -133,13 +133,12 @@ Open editor with old commit message if a message isn't given otherwise: - $ cat > editor << '__EOF__' + $ cat > editor.sh << '__EOF__' > #!/bin/sh > cat $1 > echo "another precious commit message" > "$1" > __EOF__ - $ chmod +x editor - $ HGEDITOR="'`pwd`'"/editor hg commit --amend -v + $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v amending changeset 2c94e4a5756f copying changeset 2c94e4a5756f to ad120869acf0 no changes, new message @@ -154,7 +153,7 @@ a stripping amended changeset 2c94e4a5756f 1 changesets found - saved backup bundle to $TESTTMP/.hg/strip-backup/2c94e4a5756f-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/2c94e4a5756f-amend-backup.hg (glob) 1 changesets found adding branch adding changesets @@ -166,7 +165,7 @@ Same, but with changes in working dir (different code path): $ echo a >> a - $ HGEDITOR="'`pwd`'"/editor hg commit --amend -v + $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v amending changeset ffb49186f961 another precious commit message @@ -183,7 +182,7 @@ stripping intermediate changeset 27f3aacd3011 stripping amended changeset ffb49186f961 2 changesets found - saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg (glob) 1 changesets found adding branch adding changesets @@ -192,7 +191,7 @@ added 1 changesets with 1 changes to 1 files committed changeset 1:fb6cca43446f - $ rm editor + $ rm editor.sh $ hg log -r . changeset: 1:fb6cca43446f tag: tip @@ -206,13 +205,13 @@ $ hg book book1 $ hg book book2 $ hg ci --amend -m 'move bookmarks' - saved backup bundle to $TESTTMP/.hg/strip-backup/fb6cca43446f-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/fb6cca43446f-amend-backup.hg (glob) $ hg book book1 1:0cf1c7a51bcf * book2 1:0cf1c7a51bcf $ echo a >> a $ hg ci --amend -m 'move bookmarks' - saved backup bundle to $TESTTMP/.hg/strip-backup/0cf1c7a51bcf-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/0cf1c7a51bcf-amend-backup.hg (glob) $ hg book book1 1:7344472bd951 * book2 1:7344472bd951 @@ -231,7 +230,7 @@ marked working directory as branch default (branches are permanent and global, did you want a bookmark?) $ hg ci --amend -m 'back to default' - saved backup bundle to $TESTTMP/.hg/strip-backup/1661ca36a2db-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/1661ca36a2db-amend-backup.hg (glob) $ hg branches default 2:f24ee5961967 @@ -247,7 +246,7 @@ $ echo b >> b $ hg ci -mb $ hg ci --amend --close-branch -m 'closing branch foo' - saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob) Same thing, different code path: @@ -256,7 +255,7 @@ reopening closed branch head 4 $ echo b >> b $ hg ci --amend --close-branch - saved backup bundle to $TESTTMP/.hg/strip-backup/5e302dcc12b8-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/5e302dcc12b8-amend-backup.hg (glob) $ hg branches default 2:f24ee5961967 @@ -280,7 +279,7 @@ $ hg ci -m 'b -> c' $ hg mv c d $ hg ci --amend -m 'b -> d' - saved backup bundle to $TESTTMP/.hg/strip-backup/9c207120aa98-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/9c207120aa98-amend-backup.hg (glob) $ hg st --rev '.^' --copies d A d b @@ -288,7 +287,7 @@ $ hg ci -m 'e = d' $ hg cp e f $ hg ci --amend -m 'f = d' - saved backup bundle to $TESTTMP/.hg/strip-backup/fda2b3b27b22-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/fda2b3b27b22-amend-backup.hg (glob) $ hg st --rev '.^' --copies f A f d @@ -299,7 +298,7 @@ $ hg cp a f $ mv f.orig f $ hg ci --amend -m replacef - saved backup bundle to $TESTTMP/.hg/strip-backup/20a7413547f9-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/20a7413547f9-amend-backup.hg (glob) $ hg st --change . --copies $ hg log -r . --template "{file_copies}\n" @@ -311,7 +310,7 @@ adding g $ hg mv g h $ hg ci --amend - saved backup bundle to $TESTTMP/.hg/strip-backup/5daa77a5d616-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/5daa77a5d616-amend-backup.hg (glob) $ hg st --change . --copies h A h $ hg log -r . --template "{file_copies}\n" @@ -331,11 +330,11 @@ $ echo a >> a $ hg ci -ma $ hg ci --amend -m "a'" - saved backup bundle to $TESTTMP/.hg/strip-backup/167f8e3031df-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/167f8e3031df-amend-backup.hg (glob) $ hg log -r . --template "{branch}\n" a $ hg ci --amend -m "a''" - saved backup bundle to $TESTTMP/.hg/strip-backup/ceac1a44c806-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/ceac1a44c806-amend-backup.hg (glob) $ hg log -r . --template "{branch}\n" a @@ -352,7 +351,7 @@ $ hg graft 12 grafting revision 12 $ hg ci --amend -m 'graft amend' - saved backup bundle to $TESTTMP/.hg/strip-backup/18a5124daf7a-amend-backup.hg + saved backup bundle to $TESTTMP/.hg/strip-backup/18a5124daf7a-amend-backup.hg (glob) $ hg log -r . --debug | grep extra extra: branch=a extra: source=2647734878ef0236dda712fae9c1651cf694ea8a diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commit-copy.t --- a/tests/test-commit-copy.t Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ - $ hg init dir - $ cd dir - $ echo bleh > bar - $ hg add bar - $ hg ci -m 'add bar' - - $ hg cp bar foo - $ echo >> bar - $ hg ci -m 'cp bar foo; change bar' - - $ hg debugrename foo - foo renamed from bar:26d3ca0dfd18e44d796b564e38dd173c9668d3a9 - $ hg debugindex bar - rev offset length base linkrev nodeid p1 p2 - 0 0 6 0 0 26d3ca0dfd18 000000000000 000000000000 - 1 6 7 1 1 d267bddd54f7 26d3ca0dfd18 000000000000 diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commit-multiple.t --- a/tests/test-commit-multiple.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-commit-multiple.t Wed Jul 18 19:08:25 2012 -0500 @@ -129,3 +129,5 @@ 5 fix 2 bugfix file1 6 x bugfix file1 7 y file1 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commit-unresolved.t --- a/tests/test-commit-unresolved.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-commit-unresolved.t Wed Jul 18 19:08:25 2012 -0500 @@ -45,3 +45,5 @@ $ hg resolve -m A $ hg commit -m "Merged" + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-commit.t --- a/tests/test-commit.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-commit.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" symlink || exit 80 - commit date test $ hg init test @@ -75,10 +73,14 @@ $ hg commit -m commit-14 does-not-exist abort: does-not-exist: * (glob) [255] + +#if symlink $ ln -s foo baz $ hg commit -m commit-15 baz abort: baz: file not tracked! [255] +#endif + $ touch quux $ hg commit -m commit-16 quux abort: quux: file not tracked! @@ -280,3 +282,26 @@ HG: removed removed abort: empty commit message [255] + $ cd .. + + +commit copy + + $ hg init dir2 + $ cd dir2 + $ echo bleh > bar + $ hg add bar + $ hg ci -m 'add bar' + + $ hg cp bar foo + $ echo >> bar + $ hg ci -m 'cp bar foo; change bar' + + $ hg debugrename foo + foo renamed from bar:26d3ca0dfd18e44d796b564e38dd173c9668d3a9 + $ hg debugindex bar + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 6 ..... 0 26d3ca0dfd18 000000000000 000000000000 (re) + 1 6 7 ..... 1 d267bddd54f7 26d3ca0dfd18 000000000000 (re) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-committer.t --- a/tests/test-committer.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-committer.t Wed Jul 18 19:08:25 2012 -0500 @@ -53,7 +53,7 @@ [255] $ rm .hg/hgrc $ hg commit -m commit-1 2>&1 - No username found, using '[^']*' instead (re) + no username found, using '[^']*' instead (re) $ echo space > asdf $ hg commit -u ' ' -m commit-1 @@ -61,3 +61,5 @@ rollback completed abort: empty username! [255] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-config-case.t --- a/tests/test-config-case.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-config-case.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,3 +1,6 @@ +hide outer repo + $ hg init + $ echo '[Section]' >> $HGRCPATH $ echo 'KeY = Case Sensitive' >> $HGRCPATH $ echo 'key = lower case' >> $HGRCPATH diff -r 98823bd0d697 -r a06e2681dd17 tests/test-contrib.t --- a/tests/test-contrib.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-contrib.t Wed Jul 18 19:08:25 2012 -0500 @@ -104,6 +104,8 @@ [1] +#if hardlink + Test shrink-revlog: $ cd repo-a $ hg --config extensions.shrink="$CONTRIBDIR/shrink-revlog.py" shrink @@ -127,6 +129,8 @@ 1 files, 3 changesets, 3 total revisions $ cd .. +#endif + Test simplemerge command: $ cp "$CONTRIBDIR/simplemerge" . diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-authormap.t --- a/tests/test-convert-authormap.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-authormap.t Wed Jul 18 19:08:25 2012 -0500 @@ -22,12 +22,12 @@ > EOF $ hg convert --authors authormap.txt orig new initializing destination new repository - Ignoring bad line in author map file authormap.txt: this line is ignored + ignoring bad line in author map file authormap.txt: this line is ignored scanning source... sorting... converting... 0 foo - Writing author map file $TESTTMP/new/.hg/authormap (glob) + writing author map file $TESTTMP/new/.hg/authormap (glob) $ cat new/.hg/authormap user name=Long User Name $ hg -Rnew log @@ -44,7 +44,7 @@ $ hg init new $ mv authormap.txt new/.hg/authormap $ hg convert orig new - Ignoring bad line in author map file $TESTTMP/new/.hg/authormap: this line is ignored (glob) + ignoring bad line in author map file $TESTTMP/new/.hg/authormap: this line is ignored (glob) scanning source... sorting... converting... diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-baz --- a/tests/test-convert-baz Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -#!/bin/sh - -"$TESTDIR/hghave" baz || exit 80 - -baz my-id "mercurial " - -echo "[extensions]" >> $HGRCPATH -echo "convert=" >> $HGRCPATH -echo 'graphlog =' >> $HGRCPATH - -echo % create baz archive -baz make-archive baz@mercurial--convert hg-test-convert-baz - -echo % initialize baz repo -mkdir baz-repo -cd baz-repo/ -baz init-tree baz@mercurial--convert/baz--test--0 -baz import - -echo % create initial files -echo 'this is a file' > a -baz add a -mkdir src -baz add src -cd src -dd count=1 if=/dev/zero of=b > /dev/null 2> /dev/null -baz add b -# HACK: hide GNU tar-1.22 "tar: The --preserve option is deprecated, use --preserve-permissions --preserve-order instead" -baz commit -s "added a file, src and src/b (binary)" 2>&1 | grep -v '^tar' - -echo % create link file and modify a -ln -s ../a a-link -baz add a-link -echo 'this a modification to a' >> ../a -baz commit -s "added link to a and modify a" - -echo % create second link and modify b -ln -s ../a a-link-2 -baz add a-link-2 -dd count=1 seek=1 if=/dev/zero of=b > /dev/null 2> /dev/null -baz commit -s "added second link and modify b" - -echo % b file to link and a-link-2 to regular file -rm -f a-link-2 -echo 'this is now a regular file' > a-link-2 -ln -sf ../a b -baz commit -s "file to link and link to file test" - -echo % move a-link-2 file and src directory -cd .. -baz mv src/a-link-2 c -baz mv src test -baz commit -s "move and rename a-link-2 file and src directory" - -echo % move and add the moved file again -echo e > e -baz add e -baz commit -s "add e" -baz mv e f -echo ee > e -baz add e -baz commit -s "move e and recreate it again" -cd .. - -echo % converting baz repo to Mercurial -hg convert baz-repo baz-repo-hg - -baz register-archive -d baz@mercurial--convert - -glog() -{ - hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@" -} - -echo % show graph log -glog -R baz-repo-hg -hg up -q -R baz-repo-hg -hg -R baz-repo-hg manifest --debug -hg -R baz-repo-hg log -r 5 -r 7 -C --debug | grep copies diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-baz.out --- a/tests/test-convert-baz.out Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -% create baz archive -% initialize baz repo -* creating version baz@mercurial--convert/baz--test--0 -* imported baz@mercurial--convert/baz--test--0 -% create initial files -* build pristine tree for baz@mercurial--convert/baz--test--0--base-0 -* Scanning for full-tree revision: . -* from import revision: baz@mercurial--convert/baz--test--0--base-0 -A/ .arch-ids -A/ src -A/ src/.arch-ids -A .arch-ids/a.id -A a -A src/.arch-ids/=id -A src/.arch-ids/b.id -A src/b -* update pristine tree (baz@mercurial--convert/baz--test--0--base-0 => baz--test--0--patch-1) -* committed baz@mercurial--convert/baz--test--0--patch-1 -% create link file and modify a -A src/.arch-ids/a-link.id -A src/a-link -M a -* update pristine tree (baz@mercurial--convert/baz--test--0--patch-1 => baz--test--0--patch-2) -* committed baz@mercurial--convert/baz--test--0--patch-2 -% create second link and modify b -A src/.arch-ids/a-link-2.id -A src/a-link-2 -Mb src/b -* update pristine tree (baz@mercurial--convert/baz--test--0--patch-2 => baz--test--0--patch-3) -* committed baz@mercurial--convert/baz--test--0--patch-3 -% b file to link and a-link-2 to regular file -fl src/b -lf src/a-link-2 -* update pristine tree (baz@mercurial--convert/baz--test--0--patch-3 => baz--test--0--patch-4) -* committed baz@mercurial--convert/baz--test--0--patch-4 -% move a-link-2 file and src directory -D/ src/.arch-ids -A/ test/.arch-ids -/> src test -=> src/.arch-ids/a-link-2.id .arch-ids/c.id -=> src/a-link-2 c -=> src/.arch-ids/=id test/.arch-ids/=id -=> src/.arch-ids/a-link.id test/.arch-ids/a-link.id -=> src/.arch-ids/b.id test/.arch-ids/b.id -* update pristine tree (baz@mercurial--convert/baz--test--0--patch-4 => baz--test--0--patch-5) -* committed baz@mercurial--convert/baz--test--0--patch-5 -% move and add the moved file again -A .arch-ids/e.id -A e -* update pristine tree (baz@mercurial--convert/baz--test--0--patch-5 => baz--test--0--patch-6) -* committed baz@mercurial--convert/baz--test--0--patch-6 -A .arch-ids/e.id -A e -=> .arch-ids/e.id .arch-ids/f.id -=> e f -* update pristine tree (baz@mercurial--convert/baz--test--0--patch-6 => baz--test--0--patch-7) -* committed baz@mercurial--convert/baz--test--0--patch-7 -% converting baz repo to Mercurial -initializing destination baz-repo-hg repository -analyzing tree version baz@mercurial--convert/baz--test--0... -scanning source... -sorting... -converting... -7 initial import -6 added a file, src and src/b (binary) -5 added link to a and modify a -4 added second link and modify b -3 file to link and link to file test -2 move and rename a-link-2 file and src directory -1 add e -0 move e and recreate it again -% show graph log -o 7 "move e and recreate it again" files: e f -| -o 6 "add e" files: e -| -o 5 "move and rename a-link-2 file and src directory" files: c src/a-link src/a-link-2 src/b test/a-link test/b -| -o 4 "file to link and link to file test" files: src/a-link-2 src/b -| -o 3 "added second link and modify b" files: src/a-link-2 src/b -| -o 2 "added link to a and modify a" files: a src/a-link -| -o 1 "added a file, src and src/b (binary)" files: a src/b -| -o 0 "initial import" files: - -c4072c4b72e1cabace081888efa148ee80ca3cbb 644 a -0201ac32a3a8e86e303dff60366382a54b48a72e 644 c -1a4a864db0073705a11b1439f563bfa4b46d9246 644 e -09e0222742fc3f75777fa9d68a5d8af7294cb5e7 644 f -c0067ba5ff0b7c9a3eb17270839d04614c435623 644 @ test/a-link -375f4263d86feacdea7e3c27100abd1560f2a973 644 @ test/b -copies: c (src/a-link-2) test/a-link (src/a-link) test/b (src/b) -copies: f (e) diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-baz.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-baz.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,163 @@ + $ "$TESTDIR/hghave" baz symlink || exit 80 + + $ baz my-id "mercurial " + + $ echo "[extensions]" >> $HGRCPATH + $ echo "convert=" >> $HGRCPATH + $ echo 'graphlog =' >> $HGRCPATH + +create baz archive + $ baz make-archive baz@mercurial--convert hg-test-convert-baz + +initialize baz repo + $ mkdir baz-repo + $ cd baz-repo/ + $ baz init-tree baz@mercurial--convert/baz--test--0 + $ baz import + * creating version baz@mercurial--convert/baz--test--0 + * imported baz@mercurial--convert/baz--test--0 + +create initial files + $ echo 'this is a file' > a + $ baz add a + $ mkdir src + $ baz add src + $ cd src + $ dd count=1 if=/dev/zero of=b > /dev/null 2> /dev/null + $ baz add b +HACK: hide GNU tar-1.22 "tar: The --preserve option is deprecated, use --preserve-permissions --preserve-order instead" + $ baz commit -s "added a file, src and src/b (binary)" 2>&1 | grep -v '^tar' + * build pristine tree for baz@mercurial--convert/baz--test--0--base-0 + * Scanning for full-tree revision: . + * from import revision: baz@mercurial--convert/baz--test--0--base-0 + A/ .arch-ids + A/ src + A/ src/.arch-ids + A .arch-ids/a.id + A a + A src/.arch-ids/=id + A src/.arch-ids/b.id + A src/b + * update pristine tree (baz@mercurial--convert/baz--test--0--base-0 => baz--test--0--patch-1) + * committed baz@mercurial--convert/baz--test--0--patch-1 + +create link file and modify a + $ ln -s ../a a-link + $ baz add a-link + $ echo 'this a modification to a' >> ../a + $ baz commit -s "added link to a and modify a" + A src/.arch-ids/a-link.id + A src/a-link + M a + * update pristine tree (baz@mercurial--convert/baz--test--0--patch-1 => baz--test--0--patch-2) + * committed baz@mercurial--convert/baz--test--0--patch-2 + +create second link and modify b + $ ln -s ../a a-link-2 + $ baz add a-link-2 + $ dd count=1 seek=1 if=/dev/zero of=b > /dev/null 2> /dev/null + $ baz commit -s "added second link and modify b" + A src/.arch-ids/a-link-2.id + A src/a-link-2 + Mb src/b + * update pristine tree (baz@mercurial--convert/baz--test--0--patch-2 => baz--test--0--patch-3) + * committed baz@mercurial--convert/baz--test--0--patch-3 + +b file to link and a-link-2 to regular file + $ rm -f a-link-2 + $ echo 'this is now a regular file' > a-link-2 + $ ln -sf ../a b + $ baz commit -s "file to link and link to file test" + fl src/b + lf src/a-link-2 + * update pristine tree (baz@mercurial--convert/baz--test--0--patch-3 => baz--test--0--patch-4) + * committed baz@mercurial--convert/baz--test--0--patch-4 + +move a-link-2 file and src directory + $ cd .. + $ baz mv src/a-link-2 c + $ baz mv src test + $ baz commit -s "move and rename a-link-2 file and src directory" + D/ src/.arch-ids + A/ test/.arch-ids + /> src test + => src/.arch-ids/a-link-2.id .arch-ids/c.id + => src/a-link-2 c + => src/.arch-ids/=id test/.arch-ids/=id + => src/.arch-ids/a-link.id test/.arch-ids/a-link.id + => src/.arch-ids/b.id test/.arch-ids/b.id + * update pristine tree (baz@mercurial--convert/baz--test--0--patch-4 => baz--test--0--patch-5) + * committed baz@mercurial--convert/baz--test--0--patch-5 + +move and add the moved file again + $ echo e > e + $ baz add e + $ baz commit -s "add e" + A .arch-ids/e.id + A e + * update pristine tree (baz@mercurial--convert/baz--test--0--patch-5 => baz--test--0--patch-6) + * committed baz@mercurial--convert/baz--test--0--patch-6 + $ baz mv e f + $ echo ee > e + $ baz add e + $ baz commit -s "move e and recreate it again" + A .arch-ids/e.id + A e + => .arch-ids/e.id .arch-ids/f.id + => e f + * update pristine tree (baz@mercurial--convert/baz--test--0--patch-6 => baz--test--0--patch-7) + * committed baz@mercurial--convert/baz--test--0--patch-7 + $ cd .. + +converting baz repo to Mercurial + $ hg convert baz-repo baz-repo-hg + initializing destination baz-repo-hg repository + analyzing tree version baz@mercurial--convert/baz--test--0... + scanning source... + sorting... + converting... + 7 initial import + 6 added a file, src and src/b (binary) + 5 added link to a and modify a + 4 added second link and modify b + 3 file to link and link to file test + 2 move and rename a-link-2 file and src directory + 1 add e + 0 move e and recreate it again + + $ baz register-archive -d baz@mercurial--convert + + $ glog() + > { + > hg glog --template '{rev} "{desc|firstline}" files: {files}\n' "$@" + > } + +show graph log + $ glog -R baz-repo-hg + o 7 "move e and recreate it again" files: e f + | + o 6 "add e" files: e + | + o 5 "move and rename a-link-2 file and src directory" files: c src/a-link src/a-link-2 src/b test/a-link test/b + | + o 4 "file to link and link to file test" files: src/a-link-2 src/b + | + o 3 "added second link and modify b" files: src/a-link-2 src/b + | + o 2 "added link to a and modify a" files: a src/a-link + | + o 1 "added a file, src and src/b (binary)" files: a src/b + | + o 0 "initial import" files: + + $ hg up -q -R baz-repo-hg + $ hg -R baz-repo-hg manifest --debug + c4072c4b72e1cabace081888efa148ee80ca3cbb 644 a + 0201ac32a3a8e86e303dff60366382a54b48a72e 644 c + 1a4a864db0073705a11b1439f563bfa4b46d9246 644 e + 09e0222742fc3f75777fa9d68a5d8af7294cb5e7 644 f + c0067ba5ff0b7c9a3eb17270839d04614c435623 644 @ test/a-link + 375f4263d86feacdea7e3c27100abd1560f2a973 644 @ test/b + $ hg -R baz-repo-hg log -r 5 -r 7 -C --debug | grep copies + copies: c (src/a-link-2) test/a-link (src/a-link) test/b (src/b) + copies: f (e) diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-bzr-ghosts.t --- a/tests/test-convert-bzr-ghosts.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-bzr-ghosts.t Wed Jul 18 19:08:25 2012 -0500 @@ -34,3 +34,5 @@ | o 0@source "Initial layout setup" files: somefile + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-bzr-merges.t --- a/tests/test-convert-bzr-merges.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-bzr-merges.t Wed Jul 18 19:08:25 2012 -0500 @@ -66,3 +66,5 @@ 644 file-branch1 644 file-branch2 644 file-parent + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-bzr-treeroot.t --- a/tests/test-convert-bzr-treeroot.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-bzr-treeroot.t Wed Jul 18 19:08:25 2012 -0500 @@ -31,3 +31,5 @@ $ manifest source-hg tip % manifest of tip 644 file + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-bzr.t --- a/tests/test-convert-bzr.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-bzr.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" symlink execbit || exit 80 - $ . "$TESTDIR/bzr-definitions" create and rename on the same file in the same step @@ -157,6 +155,8 @@ $ cd .. +#if symlink execbit + symlinks and executable files $ mkdir test-symlinks @@ -199,14 +199,17 @@ 755 * newprog 644 program 644 @ syma - $ cd source-hg test the symlinks can be recreated + $ cd source-hg $ hg up 5 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg cat syma; echo a + $ cd ../.. + +#endif Multiple branches @@ -222,6 +225,7 @@ $ bzr switch -b branch Tree is up to date at revision 1. Switched to branch: *repo/branch/ (glob) + $ sleep 1 $ echo b > b $ bzr add -q b $ bzr ci -qm addb @@ -230,6 +234,7 @@ $ bzr switch --force ../repo/trunk Updated to revision 1. Switched to branch: */repo/trunk/ (glob) + $ sleep 1 $ echo a >> a $ bzr ci -qm changea $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-cvs-branch.t --- a/tests/test-convert-cvs-branch.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-cvs-branch.t Wed Jul 18 19:08:25 2012 -0500 @@ -190,3 +190,4 @@ b.txt:1.2->1.2.2.1 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-cvs.t --- a/tests/test-convert-cvs.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-cvs.t Wed Jul 18 19:08:25 2012 -0500 @@ -143,9 +143,9 @@ sorting... converting... updating tags - $ hg cat -r tip srcfull-hg/src/a + $ hg cat -r tip --cwd srcfull-hg src/a a - $ hg cat -r tip srcfull-hg/src/b/c + $ hg cat -r tip --cwd srcfull-hg src/b/c c c @@ -458,3 +458,4 @@ b/c:1.1.2.1->1.1.2.2 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-cvsnt-mergepoints.t --- a/tests/test-convert-cvsnt-mergepoints.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-cvsnt-mergepoints.t Wed Jul 18 19:08:25 2012 -0500 @@ -91,6 +91,8 @@ $ echo xyzzy > foo.txt $ cvsci -m "merge1+clobber" foo.txt +#if unix-permissions + return to trunk and merge MYBRANCH1_2 $ cvscall -Q update -P -A @@ -200,3 +202,6 @@ Members: foo.txt:1.1.4.1->1.1.4.2 +#endif + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-darcs.t --- a/tests/test-convert-darcs.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-darcs.t Wed Jul 18 19:08:25 2012 -0500 @@ -14,12 +14,16 @@ > exit 80 > fi +#if no-outer-repo + try converting darcs1 repository $ hg clone -q "$TESTDIR/bundles/darcs1.hg" darcs $ hg convert -s darcs darcs/darcs1 2>&1 | grep darcs-1.0 darcs-1.0 repository format is unsupported, please upgrade +#endif + initialize darcs repo $ mkdir darcs-repo @@ -32,7 +36,7 @@ branch and update - $ darcs get darcs-repo darcs-clone >/dev/null + $ darcs get -q darcs-repo darcs-clone >/dev/null $ cd darcs-clone $ echo c >> a $ echo c > c @@ -48,11 +52,10 @@ $ darcs record -a -l -m p1.2 Finished recording patch 'p1.2' - $ darcs pull -a --no-set-default ../darcs-clone - Backing up ./a(-darcs-backup0) + $ darcs pull -q -a --no-set-default ../darcs-clone + Backing up ./a(*) (glob) We have conflicts in the following files: ./a - Finished pulling and applying. $ sleep 1 $ echo e > a $ echo f > f diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-filemap.t --- a/tests/test-convert-filemap.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-filemap.t Wed Jul 18 19:08:25 2012 -0500 @@ -408,3 +408,189 @@ o 0 "add" files: a c + +test merge parents/empty merges pruning + + $ glog() + > { + > hg glog --template '{rev}:{node|short}@{branch} "{desc}" files: {files}\n' "$@" + > } + +test anonymous branch pruning + + $ hg init anonymousbranch + $ cd anonymousbranch + $ echo a > a + $ echo b > b + $ hg ci -Am add + adding a + adding b + $ echo a >> a + $ hg ci -m changea + $ hg up 0 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo b >> b + $ hg ci -m changeb + created new head + $ hg up 1 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m merge + $ cd .. + + $ cat > filemap < include a + > EOF + $ hg convert --filemap filemap anonymousbranch anonymousbranch-hg + initializing destination anonymousbranch-hg repository + scanning source... + sorting... + converting... + 3 add + 2 changea + 1 changeb + 0 merge + $ glog -R anonymousbranch + @ 3:c71d5201a498@default "merge" files: + |\ + | o 2:607eb44b17f9@default "changeb" files: b + | | + o | 1:1f60ea617824@default "changea" files: a + |/ + o 0:0146e6129113@default "add" files: a b + + $ glog -R anonymousbranch-hg + o 1:cda818e7219b@default "changea" files: a + | + o 0:c334dc3be0da@default "add" files: a + + $ cat anonymousbranch-hg/.hg/shamap + 0146e6129113dba9ac90207cfdf2d7ed35257ae5 c334dc3be0daa2a4e9ce4d2e2bdcba40c09d4916 + 1f60ea61782421edf8d051ff4fcb61b330f26a4a cda818e7219b5f7f3fb9f49780054ed6a1905ec3 + 607eb44b17f9348cd5cbd26e16af87ba77b0b037 c334dc3be0daa2a4e9ce4d2e2bdcba40c09d4916 + c71d5201a498b2658d105a6bf69d7a0df2649aea cda818e7219b5f7f3fb9f49780054ed6a1905ec3 + + $ cat > filemap < include b + > EOF + $ hg convert --filemap filemap anonymousbranch anonymousbranch-hg2 + initializing destination anonymousbranch-hg2 repository + scanning source... + sorting... + converting... + 3 add + 2 changea + 1 changeb + 0 merge + $ glog -R anonymousbranch + @ 3:c71d5201a498@default "merge" files: + |\ + | o 2:607eb44b17f9@default "changeb" files: b + | | + o | 1:1f60ea617824@default "changea" files: a + |/ + o 0:0146e6129113@default "add" files: a b + + $ glog -R anonymousbranch-hg2 + o 1:62dd350b0df6@default "changeb" files: b + | + o 0:4b9ced861657@default "add" files: b + + $ cat anonymousbranch-hg2/.hg/shamap + 0146e6129113dba9ac90207cfdf2d7ed35257ae5 4b9ced86165703791653059a1db6ed864630a523 + 1f60ea61782421edf8d051ff4fcb61b330f26a4a 4b9ced86165703791653059a1db6ed864630a523 + 607eb44b17f9348cd5cbd26e16af87ba77b0b037 62dd350b0df695f7d2c82a02e0499b16fd790f22 + c71d5201a498b2658d105a6bf69d7a0df2649aea 62dd350b0df695f7d2c82a02e0499b16fd790f22 + +test named branch pruning + + $ hg init namedbranch + $ cd namedbranch + $ echo a > a + $ echo b > b + $ hg ci -Am add + adding a + adding b + $ echo a >> a + $ hg ci -m changea + $ hg up 0 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg branch foo + marked working directory as branch foo + (branches are permanent and global, did you want a bookmark?) + $ echo b >> b + $ hg ci -m changeb + $ hg up default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge foo + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m merge + $ cd .. + + $ cat > filemap < include a + > EOF + $ hg convert --filemap filemap namedbranch namedbranch-hg + initializing destination namedbranch-hg repository + scanning source... + sorting... + converting... + 3 add + 2 changea + 1 changeb + 0 merge + $ glog -R namedbranch + @ 3:73899bcbe45c@default "merge" files: + |\ + | o 2:8097982d19fc@foo "changeb" files: b + | | + o | 1:1f60ea617824@default "changea" files: a + |/ + o 0:0146e6129113@default "add" files: a b + + $ glog -R namedbranch-hg + o 1:cda818e7219b@default "changea" files: a + | + o 0:c334dc3be0da@default "add" files: a + + + $ cd namedbranch + $ hg --config extensions.mq= strip tip + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/namedbranch/.hg/strip-backup/73899bcbe45c-backup.hg (glob) + $ hg up foo + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg ci -m merge + $ cd .. + + $ hg convert --filemap filemap namedbranch namedbranch-hg2 + initializing destination namedbranch-hg2 repository + scanning source... + sorting... + converting... + 3 add + 2 changea + 1 changeb + 0 merge + $ glog -R namedbranch + @ 3:e1959de76e1b@foo "merge" files: + |\ + | o 2:8097982d19fc@foo "changeb" files: b + | | + o | 1:1f60ea617824@default "changea" files: a + |/ + o 0:0146e6129113@default "add" files: a b + + $ glog -R namedbranch-hg2 + o 2:dcf314454667@foo "merge" files: + |\ + | o 1:cda818e7219b@default "changea" files: a + |/ + o 0:c334dc3be0da@default "add" files: a + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-git.t --- a/tests/test-convert-git.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-git.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,9 @@ $ "$TESTDIR/hghave" git || exit 80 + $ echo "[core]" >> $HOME/.gitconfig + $ echo "autocrlf = false" >> $HOME/.gitconfig + $ echo "[core]" >> $HOME/.gitconfig + $ echo "autocrlf = false" >> $HOME/.gitconfig $ echo "[extensions]" >> $HGRCPATH $ echo "convert=" >> $HGRCPATH $ echo 'hgext.graphlog =' >> $HGRCPATH @@ -281,9 +285,12 @@ $ cat > damage.py < import os + > import stat > for root, dirs, files in os.walk('git-repo4/.git/objects'): > if files: > path = os.path.join(root, files[0]) + > if os.name == 'nt': + > os.chmod(path, stat.S_IWUSR) > os.remove(path) > break > EOF diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-hg-source.t --- a/tests/test-convert-hg-source.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-hg-source.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" execbit || exit 80 - $ cat >> $HGRCPATH < [extensions] > convert= @@ -19,7 +17,7 @@ $ hg ci -m 'make bar and baz copies of foo' -d '2 0' created new head $ hg bookmark premerge1 - $ hg merge + $ hg merge -r 1 merging baz and foo to baz 1 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -33,7 +31,11 @@ (branch merge, don't forget to commit) $ hg ci -m 'merge remote copy' -d '4 0' created new head +#if execbit $ chmod +x baz +#else + $ echo some other change to make sure we get a rev 5 > baz +#endif $ hg ci -m 'mark baz executable' -d '5 0' $ cd .. $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded' @@ -54,9 +56,16 @@ searching for changes no changes found [1] +#if execbit $ hg bookmarks premerge1 3:973ef48a98a4 premerge2 5:13d9b87cf8f8 +#else +Different hash because no x bit + $ hg bookmarks + premerge1 3:973ef48a98a4 + premerge2 5:df0779bcf33c +#endif $ cd .. check shamap LF and CRLF handling diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-hg-svn.t --- a/tests/test-convert-hg-svn.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-hg-svn.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,15 +1,18 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ fix_path() - > { - > tr '\\' / - > } $ echo "[extensions]" >> $HGRCPATH $ echo "convert = " >> $HGRCPATH $ echo "mq = " >> $HGRCPATH - $ svnpath=`pwd | fix_path`/svn-repo - $ svnadmin create "$svnpath" - $ cat > "$svnpath"/hooks/pre-revprop-change < "$SVNREPOPATH"/hooks/pre-revprop-change < #!/bin/sh > > REPOS="$1" @@ -25,16 +28,10 @@ > echo "Changing prohibited revision property" >&2 > exit 1 > EOF - $ chmod +x "$svnpath"/hooks/pre-revprop-change - $ - $ # SVN wants all paths to start with a slash. Unfortunately, - $ # Windows ones don't. Handle that. - $ svnurl="$svnpath" - $ expr "$svnurl" : "\/" > /dev/null || svnurl="/$svnurl" - $ svnurl="file://$svnurl" - $ svn co "$svnurl" "$svnpath"-wc + $ chmod +x "$SVNREPOPATH"/hooks/pre-revprop-change + $ svn co "$SVNREPOURL" "$SVNREPOPATH"-wc Checked out revision 0. - $ cd "$svnpath"-wc + $ cd "$SVNREPOPATH"-wc $ echo a > a $ svn add a A a @@ -46,33 +43,33 @@ initial roundtrip - $ hg convert -s svn -d hg "$svnpath"-wc "$svnpath"-hg | grep -v initializing + $ hg convert -s svn -d hg "$SVNREPOPATH"-wc "$SVNREPOPATH"-hg | grep -v initializing scanning source... sorting... converting... 0 added a - $ hg convert -s hg -d svn "$svnpath"-hg "$svnpath"-wc + $ hg convert -s hg -d svn "$SVNREPOPATH"-hg "$SVNREPOPATH"-wc scanning source... sorting... converting... second roundtrip should do nothing - $ hg convert -s svn -d hg "$svnpath"-wc "$svnpath"-hg + $ hg convert -s svn -d hg "$SVNREPOPATH"-wc "$SVNREPOPATH"-hg scanning source... sorting... converting... - $ hg convert -s hg -d svn "$svnpath"-hg "$svnpath"-wc + $ hg convert -s hg -d svn "$SVNREPOPATH"-hg "$SVNREPOPATH"-wc scanning source... sorting... converting... new hg rev - $ hg clone "$svnpath"-hg "$svnpath"-work + $ hg clone "$SVNREPOPATH"-hg "$SVNREPOPATH"-work updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd "$svnpath"-work + $ cd "$SVNREPOPATH"-work $ echo b > b $ hg add b $ hg ci -mb @@ -85,8 +82,8 @@ echo hg to svn - $ hg --cwd "$svnpath"-hg pull -q "$svnpath"-work - $ hg convert -s hg -d svn "$svnpath"-hg "$svnpath"-wc + $ hg --cwd "$SVNREPOPATH"-hg pull -q "$SVNREPOPATH"-work + $ hg convert -s hg -d svn "$SVNREPOPATH"-hg "$SVNREPOPATH"-wc scanning source... sorting... converting... @@ -95,14 +92,14 @@ svn back to hg should do nothing - $ hg convert -s svn -d hg "$svnpath"-wc "$svnpath"-hg + $ hg convert -s svn -d hg "$SVNREPOPATH"-wc "$SVNREPOPATH"-hg scanning source... sorting... converting... hg back to svn should do nothing - $ hg convert -s hg -d svn "$svnpath"-hg "$svnpath"-wc + $ hg convert -s hg -d svn "$SVNREPOPATH"-hg "$SVNREPOPATH"-wc scanning source... sorting... converting... diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-p4 --- a/tests/test-convert-p4 Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -#!/bin/sh - -"$TESTDIR/hghave" p4 || exit 80 - -echo "[extensions]" >> $HGRCPATH -echo "convert = " >> $HGRCPATH - -echo % create p4 depot -P4ROOT=`pwd`/depot; export P4ROOT -P4AUDIT=$P4ROOT/audit; export P4AUDIT -P4JOURNAL=$P4ROOT/journal; export P4JOURNAL -P4LOG=$P4ROOT/log; export P4LOG -P4PORT=localhost:16661; export P4PORT -P4DEBUG=1; export P4DEBUG - -echo % start the p4 server -[ ! -d $P4ROOT ] && mkdir $P4ROOT -p4d -f -J off >$P4ROOT/stdout 2>$P4ROOT/stderr & -trap "echo % stop the p4 server ; p4 admin stop" EXIT - -# wait for the server to initialize -while ! p4 ; do - sleep 1 -done >/dev/null 2>/dev/null - -echo % create a client spec -P4CLIENT=hg-p4-import; export P4CLIENT -DEPOTPATH=//depot/test-mercurial-import/... -p4 client -o | sed '/^View:/,$ d' >p4client -echo View: >>p4client -echo " $DEPOTPATH //$P4CLIENT/..." >>p4client -p4 client -i a -mkdir b -echo c > b/c -p4 add a b/c -p4 submit -d initial - -echo % change some files -p4 edit a -echo aa >> a -p4 submit -d "change a" - -p4 edit b/c -echo cc >> b/c -p4 submit -d "change b/c" - -echo % convert -hg convert -s p4 $DEPOTPATH dst -hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' - -echo % change some files -p4 edit a b/c -echo aaa >> a -echo ccc >> b/c -p4 submit -d "change a b/c" - -echo % convert again -hg convert -s p4 $DEPOTPATH dst -hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' - -echo % interesting names -echo dddd > "d d" -mkdir " e" -echo fff >" e/ f" -p4 add "d d" " e/ f" -p4 submit -d "add d e f" - -echo % convert again -hg convert -s p4 $DEPOTPATH dst -hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' - - diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-p4-filetypes --- a/tests/test-convert-p4-filetypes Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -#!/bin/sh - -"$TESTDIR/hghave" p4 execbit symlink || exit 80 - -echo "[extensions]" >> $HGRCPATH -echo "convert = " >> $HGRCPATH - -echo % create p4 depot -P4ROOT=`pwd`/depot; export P4ROOT -P4AUDIT=$P4ROOT/audit; export P4AUDIT -P4JOURNAL=$P4ROOT/journal; export P4JOURNAL -P4LOG=$P4ROOT/log; export P4LOG -P4PORT=localhost:16661; export P4PORT -P4DEBUG=1; export P4DEBUG -P4CHARSET=utf8; export P4CHARSET - -echo % start the p4 server -[ ! -d $P4ROOT ] && mkdir $P4ROOT -p4d -f -J off -xi >$P4ROOT/stdout 2>$P4ROOT/stderr -p4d -f -J off >$P4ROOT/stdout 2>$P4ROOT/stderr & -trap "echo % stop the p4 server ; p4 admin stop" EXIT - -# wait for the server to initialize -while ! p4 ; do - sleep 1 -done >/dev/null 2>/dev/null - -echo % create a client spec -P4CLIENT=hg-p4-import; export P4CLIENT -DEPOTPATH=//depot/test-mercurial-import/... -p4 client -o | sed '/^View:/,$ d' >p4client -echo View: >>p4client -echo " $DEPOTPATH //$P4CLIENT/..." >>p4client -p4 client -i target_$T2 - ln -s target_$T file_$T2 - p4 add target_$T2 - p4 add -t $T file_$T2 - ;; - binary*) - python -c "file('file_$T2', 'wb').write('this is $T')" - p4 add -t $T file_$T2 - ;; - *) - echo "this is $T" >file_$T2 - p4 add -t $T file_$T2 - ;; - esac -done -p4 submit -d initial - -echo % test keyword expansion -p4 edit file_* target_* -for T in $TYPES ; do - T2=`echo $T | tr [:upper:] [:lower:]` - echo '$Id$' >>file_$T2 - echo '$Header$' >>file_$T2 - echo '$Date$' >>file_$T2 - echo '$DateTime$' >>file_$T2 - echo '$Change$' >>file_$T2 - echo '$File$' >>file_$T2 - echo '$Revision$' >>file_$T2 - echo '$Header$$Header$Header$' >>file_$T2 -done - -ln -s 'target_$Header$' crazy_symlink+k -p4 add -t symlink+k crazy_symlink+k - -p4 submit -d keywords - -echo % check keywords in p4 -grep -H Header file_* - -echo % convert -hg convert -s p4 $DEPOTPATH dst -hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n'g - -echo % revision 0 -hg -R dst update 0 -head dst/file_* | cat -v - -echo -echo % revision 1 -hg -R dst update 1 -head dst/file_* | cat -v -echo -echo % crazy_symlink -readlink crazy_symlink+k -readlink dst/crazy_symlink+k - diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-p4-filetypes.out --- a/tests/test-convert-p4-filetypes.out Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,642 +0,0 @@ -% create p4 depot -% start the p4 server -% create a client spec -Client hg-p4-import saved. -% populate the depot -//depot/test-mercurial-import/file_text#1 - opened for add -//depot/test-mercurial-import/file_binary#1 - opened for add -//depot/test-mercurial-import/target_symlink#1 - opened for add -//depot/test-mercurial-import/file_symlink#1 - opened for add -//depot/test-mercurial-import/file_text+m#1 - opened for add -//depot/test-mercurial-import/file_text+w#1 - opened for add -//depot/test-mercurial-import/file_text+x#1 - opened for add -//depot/test-mercurial-import/file_text+k#1 - opened for add -//depot/test-mercurial-import/file_text+kx#1 - opened for add -//depot/test-mercurial-import/file_text+ko#1 - opened for add -//depot/test-mercurial-import/file_text+l#1 - opened for add -//depot/test-mercurial-import/file_text+c#1 - opened for add -//depot/test-mercurial-import/file_text+d#1 - opened for add -//depot/test-mercurial-import/file_text+f#1 - opened for add -//depot/test-mercurial-import/file_text+s#1 - opened for add -//depot/test-mercurial-import/file_text+s2#1 - opened for add -//depot/test-mercurial-import/file_binary+k#1 - opened for add -//depot/test-mercurial-import/file_binary+x#1 - opened for add -//depot/test-mercurial-import/file_binary+kx#1 - opened for add -//depot/test-mercurial-import/target_symlink+k#1 - opened for add -//depot/test-mercurial-import/file_symlink+k#1 - opened for add -//depot/test-mercurial-import/file_ctext#1 - opened for add -//depot/test-mercurial-import/file_cxtext#1 - opened for add -//depot/test-mercurial-import/file_ktext#1 - opened for add -//depot/test-mercurial-import/file_kxtext#1 - opened for add -//depot/test-mercurial-import/file_ltext#1 - opened for add -//depot/test-mercurial-import/file_tempobj#1 - opened for add -//depot/test-mercurial-import/file_ubinary#1 - opened for add -//depot/test-mercurial-import/file_uxbinary#1 - opened for add -//depot/test-mercurial-import/file_xbinary#1 - opened for add -//depot/test-mercurial-import/file_xltext#1 - opened for add -//depot/test-mercurial-import/file_xtempobj#1 - opened for add -//depot/test-mercurial-import/file_xtext#1 - opened for add -Submitting change 1. -Locking 33 files ... -add //depot/test-mercurial-import/file_binary#1 -add //depot/test-mercurial-import/file_binary+k#1 -add //depot/test-mercurial-import/file_binary+kx#1 -add //depot/test-mercurial-import/file_binary+x#1 -add //depot/test-mercurial-import/file_ctext#1 -add //depot/test-mercurial-import/file_cxtext#1 -add //depot/test-mercurial-import/file_ktext#1 -add //depot/test-mercurial-import/file_kxtext#1 -add //depot/test-mercurial-import/file_ltext#1 -add //depot/test-mercurial-import/file_symlink#1 -add //depot/test-mercurial-import/file_symlink+k#1 -add //depot/test-mercurial-import/file_tempobj#1 -add //depot/test-mercurial-import/file_text#1 -add //depot/test-mercurial-import/file_text+c#1 -add //depot/test-mercurial-import/file_text+d#1 -add //depot/test-mercurial-import/file_text+f#1 -add //depot/test-mercurial-import/file_text+k#1 -add //depot/test-mercurial-import/file_text+ko#1 -add //depot/test-mercurial-import/file_text+kx#1 -add //depot/test-mercurial-import/file_text+l#1 -add //depot/test-mercurial-import/file_text+m#1 -add //depot/test-mercurial-import/file_text+s#1 -add //depot/test-mercurial-import/file_text+s2#1 -add //depot/test-mercurial-import/file_text+w#1 -add //depot/test-mercurial-import/file_text+x#1 -add //depot/test-mercurial-import/file_ubinary#1 -add //depot/test-mercurial-import/file_uxbinary#1 -add //depot/test-mercurial-import/file_xbinary#1 -add //depot/test-mercurial-import/file_xltext#1 -add //depot/test-mercurial-import/file_xtempobj#1 -add //depot/test-mercurial-import/file_xtext#1 -add //depot/test-mercurial-import/target_symlink#1 -add //depot/test-mercurial-import/target_symlink+k#1 -Change 1 submitted. -//depot/test-mercurial-import/file_binary+k#1 - refreshing -//depot/test-mercurial-import/file_binary+kx#1 - refreshing -//depot/test-mercurial-import/file_ktext#1 - refreshing -//depot/test-mercurial-import/file_kxtext#1 - refreshing -//depot/test-mercurial-import/file_symlink+k#1 - refreshing -//depot/test-mercurial-import/file_text+k#1 - refreshing -//depot/test-mercurial-import/file_text+ko#1 - refreshing -//depot/test-mercurial-import/file_text+kx#1 - refreshing -% test keyword expansion -//depot/test-mercurial-import/file_binary#1 - opened for edit -//depot/test-mercurial-import/file_binary+k#1 - opened for edit -//depot/test-mercurial-import/file_binary+kx#1 - opened for edit -//depot/test-mercurial-import/file_binary+x#1 - opened for edit -//depot/test-mercurial-import/file_ctext#1 - opened for edit -//depot/test-mercurial-import/file_cxtext#1 - opened for edit -//depot/test-mercurial-import/file_ktext#1 - opened for edit -//depot/test-mercurial-import/file_kxtext#1 - opened for edit -//depot/test-mercurial-import/file_ltext#1 - opened for edit -//depot/test-mercurial-import/file_symlink#1 - opened for edit -//depot/test-mercurial-import/file_symlink+k#1 - opened for edit -//depot/test-mercurial-import/file_tempobj#1 - opened for edit -//depot/test-mercurial-import/file_text#1 - opened for edit -//depot/test-mercurial-import/file_text+c#1 - opened for edit -//depot/test-mercurial-import/file_text+d#1 - opened for edit -//depot/test-mercurial-import/file_text+f#1 - opened for edit -//depot/test-mercurial-import/file_text+k#1 - opened for edit -//depot/test-mercurial-import/file_text+ko#1 - opened for edit -//depot/test-mercurial-import/file_text+kx#1 - opened for edit -//depot/test-mercurial-import/file_text+l#1 - opened for edit -//depot/test-mercurial-import/file_text+m#1 - opened for edit -//depot/test-mercurial-import/file_text+s#1 - opened for edit -//depot/test-mercurial-import/file_text+s2#1 - opened for edit -//depot/test-mercurial-import/file_text+w#1 - opened for edit -//depot/test-mercurial-import/file_text+x#1 - opened for edit -//depot/test-mercurial-import/file_ubinary#1 - opened for edit -//depot/test-mercurial-import/file_uxbinary#1 - opened for edit -//depot/test-mercurial-import/file_xbinary#1 - opened for edit -//depot/test-mercurial-import/file_xltext#1 - opened for edit -//depot/test-mercurial-import/file_xtempobj#1 - opened for edit -//depot/test-mercurial-import/file_xtext#1 - opened for edit -//depot/test-mercurial-import/target_symlink#1 - opened for edit -//depot/test-mercurial-import/target_symlink+k#1 - opened for edit -//depot/test-mercurial-import/crazy_symlink+k#1 - opened for add -Submitting change 2. -Locking 34 files ... -add //depot/test-mercurial-import/crazy_symlink+k#1 -edit //depot/test-mercurial-import/file_binary#2 -edit //depot/test-mercurial-import/file_binary+k#2 -edit //depot/test-mercurial-import/file_binary+kx#2 -edit //depot/test-mercurial-import/file_binary+x#2 -edit //depot/test-mercurial-import/file_ctext#2 -edit //depot/test-mercurial-import/file_cxtext#2 -edit //depot/test-mercurial-import/file_ktext#2 -edit //depot/test-mercurial-import/file_kxtext#2 -edit //depot/test-mercurial-import/file_ltext#2 -edit //depot/test-mercurial-import/file_symlink#2 -edit //depot/test-mercurial-import/file_symlink+k#2 -edit //depot/test-mercurial-import/file_tempobj#2 -edit //depot/test-mercurial-import/file_text#2 -edit //depot/test-mercurial-import/file_text+c#2 -edit //depot/test-mercurial-import/file_text+d#2 -edit //depot/test-mercurial-import/file_text+f#2 -edit //depot/test-mercurial-import/file_text+k#2 -edit //depot/test-mercurial-import/file_text+ko#2 -edit //depot/test-mercurial-import/file_text+kx#2 -edit //depot/test-mercurial-import/file_text+l#2 -edit //depot/test-mercurial-import/file_text+m#2 -edit //depot/test-mercurial-import/file_text+s#2 -edit //depot/test-mercurial-import/file_text+s2#2 -edit //depot/test-mercurial-import/file_text+w#2 -edit //depot/test-mercurial-import/file_text+x#2 -edit //depot/test-mercurial-import/file_ubinary#2 -edit //depot/test-mercurial-import/file_uxbinary#2 -edit //depot/test-mercurial-import/file_xbinary#2 -edit //depot/test-mercurial-import/file_xltext#2 -edit //depot/test-mercurial-import/file_xtempobj#2 -edit //depot/test-mercurial-import/file_xtext#2 -edit //depot/test-mercurial-import/target_symlink#2 -edit //depot/test-mercurial-import/target_symlink+k#2 -Change 2 submitted. -//depot/test-mercurial-import/crazy_symlink+k#1 - refreshing -//depot/test-mercurial-import/file_binary+k#2 - refreshing -//depot/test-mercurial-import/file_binary+kx#2 - refreshing -//depot/test-mercurial-import/file_ktext#2 - refreshing -//depot/test-mercurial-import/file_kxtext#2 - refreshing -//depot/test-mercurial-import/file_symlink+k#2 - refreshing -//depot/test-mercurial-import/file_text+k#2 - refreshing -//depot/test-mercurial-import/file_text+ko#2 - refreshing -//depot/test-mercurial-import/file_text+kx#2 - refreshing -% check keywords in p4 -file_binary:$Header$ -file_binary:$Header$$Header$Header$ -file_binary+k:$Header: //depot/test-mercurial-import/file_binary+k#2 $ -file_binary+k:$Header: //depot/test-mercurial-import/file_binary+k#2 $$Header: //depot/test-mercurial-import/file_binary+k#2 $Header$ -file_binary+kx:$Header: //depot/test-mercurial-import/file_binary+kx#2 $ -file_binary+kx:$Header: //depot/test-mercurial-import/file_binary+kx#2 $$Header: //depot/test-mercurial-import/file_binary+kx#2 $Header$ -file_binary+x:$Header$ -file_binary+x:$Header$$Header$Header$ -file_ctext:$Header$ -file_ctext:$Header$$Header$Header$ -file_cxtext:$Header$ -file_cxtext:$Header$$Header$Header$ -file_ktext:$Header: //depot/test-mercurial-import/file_ktext#2 $ -file_ktext:$Header: //depot/test-mercurial-import/file_ktext#2 $$Header: //depot/test-mercurial-import/file_ktext#2 $Header$ -file_kxtext:$Header: //depot/test-mercurial-import/file_kxtext#2 $ -file_kxtext:$Header: //depot/test-mercurial-import/file_kxtext#2 $$Header: //depot/test-mercurial-import/file_kxtext#2 $Header$ -file_ltext:$Header$ -file_ltext:$Header$$Header$Header$ -file_symlink:$Header$ -file_symlink:$Header$$Header$Header$ -file_symlink+k:$Header$ -file_symlink+k:$Header$$Header$Header$ -file_tempobj:$Header$ -file_tempobj:$Header$$Header$Header$ -file_text:$Header$ -file_text:$Header$$Header$Header$ -file_text+c:$Header$ -file_text+c:$Header$$Header$Header$ -file_text+d:$Header$ -file_text+d:$Header$$Header$Header$ -file_text+f:$Header$ -file_text+f:$Header$$Header$Header$ -file_text+k:$Header: //depot/test-mercurial-import/file_text+k#2 $ -file_text+k:$Header: //depot/test-mercurial-import/file_text+k#2 $$Header: //depot/test-mercurial-import/file_text+k#2 $Header$ -file_text+ko:$Header: //depot/test-mercurial-import/file_text+ko#2 $ -file_text+ko:$Header: //depot/test-mercurial-import/file_text+ko#2 $$Header: //depot/test-mercurial-import/file_text+ko#2 $Header$ -file_text+kx:$Header: //depot/test-mercurial-import/file_text+kx#2 $ -file_text+kx:$Header: //depot/test-mercurial-import/file_text+kx#2 $$Header: //depot/test-mercurial-import/file_text+kx#2 $Header$ -file_text+l:$Header$ -file_text+l:$Header$$Header$Header$ -file_text+m:$Header$ -file_text+m:$Header$$Header$Header$ -file_text+s:$Header$ -file_text+s:$Header$$Header$Header$ -file_text+s2:$Header$ -file_text+s2:$Header$$Header$Header$ -file_text+w:$Header$ -file_text+w:$Header$$Header$Header$ -file_text+x:$Header$ -file_text+x:$Header$$Header$Header$ -file_ubinary:$Header$ -file_ubinary:$Header$$Header$Header$ -file_uxbinary:$Header$ -file_uxbinary:$Header$$Header$Header$ -file_xbinary:$Header$ -file_xbinary:$Header$$Header$Header$ -file_xltext:$Header$ -file_xltext:$Header$$Header$Header$ -file_xtempobj:$Header$ -file_xtempobj:$Header$$Header$Header$ -file_xtext:$Header$ -file_xtext:$Header$$Header$Header$ -% convert -initializing destination dst repository -reading p4 views -collecting p4 changelists -1 initial -2 keywords -scanning source... -sorting... -converting... -1 initial -0 keywords -rev=1 desc="keywords" tags="tip" files="crazy_symlink+k file_binary file_binary+k file_binary+kx file_binary+x file_ctext file_cxtext file_ktext file_kxtext file_ltext file_text file_text+c file_text+d file_text+f file_text+k file_text+ko file_text+kx file_text+l file_text+m file_text+s file_text+s2 file_text+w file_text+x file_ubinary file_uxbinary file_xbinary file_xltext file_xtext target_symlink target_symlink+k" -grev=0 desc="initial" tags="" files="file_binary file_binary+k file_binary+kx file_binary+x file_ctext file_cxtext file_ktext file_kxtext file_ltext file_symlink file_symlink+k file_text file_text+c file_text+d file_text+f file_text+k file_text+ko file_text+kx file_text+l file_text+m file_text+s2 file_text+w file_text+x file_ubinary file_uxbinary file_xbinary file_xltext file_xtext target_symlink target_symlink+k" -g% revision 0 -30 files updated, 0 files merged, 0 files removed, 0 files unresolved -==> dst/file_binary <== -this is binary -==> dst/file_binary+k <== -this is binary+k -==> dst/file_binary+kx <== -this is binary+kx -==> dst/file_binary+x <== -this is binary+x -==> dst/file_ctext <== -this is ctext - -==> dst/file_cxtext <== -this is cxtext - -==> dst/file_ktext <== -this is ktext - -==> dst/file_kxtext <== -this is kxtext - -==> dst/file_ltext <== -this is ltext - -==> dst/file_symlink <== -this is target symlink - -==> dst/file_symlink+k <== -this is target symlink+k - -==> dst/file_text <== -this is text - -==> dst/file_text+c <== -this is text+C - -==> dst/file_text+d <== -this is text+D - -==> dst/file_text+f <== -this is text+F - -==> dst/file_text+k <== -this is text+k - -==> dst/file_text+ko <== -this is text+ko - -==> dst/file_text+kx <== -this is text+kx - -==> dst/file_text+l <== -this is text+l - -==> dst/file_text+m <== -this is text+m - -==> dst/file_text+s2 <== -this is text+S2 - -==> dst/file_text+w <== -this is text+w - -==> dst/file_text+x <== -this is text+x - -==> dst/file_ubinary <== -this is ubinary - -==> dst/file_uxbinary <== -this is uxbinary - -==> dst/file_xbinary <== -this is xbinary - -==> dst/file_xltext <== -this is xltext - -==> dst/file_xtext <== -this is xtext - -% revision 1 -30 files updated, 0 files merged, 0 files removed, 0 files unresolved -==> dst/file_binary <== -this is binary$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_binary+k <== -this is binary+k$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_binary+kx <== -this is binary+kx$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_binary+x <== -this is binary+x$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_ctext <== -this is ctext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_cxtext <== -this is cxtext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_ktext <== -this is ktext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_kxtext <== -this is kxtext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_ltext <== -this is ltext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_symlink <== -this is target symlink -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_symlink+k <== -this is target symlink+k -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text <== -this is text -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+c <== -this is text+C -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+d <== -this is text+D -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+f <== -this is text+F -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+k <== -this is text+k -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+ko <== -this is text+ko -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+kx <== -this is text+kx -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+l <== -this is text+l -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+m <== -this is text+m -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+s <== -this is text+S -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+s2 <== -this is text+S2 -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+w <== -this is text+w -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_text+x <== -this is text+x -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_ubinary <== -this is ubinary -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_uxbinary <== -this is uxbinary -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_xbinary <== -this is xbinary -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_xltext <== -this is xltext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -==> dst/file_xtext <== -this is xtext -$Id$ -$Header$ -$Date$ -$DateTime$ -$Change$ -$File$ -$Revision$ -$Header$$Header$Header$ - -% crazy_symlink -target_$Header: //depot/test-mercurial-import/crazy_symlink+k#1 $ -target_$Header$ -% stop the p4 server diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-p4-filetypes.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-p4-filetypes.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,733 @@ + $ "$TESTDIR/hghave" p4 execbit symlink || exit 80 + + $ echo "[extensions]" >> $HGRCPATH + $ echo "convert = " >> $HGRCPATH + +create p4 depot + $ P4ROOT=`pwd`/depot; export P4ROOT + $ P4AUDIT=$P4ROOT/audit; export P4AUDIT + $ P4JOURNAL=$P4ROOT/journal; export P4JOURNAL + $ P4LOG=$P4ROOT/log; export P4LOG + $ P4PORT=localhost:16661; export P4PORT + $ P4DEBUG=1; export P4DEBUG + $ P4CHARSET=utf8; export P4CHARSET + +start the p4 server + $ [ ! -d $P4ROOT ] && mkdir $P4ROOT + $ p4d -f -J off -xi >$P4ROOT/stdout 2>$P4ROOT/stderr + $ p4d -f -J off >$P4ROOT/stdout 2>$P4ROOT/stderr & + $ echo $! >> $DAEMON_PIDS + $ trap "echo stopping the p4 server ; p4 admin stop" EXIT + +wait for the server to initialize + $ while ! p4 ; do + > sleep 1 + > done >/dev/null 2>/dev/null + +create a client spec + $ P4CLIENT=hg-p4-import; export P4CLIENT + $ DEPOTPATH=//depot/test-mercurial-import/... + $ p4 client -o | sed '/^View:/,$ d' >p4client + $ echo View: >>p4client + $ echo " $DEPOTPATH //$P4CLIENT/..." >>p4client + $ p4 client -i T2=`echo $T | tr [:upper:] [:lower:]` + > case $T in + > apple) + > ;; + > symlink*) + > echo "this is target $T" >target_$T2 + > ln -s target_$T file_$T2 + > p4 add target_$T2 + > p4 add -t $T file_$T2 + > ;; + > binary*) + > python -c "file('file_$T2', 'wb').write('this is $T')" + > p4 add -t $T file_$T2 + > ;; + > *) + > echo "this is $T" >file_$T2 + > p4 add -t $T file_$T2 + > ;; + > esac + > done + //depot/test-mercurial-import/file_text#1 - opened for add + //depot/test-mercurial-import/file_binary#1 - opened for add + //depot/test-mercurial-import/target_symlink#1 - opened for add + //depot/test-mercurial-import/file_symlink#1 - opened for add + //depot/test-mercurial-import/file_text+m#1 - opened for add + //depot/test-mercurial-import/file_text+w#1 - opened for add + //depot/test-mercurial-import/file_text+x#1 - opened for add + //depot/test-mercurial-import/file_text+k#1 - opened for add + //depot/test-mercurial-import/file_text+kx#1 - opened for add + //depot/test-mercurial-import/file_text+ko#1 - opened for add + //depot/test-mercurial-import/file_text+l#1 - opened for add + //depot/test-mercurial-import/file_text+c#1 - opened for add + //depot/test-mercurial-import/file_text+d#1 - opened for add + //depot/test-mercurial-import/file_text+f#1 - opened for add + //depot/test-mercurial-import/file_text+s#1 - opened for add + //depot/test-mercurial-import/file_text+s2#1 - opened for add + //depot/test-mercurial-import/file_binary+k#1 - opened for add + //depot/test-mercurial-import/file_binary+x#1 - opened for add + //depot/test-mercurial-import/file_binary+kx#1 - opened for add + //depot/test-mercurial-import/target_symlink+k#1 - opened for add + //depot/test-mercurial-import/file_symlink+k#1 - opened for add + //depot/test-mercurial-import/file_ctext#1 - opened for add + //depot/test-mercurial-import/file_cxtext#1 - opened for add + //depot/test-mercurial-import/file_ktext#1 - opened for add + //depot/test-mercurial-import/file_kxtext#1 - opened for add + //depot/test-mercurial-import/file_ltext#1 - opened for add + //depot/test-mercurial-import/file_tempobj#1 - opened for add + //depot/test-mercurial-import/file_ubinary#1 - opened for add + //depot/test-mercurial-import/file_uxbinary#1 - opened for add + //depot/test-mercurial-import/file_xbinary#1 - opened for add + //depot/test-mercurial-import/file_xltext#1 - opened for add + //depot/test-mercurial-import/file_xtempobj#1 - opened for add + //depot/test-mercurial-import/file_xtext#1 - opened for add + $ p4 submit -d initial + Submitting change 1. + Locking 33 files ... + add //depot/test-mercurial-import/file_binary#1 + add //depot/test-mercurial-import/file_binary+k#1 + add //depot/test-mercurial-import/file_binary+kx#1 + add //depot/test-mercurial-import/file_binary+x#1 + add //depot/test-mercurial-import/file_ctext#1 + add //depot/test-mercurial-import/file_cxtext#1 + add //depot/test-mercurial-import/file_ktext#1 + add //depot/test-mercurial-import/file_kxtext#1 + add //depot/test-mercurial-import/file_ltext#1 + add //depot/test-mercurial-import/file_symlink#1 + add //depot/test-mercurial-import/file_symlink+k#1 + add //depot/test-mercurial-import/file_tempobj#1 + add //depot/test-mercurial-import/file_text#1 + add //depot/test-mercurial-import/file_text+c#1 + add //depot/test-mercurial-import/file_text+d#1 + add //depot/test-mercurial-import/file_text+f#1 + add //depot/test-mercurial-import/file_text+k#1 + add //depot/test-mercurial-import/file_text+ko#1 + add //depot/test-mercurial-import/file_text+kx#1 + add //depot/test-mercurial-import/file_text+l#1 + add //depot/test-mercurial-import/file_text+m#1 + add //depot/test-mercurial-import/file_text+s#1 + add //depot/test-mercurial-import/file_text+s2#1 + add //depot/test-mercurial-import/file_text+w#1 + add //depot/test-mercurial-import/file_text+x#1 + add //depot/test-mercurial-import/file_ubinary#1 + add //depot/test-mercurial-import/file_uxbinary#1 + add //depot/test-mercurial-import/file_xbinary#1 + add //depot/test-mercurial-import/file_xltext#1 + add //depot/test-mercurial-import/file_xtempobj#1 + add //depot/test-mercurial-import/file_xtext#1 + add //depot/test-mercurial-import/target_symlink#1 + add //depot/test-mercurial-import/target_symlink+k#1 + Change 1 submitted. + //depot/test-mercurial-import/file_binary+k#1 - refreshing + //depot/test-mercurial-import/file_binary+kx#1 - refreshing + //depot/test-mercurial-import/file_ktext#1 - refreshing + //depot/test-mercurial-import/file_kxtext#1 - refreshing + //depot/test-mercurial-import/file_symlink+k#1 - refreshing + //depot/test-mercurial-import/file_text+k#1 - refreshing + //depot/test-mercurial-import/file_text+ko#1 - refreshing + //depot/test-mercurial-import/file_text+kx#1 - refreshing + +test keyword expansion + $ p4 edit file_* target_* + //depot/test-mercurial-import/file_binary#1 - opened for edit + //depot/test-mercurial-import/file_binary+k#1 - opened for edit + //depot/test-mercurial-import/file_binary+kx#1 - opened for edit + //depot/test-mercurial-import/file_binary+x#1 - opened for edit + //depot/test-mercurial-import/file_ctext#1 - opened for edit + //depot/test-mercurial-import/file_cxtext#1 - opened for edit + //depot/test-mercurial-import/file_ktext#1 - opened for edit + //depot/test-mercurial-import/file_kxtext#1 - opened for edit + //depot/test-mercurial-import/file_ltext#1 - opened for edit + //depot/test-mercurial-import/file_symlink#1 - opened for edit + //depot/test-mercurial-import/file_symlink+k#1 - opened for edit + //depot/test-mercurial-import/file_tempobj#1 - opened for edit + //depot/test-mercurial-import/file_text#1 - opened for edit + //depot/test-mercurial-import/file_text+c#1 - opened for edit + //depot/test-mercurial-import/file_text+d#1 - opened for edit + //depot/test-mercurial-import/file_text+f#1 - opened for edit + //depot/test-mercurial-import/file_text+k#1 - opened for edit + //depot/test-mercurial-import/file_text+ko#1 - opened for edit + //depot/test-mercurial-import/file_text+kx#1 - opened for edit + //depot/test-mercurial-import/file_text+l#1 - opened for edit + //depot/test-mercurial-import/file_text+m#1 - opened for edit + //depot/test-mercurial-import/file_text+s#1 - opened for edit + //depot/test-mercurial-import/file_text+s2#1 - opened for edit + //depot/test-mercurial-import/file_text+w#1 - opened for edit + //depot/test-mercurial-import/file_text+x#1 - opened for edit + //depot/test-mercurial-import/file_ubinary#1 - opened for edit + //depot/test-mercurial-import/file_uxbinary#1 - opened for edit + //depot/test-mercurial-import/file_xbinary#1 - opened for edit + //depot/test-mercurial-import/file_xltext#1 - opened for edit + //depot/test-mercurial-import/file_xtempobj#1 - opened for edit + //depot/test-mercurial-import/file_xtext#1 - opened for edit + //depot/test-mercurial-import/target_symlink#1 - opened for edit + //depot/test-mercurial-import/target_symlink+k#1 - opened for edit + $ for T in $TYPES ; do + > T2=`echo $T | tr [:upper:] [:lower:]` + > echo '$Id$' >>file_$T2 + > echo '$Header$' >>file_$T2 + > echo '$Date$' >>file_$T2 + > echo '$DateTime$' >>file_$T2 + > echo '$Change$' >>file_$T2 + > echo '$File$' >>file_$T2 + > echo '$Revision$' >>file_$T2 + > echo '$Header$$Header$Header$' >>file_$T2 + > done + + $ ln -s 'target_$Header$' crazy_symlink+k + $ p4 add -t symlink+k crazy_symlink+k + //depot/test-mercurial-import/crazy_symlink+k#1 - opened for add + + $ p4 submit -d keywords + Submitting change 2. + Locking 34 files ... + add //depot/test-mercurial-import/crazy_symlink+k#1 + edit //depot/test-mercurial-import/file_binary#2 + edit //depot/test-mercurial-import/file_binary+k#2 + edit //depot/test-mercurial-import/file_binary+kx#2 + edit //depot/test-mercurial-import/file_binary+x#2 + edit //depot/test-mercurial-import/file_ctext#2 + edit //depot/test-mercurial-import/file_cxtext#2 + edit //depot/test-mercurial-import/file_ktext#2 + edit //depot/test-mercurial-import/file_kxtext#2 + edit //depot/test-mercurial-import/file_ltext#2 + edit //depot/test-mercurial-import/file_symlink#2 + edit //depot/test-mercurial-import/file_symlink+k#2 + edit //depot/test-mercurial-import/file_tempobj#2 + edit //depot/test-mercurial-import/file_text#2 + edit //depot/test-mercurial-import/file_text+c#2 + edit //depot/test-mercurial-import/file_text+d#2 + edit //depot/test-mercurial-import/file_text+f#2 + edit //depot/test-mercurial-import/file_text+k#2 + edit //depot/test-mercurial-import/file_text+ko#2 + edit //depot/test-mercurial-import/file_text+kx#2 + edit //depot/test-mercurial-import/file_text+l#2 + edit //depot/test-mercurial-import/file_text+m#2 + edit //depot/test-mercurial-import/file_text+s#2 + edit //depot/test-mercurial-import/file_text+s2#2 + edit //depot/test-mercurial-import/file_text+w#2 + edit //depot/test-mercurial-import/file_text+x#2 + edit //depot/test-mercurial-import/file_ubinary#2 + edit //depot/test-mercurial-import/file_uxbinary#2 + edit //depot/test-mercurial-import/file_xbinary#2 + edit //depot/test-mercurial-import/file_xltext#2 + edit //depot/test-mercurial-import/file_xtempobj#2 + edit //depot/test-mercurial-import/file_xtext#2 + edit //depot/test-mercurial-import/target_symlink#2 + edit //depot/test-mercurial-import/target_symlink+k#2 + Change 2 submitted. + //depot/test-mercurial-import/crazy_symlink+k#1 - refreshing + //depot/test-mercurial-import/file_binary+k#2 - refreshing + //depot/test-mercurial-import/file_binary+kx#2 - refreshing + //depot/test-mercurial-import/file_ktext#2 - refreshing + //depot/test-mercurial-import/file_kxtext#2 - refreshing + //depot/test-mercurial-import/file_symlink+k#2 - refreshing + //depot/test-mercurial-import/file_text+k#2 - refreshing + //depot/test-mercurial-import/file_text+ko#2 - refreshing + //depot/test-mercurial-import/file_text+kx#2 - refreshing + +check keywords in p4 + $ grep -H Header file_* + file_binary:$Header$ + file_binary:$Header$$Header$Header$ + file_binary+k:$Header: //depot/test-mercurial-import/file_binary+k#2 $ + file_binary+k:$Header: //depot/test-mercurial-import/file_binary+k#2 $$Header: //depot/test-mercurial-import/file_binary+k#2 $Header$ + file_binary+kx:$Header: //depot/test-mercurial-import/file_binary+kx#2 $ + file_binary+kx:$Header: //depot/test-mercurial-import/file_binary+kx#2 $$Header: //depot/test-mercurial-import/file_binary+kx#2 $Header$ + file_binary+x:$Header$ + file_binary+x:$Header$$Header$Header$ + file_ctext:$Header$ + file_ctext:$Header$$Header$Header$ + file_cxtext:$Header$ + file_cxtext:$Header$$Header$Header$ + file_ktext:$Header: //depot/test-mercurial-import/file_ktext#2 $ + file_ktext:$Header: //depot/test-mercurial-import/file_ktext#2 $$Header: //depot/test-mercurial-import/file_ktext#2 $Header$ + file_kxtext:$Header: //depot/test-mercurial-import/file_kxtext#2 $ + file_kxtext:$Header: //depot/test-mercurial-import/file_kxtext#2 $$Header: //depot/test-mercurial-import/file_kxtext#2 $Header$ + file_ltext:$Header$ + file_ltext:$Header$$Header$Header$ + file_symlink:$Header$ + file_symlink:$Header$$Header$Header$ + file_symlink+k:$Header$ + file_symlink+k:$Header$$Header$Header$ + file_tempobj:$Header$ + file_tempobj:$Header$$Header$Header$ + file_text:$Header$ + file_text:$Header$$Header$Header$ + file_text+c:$Header$ + file_text+c:$Header$$Header$Header$ + file_text+d:$Header$ + file_text+d:$Header$$Header$Header$ + file_text+f:$Header$ + file_text+f:$Header$$Header$Header$ + file_text+k:$Header: //depot/test-mercurial-import/file_text+k#2 $ + file_text+k:$Header: //depot/test-mercurial-import/file_text+k#2 $$Header: //depot/test-mercurial-import/file_text+k#2 $Header$ + file_text+ko:$Header: //depot/test-mercurial-import/file_text+ko#2 $ + file_text+ko:$Header: //depot/test-mercurial-import/file_text+ko#2 $$Header: //depot/test-mercurial-import/file_text+ko#2 $Header$ + file_text+kx:$Header: //depot/test-mercurial-import/file_text+kx#2 $ + file_text+kx:$Header: //depot/test-mercurial-import/file_text+kx#2 $$Header: //depot/test-mercurial-import/file_text+kx#2 $Header$ + file_text+l:$Header$ + file_text+l:$Header$$Header$Header$ + file_text+m:$Header$ + file_text+m:$Header$$Header$Header$ + file_text+s:$Header$ + file_text+s:$Header$$Header$Header$ + file_text+s2:$Header$ + file_text+s2:$Header$$Header$Header$ + file_text+w:$Header$ + file_text+w:$Header$$Header$Header$ + file_text+x:$Header$ + file_text+x:$Header$$Header$Header$ + file_ubinary:$Header$ + file_ubinary:$Header$$Header$Header$ + file_uxbinary:$Header$ + file_uxbinary:$Header$$Header$Header$ + file_xbinary:$Header$ + file_xbinary:$Header$$Header$Header$ + file_xltext:$Header$ + file_xltext:$Header$$Header$Header$ + file_xtempobj:$Header$ + file_xtempobj:$Header$$Header$Header$ + file_xtext:$Header$ + file_xtext:$Header$$Header$Header$ + +convert + $ hg convert -s p4 $DEPOTPATH dst + initializing destination dst repository + reading p4 views + collecting p4 changelists + 1 initial + 2 keywords + scanning source... + sorting... + converting... + 1 initial + 0 keywords + $ hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' + rev=1 desc="keywords" tags="tip" files="crazy_symlink+k file_binary file_binary+k file_binary+kx file_binary+x file_ctext file_cxtext file_ktext file_kxtext file_ltext file_text file_text+c file_text+d file_text+f file_text+k file_text+ko file_text+kx file_text+l file_text+m file_text+s file_text+s2 file_text+w file_text+x file_ubinary file_uxbinary file_xbinary file_xltext file_xtext target_symlink target_symlink+k" + rev=0 desc="initial" tags="" files="file_binary file_binary+k file_binary+kx file_binary+x file_ctext file_cxtext file_ktext file_kxtext file_ltext file_symlink file_symlink+k file_text file_text+c file_text+d file_text+f file_text+k file_text+ko file_text+kx file_text+l file_text+m file_text+s2 file_text+w file_text+x file_ubinary file_uxbinary file_xbinary file_xltext file_xtext target_symlink target_symlink+k" + +revision 0 + $ hg -R dst update 0 + 30 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ head dst/file_* | cat -v + ==> dst/file_binary <== + this is binary + ==> dst/file_binary+k <== + this is binary+k + ==> dst/file_binary+kx <== + this is binary+kx + ==> dst/file_binary+x <== + this is binary+x + ==> dst/file_ctext <== + this is ctext + + ==> dst/file_cxtext <== + this is cxtext + + ==> dst/file_ktext <== + this is ktext + + ==> dst/file_kxtext <== + this is kxtext + + ==> dst/file_ltext <== + this is ltext + + ==> dst/file_symlink <== + this is target symlink + + ==> dst/file_symlink+k <== + this is target symlink+k + + ==> dst/file_text <== + this is text + + ==> dst/file_text+c <== + this is text+C + + ==> dst/file_text+d <== + this is text+D + + ==> dst/file_text+f <== + this is text+F + + ==> dst/file_text+k <== + this is text+k + + ==> dst/file_text+ko <== + this is text+ko + + ==> dst/file_text+kx <== + this is text+kx + + ==> dst/file_text+l <== + this is text+l + + ==> dst/file_text+m <== + this is text+m + + ==> dst/file_text+s2 <== + this is text+S2 + + ==> dst/file_text+w <== + this is text+w + + ==> dst/file_text+x <== + this is text+x + + ==> dst/file_ubinary <== + this is ubinary + + ==> dst/file_uxbinary <== + this is uxbinary + + ==> dst/file_xbinary <== + this is xbinary + + ==> dst/file_xltext <== + this is xltext + + ==> dst/file_xtext <== + this is xtext + +revision 1 + $ hg -R dst update 1 + 30 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ head dst/file_* | cat -v + ==> dst/file_binary <== + this is binary$Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_binary+k <== + this is binary+k$Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_binary+kx <== + this is binary+kx$Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_binary+x <== + this is binary+x$Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_ctext <== + this is ctext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_cxtext <== + this is cxtext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_ktext <== + this is ktext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_kxtext <== + this is kxtext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_ltext <== + this is ltext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_symlink <== + this is target symlink + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_symlink+k <== + this is target symlink+k + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text <== + this is text + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+c <== + this is text+C + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+d <== + this is text+D + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+f <== + this is text+F + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+k <== + this is text+k + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+ko <== + this is text+ko + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+kx <== + this is text+kx + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+l <== + this is text+l + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+m <== + this is text+m + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+s <== + this is text+S + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+s2 <== + this is text+S2 + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+w <== + this is text+w + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_text+x <== + this is text+x + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_ubinary <== + this is ubinary + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_uxbinary <== + this is uxbinary + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_xbinary <== + this is xbinary + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_xltext <== + this is xltext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + + ==> dst/file_xtext <== + this is xtext + $Id$ + $Header$ + $Date$ + $DateTime$ + $Change$ + $File$ + $Revision$ + $Header$$Header$Header$ + +crazy_symlink + $ readlink crazy_symlink+k + target_$Header: //depot/test-mercurial-import/crazy_symlink+k#1 $ + $ readlink dst/crazy_symlink+k + target_$Header$ + +exit trap: + stopping the p4 server diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-p4.out --- a/tests/test-convert-p4.out Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -% create p4 depot -% start the p4 server -% create a client spec -Client hg-p4-import saved. -% populate the depot -//depot/test-mercurial-import/a#1 - opened for add -//depot/test-mercurial-import/b/c#1 - opened for add -Submitting change 1. -Locking 2 files ... -add //depot/test-mercurial-import/a#1 -add //depot/test-mercurial-import/b/c#1 -Change 1 submitted. -% change some files -//depot/test-mercurial-import/a#1 - opened for edit -Submitting change 2. -Locking 1 files ... -edit //depot/test-mercurial-import/a#2 -Change 2 submitted. -//depot/test-mercurial-import/b/c#1 - opened for edit -Submitting change 3. -Locking 1 files ... -edit //depot/test-mercurial-import/b/c#2 -Change 3 submitted. -% convert -initializing destination dst repository -reading p4 views -collecting p4 changelists -1 initial -2 change a -3 change b/c -scanning source... -sorting... -converting... -2 initial -1 change a -0 change b/c -rev=2 desc="change b/c" tags="tip" files="b/c" -rev=1 desc="change a" tags="" files="a" -rev=0 desc="initial" tags="" files="a b/c" -% change some files -//depot/test-mercurial-import/a#2 - opened for edit -//depot/test-mercurial-import/b/c#2 - opened for edit -Submitting change 4. -Locking 2 files ... -edit //depot/test-mercurial-import/a#3 -edit //depot/test-mercurial-import/b/c#3 -Change 4 submitted. -% convert again -reading p4 views -collecting p4 changelists -1 initial -2 change a -3 change b/c -4 change a b/c -scanning source... -sorting... -converting... -0 change a b/c -rev=3 desc="change a b/c" tags="tip" files="a b/c" -rev=2 desc="change b/c" tags="" files="b/c" -rev=1 desc="change a" tags="" files="a" -rev=0 desc="initial" tags="" files="a b/c" -% interesting names -//depot/test-mercurial-import/d d#1 - opened for add -//depot/test-mercurial-import/ e/ f#1 - opened for add -Submitting change 5. -Locking 2 files ... -add //depot/test-mercurial-import/ e/ f#1 -add //depot/test-mercurial-import/d d#1 -Change 5 submitted. -% convert again -reading p4 views -collecting p4 changelists -1 initial -2 change a -3 change b/c -4 change a b/c -5 add d e f -scanning source... -sorting... -converting... -0 add d e f -rev=4 desc="add d e f" tags="tip" files=" e/ f d d" -rev=3 desc="change a b/c" tags="" files="a b/c" -rev=2 desc="change b/c" tags="" files="b/c" -rev=1 desc="change a" tags="" files="a" -rev=0 desc="initial" tags="" files="a b/c" -% stop the p4 server diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-p4.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-convert-p4.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,152 @@ + $ "$TESTDIR/hghave" p4 || exit 80 + + $ echo "[extensions]" >> $HGRCPATH + $ echo "convert = " >> $HGRCPATH + +create p4 depot + $ P4ROOT=`pwd`/depot; export P4ROOT + $ P4AUDIT=$P4ROOT/audit; export P4AUDIT + $ P4JOURNAL=$P4ROOT/journal; export P4JOURNAL + $ P4LOG=$P4ROOT/log; export P4LOG + $ P4PORT=localhost:16661; export P4PORT + $ P4DEBUG=1; export P4DEBUG + +start the p4 server + $ [ ! -d $P4ROOT ] && mkdir $P4ROOT + $ p4d -f -J off >$P4ROOT/stdout 2>$P4ROOT/stderr & + $ echo $! >> $DAEMON_PIDS + $ trap "echo stopping the p4 server ; p4 admin stop" EXIT + + $ # wait for the server to initialize + $ while ! p4 ; do + > sleep 1 + > done >/dev/null 2>/dev/null + +create a client spec + $ P4CLIENT=hg-p4-import; export P4CLIENT + $ DEPOTPATH=//depot/test-mercurial-import/... + $ p4 client -o | sed '/^View:/,$ d' >p4client + $ echo View: >>p4client + $ echo " $DEPOTPATH //$P4CLIENT/..." >>p4client + $ p4 client -i a + $ mkdir b + $ echo c > b/c + $ p4 add a b/c + //depot/test-mercurial-import/a#1 - opened for add + //depot/test-mercurial-import/b/c#1 - opened for add + $ p4 submit -d initial + Submitting change 1. + Locking 2 files ... + add //depot/test-mercurial-import/a#1 + add //depot/test-mercurial-import/b/c#1 + Change 1 submitted. + +change some files + $ p4 edit a + //depot/test-mercurial-import/a#1 - opened for edit + $ echo aa >> a + $ p4 submit -d "change a" + Submitting change 2. + Locking 1 files ... + edit //depot/test-mercurial-import/a#2 + Change 2 submitted. + + $ p4 edit b/c + //depot/test-mercurial-import/b/c#1 - opened for edit + $ echo cc >> b/c + $ p4 submit -d "change b/c" + Submitting change 3. + Locking 1 files ... + edit //depot/test-mercurial-import/b/c#2 + Change 3 submitted. + +convert + $ hg convert -s p4 $DEPOTPATH dst + initializing destination dst repository + reading p4 views + collecting p4 changelists + 1 initial + 2 change a + 3 change b/c + scanning source... + sorting... + converting... + 2 initial + 1 change a + 0 change b/c + $ hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' + rev=2 desc="change b/c" tags="tip" files="b/c" + rev=1 desc="change a" tags="" files="a" + rev=0 desc="initial" tags="" files="a b/c" + +change some files + $ p4 edit a b/c + //depot/test-mercurial-import/a#2 - opened for edit + //depot/test-mercurial-import/b/c#2 - opened for edit + $ echo aaa >> a + $ echo ccc >> b/c + $ p4 submit -d "change a b/c" + Submitting change 4. + Locking 2 files ... + edit //depot/test-mercurial-import/a#3 + edit //depot/test-mercurial-import/b/c#3 + Change 4 submitted. + +convert again + $ hg convert -s p4 $DEPOTPATH dst + reading p4 views + collecting p4 changelists + 1 initial + 2 change a + 3 change b/c + 4 change a b/c + scanning source... + sorting... + converting... + 0 change a b/c + $ hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' + rev=3 desc="change a b/c" tags="tip" files="a b/c" + rev=2 desc="change b/c" tags="" files="b/c" + rev=1 desc="change a" tags="" files="a" + rev=0 desc="initial" tags="" files="a b/c" + +interesting names + $ echo dddd > "d d" + $ mkdir " e" + $ echo fff >" e/ f" + $ p4 add "d d" " e/ f" + //depot/test-mercurial-import/d d#1 - opened for add + //depot/test-mercurial-import/ e/ f#1 - opened for add + $ p4 submit -d "add d e f" + Submitting change 5. + Locking 2 files ... + add //depot/test-mercurial-import/ e/ f#1 + add //depot/test-mercurial-import/d d#1 + Change 5 submitted. + +convert again + $ hg convert -s p4 $DEPOTPATH dst + reading p4 views + collecting p4 changelists + 1 initial + 2 change a + 3 change b/c + 4 change a b/c + 5 add d e f + scanning source... + sorting... + converting... + 0 add d e f + $ hg -R dst log --template 'rev={rev} desc="{desc}" tags="{tags}" files="{files}"\n' + rev=4 desc="add d e f" tags="tip" files=" e/ f d d" + rev=3 desc="change a b/c" tags="" files="a b/c" + rev=2 desc="change b/c" tags="" files="b/c" + rev=1 desc="change a" tags="" files="a" + rev=0 desc="initial" tags="" files="a b/c" + +exit trap: + stopping the p4 server diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-svn-move.t --- a/tests/test-convert-svn-move.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-svn-move.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,10 +1,6 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ fixpath() - > { - > tr '\\' / - > } $ cat >> $HGRCPATH < [extensions] > convert = @@ -13,20 +9,16 @@ $ svnadmin create svn-repo $ svnadmin load -q svn-repo < "$TESTDIR/svn/move.svndump" - $ svnpath=`pwd | fixpath` - -SVN wants all paths to start with a slash. Unfortunately, -Windows ones don't. Handle that. - - $ expr "$svnpath" : "\/" > /dev/null - > if [ $? -ne 0 ]; then - > svnpath="/$svnpath" - > fi - > svnurl="file://$svnpath/svn-repo" + $ SVNREPOPATH=`pwd`/svn-repo +#if windows + $ SVNREPOURL=file:///`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"` +#else + $ SVNREPOURL=file://`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"` +#endif Convert trunk and branches - $ hg convert --datesort "$svnurl"/subproject A-hg + $ hg convert --datesort "$SVNREPOURL"/subproject A-hg initializing destination A-hg repository scanning source... sorting... @@ -247,3 +239,5 @@ 1 branch 0 clobberdir + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-svn-sink.t --- a/tests/test-convert-svn-sink.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-svn-sink.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,15 +1,11 @@ - $ "$TESTDIR/hghave" svn13 no-outer-repo symlink execbit || exit 80 + $ "$TESTDIR/hghave" svn13 || exit 80 - $ fixpath() - > { - > tr '\\' / - > } $ svnupanddisplay() > { > ( > cd $1; > svn up -q; - > svn st -v | fixpath | sed 's/ */ /g' | sort + > svn st -v | sed 's/ */ /g' | sort > limit='' > if [ $2 -gt 0 ]; then > limit="--limit=$2" @@ -31,18 +27,16 @@ $ echo a > a/a $ mkdir -p a/d1/d2 $ echo b > a/d1/d2/b - $ ln -s a/missing a/link $ hg --cwd a ci -d '0 0' -A -m 'add a file' adding a adding d1/d2/b - adding link Modify $ "$TESTDIR/svn-safe-append.py" a a/a $ hg --cwd a ci -d '1 0' -m 'modify a file' $ hg --cwd a tip -q - 1:8231f652da37 + 1:e0e2b8a9156b $ hg convert -d svn a assuming destination a-hg @@ -55,9 +49,8 @@ 0 modify a file $ svnupanddisplay a-hg-wc 2 2 1 test d1 - 2 1 test d1/d2 - 2 1 test d1/d2/b - 2 1 test link + 2 1 test d1/d2 (glob) + 2 1 test d1/d2/b (glob) 2 2 test . 2 2 test a revision: 2 @@ -71,27 +64,22 @@ A /d1 A /d1/d2 A /d1/d2/b - A /link $ ls a a-hg-wc a: a d1 - link a-hg-wc: a d1 - link $ cmp a/a a-hg-wc/a Rename $ hg --cwd a mv a b - $ hg --cwd a mv link newlink - $ hg --cwd a ci -d '2 0' -m 'rename a file' $ hg --cwd a tip -q - 2:a67e26ccec09 + 2:eb5169441d43 $ hg convert -d svn a assuming destination a-hg @@ -102,28 +90,23 @@ 0 rename a file $ svnupanddisplay a-hg-wc 1 3 1 test d1 - 3 1 test d1/d2 - 3 1 test d1/d2/b + 3 1 test d1/d2 (glob) + 3 1 test d1/d2/b (glob) 3 3 test . 3 3 test b - 3 3 test newlink revision: 3 author: test msg: rename a file D /a A /b (from /a@2) - D /link - A /newlink (from /link@2) $ ls a a-hg-wc a: b d1 - newlink a-hg-wc: b d1 - newlink Copy @@ -131,7 +114,7 @@ $ hg --cwd a ci -d '3 0' -m 'copy a file' $ hg --cwd a tip -q - 3:0cf087b9ab02 + 3:60effef6ab48 $ hg convert -d svn a assuming destination a-hg @@ -142,10 +125,9 @@ 0 copy a file $ svnupanddisplay a-hg-wc 1 4 1 test d1 - 4 1 test d1/d2 - 4 1 test d1/d2/b + 4 1 test d1/d2 (glob) + 4 1 test d1/d2/b (glob) 4 3 test b - 4 3 test newlink 4 4 test . 4 4 test c revision: 4 @@ -157,13 +139,11 @@ b c d1 - newlink a-hg-wc: b c d1 - newlink $ hg --cwd a rm b @@ -171,7 +151,7 @@ $ hg --cwd a ci -d '4 0' -m 'remove a file' $ hg --cwd a tip -q - 4:07b2e34a5b17 + 4:87bbe3013fb6 $ hg convert -d svn a assuming destination a-hg @@ -182,9 +162,8 @@ 0 remove a file $ svnupanddisplay a-hg-wc 1 5 1 test d1 - 5 1 test d1/d2 - 5 1 test d1/d2/b - 5 3 test newlink + 5 1 test d1/d2 (glob) + 5 1 test d1/d2/b (glob) 5 4 test c 5 5 test . revision: 5 @@ -195,19 +174,26 @@ a: c d1 - newlink a-hg-wc: c d1 - newlink -Exectutable +Executable +#if execbit $ chmod +x a/c +#else + $ echo fake >> a/c +#endif $ hg --cwd a ci -d '5 0' -m 'make a file executable' +#if execbit $ hg --cwd a tip -q - 5:31093672760b + 5:ff42e473c340 +#else + $ hg --cwd a tip -q + 5:817a700c8cf1 +#endif $ hg convert -d svn a assuming destination a-hg @@ -218,25 +204,64 @@ 0 make a file executable $ svnupanddisplay a-hg-wc 1 6 1 test d1 - 6 1 test d1/d2 - 6 1 test d1/d2/b - 6 3 test newlink + 6 1 test d1/d2 (glob) + 6 1 test d1/d2/b (glob) 6 6 test . 6 6 test c revision: 6 author: test msg: make a file executable M /c +#if execbit $ test -x a-hg-wc/c +#endif + +#if symlink + +Symlinks + + $ ln -s a/missing a/link + $ hg --cwd a commit -Am 'add symlink' + adding link + $ hg --cwd a mv link newlink + $ hg --cwd a commit -m 'move symlink' + $ hg convert -d svn a + assuming destination a-hg + initializing svn working copy 'a-hg-wc' + scanning source... + sorting... + converting... + 1 add symlink + 0 move symlink + $ svnupanddisplay a-hg-wc 1 + 8 1 test d1 + 8 1 test d1/d2 + 8 1 test d1/d2/b + 8 6 test c + 8 8 test . + 8 8 test newlink + revision: 8 + author: test + msg: move symlink + D /link + A /newlink (from /link@7) + +#endif + + $ rm -rf a a-hg a-hg-wc + Executable in new directory - $ rm -rf a a-hg a-hg-wc $ hg init a $ mkdir a/d1 $ echo a > a/d1/a +#if execbit $ chmod +x a/d1/a +#else + $ echo fake >> a/d1/a +#endif $ hg --cwd a ci -d '0 0' -A -m 'add executable file in new directory' adding d1/a @@ -251,13 +276,15 @@ $ svnupanddisplay a-hg-wc 1 1 1 test . 1 1 test d1 - 1 1 test d1/a + 1 1 test d1/a (glob) revision: 1 author: test msg: add executable file in new directory A /d1 A /d1/a +#if execbit $ test -x a-hg-wc/d1/a +#endif Copy to new directory @@ -274,10 +301,10 @@ 0 copy file to new directory $ svnupanddisplay a-hg-wc 1 2 1 test d1 - 2 1 test d1/a + 2 1 test d1/a (glob) 2 2 test . 2 2 test d2 - 2 2 test d2/a + 2 2 test d2/a (glob) revision: 2 author: test msg: copy file to new directory @@ -325,7 +352,7 @@ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] $ hg --cwd b revert -r 2 b - $ hg resolve -m b + $ hg --cwd b resolve -m b $ hg --cwd b ci -d '5 0' -m 'merge' Expect 4 changes diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-svn-source.t --- a/tests/test-convert-svn-source.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-svn-source.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,10 +1,6 @@ $ "$TESTDIR/hghave" svn svn-bindings || exit 80 - $ fixpath() - > { - > tr '\\' / - > } $ cat >> $HGRCPATH < [extensions] > convert = @@ -14,14 +10,12 @@ > EOF $ svnadmin create svn-repo - $ svnpath=`pwd | fixpath` - - - $ expr "$svnpath" : "\/" > /dev/null - > if [ $? -ne 0 ]; then - > svnpath="/$svnpath" - > fi - > svnurl="file://$svnpath/svn-repo" + $ SVNREPOPATH=`pwd`/svn-repo +#if windows + $ SVNREPOURL=file:///`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"` +#else + $ SVNREPOURL=file://`python -c "import urllib, sys; sys.stdout.write(urllib.quote(sys.argv[1]))" "$SVNREPOPATH"` +#endif Now test that it works with trunk/tags layout, but no branches yet. @@ -33,16 +27,15 @@ $ mkdir tags $ cd .. - $ svnurl="file://$svnpath/svn-repo/proj%20B" - $ svn import -m "init projB" projB "$svnurl" | fixpath | sort + $ svn import -m "init projB" projB "$SVNREPOURL/proj%20B" | sort - Adding projB/mytrunk - Adding projB/tags + Adding projB/mytrunk (glob) + Adding projB/tags (glob) Committed revision 1. Update svn repository - $ svn co "$svnurl"/mytrunk B | fixpath + $ svn co "$SVNREPOURL/proj%20B/mytrunk" B Checked out revision 1. $ cd B $ echo hello > 'letter .txt' @@ -59,7 +52,7 @@ Transmitting file data . Committed revision 3. - $ svn copy -m "tag v0.1" "$svnurl"/mytrunk "$svnurl"/tags/v0.1 + $ svn copy -m "tag v0.1" "$SVNREPOURL/proj%20B/mytrunk" "$SVNREPOURL/proj%20B/tags/v0.1" Committed revision 4. @@ -72,7 +65,7 @@ Convert to hg once - $ hg convert "$svnurl" B-hg + $ hg convert "$SVNREPOURL/proj%20B" B-hg initializing destination B-hg repository scanning source... sorting... @@ -96,7 +89,7 @@ Transmitting file data .. Committed revision 6. - $ svn copy -m "tag v0.2" "$svnurl"/mytrunk "$svnurl"/tags/v0.2 + $ svn copy -m "tag v0.2" "$SVNREPOURL/proj%20B/mytrunk" "$SVNREPOURL/proj%20B/tags/v0.2" Committed revision 7. @@ -107,7 +100,7 @@ Committed revision 8. $ cd .. - $ hg convert -s svn "$svnurl/non-existent-path" dest + $ hg convert -s svn "$SVNREPOURL/proj%20B/non-existent-path" dest initializing destination dest repository abort: no revision found in module /proj B/non-existent-path [255] @@ -116,7 +109,7 @@ Test incremental conversion - $ hg convert "$svnurl" B-hg + $ hg convert "$SVNREPOURL/proj%20B" B-hg scanning source... sorting... converting... @@ -150,7 +143,7 @@ Test filemap $ echo 'include letter2.txt' > filemap - $ hg convert --filemap filemap "$svnurl"/mytrunk fmap + $ hg convert --filemap filemap "$SVNREPOURL/proj%20B/mytrunk" fmap initializing destination fmap repository scanning source... sorting... @@ -170,7 +163,7 @@ Test stop revision - $ hg convert --rev 1 "$svnurl"/mytrunk stoprev + $ hg convert --rev 1 "$SVNREPOURL/proj%20B/mytrunk" stoprev initializing destination stoprev repository scanning source... sorting... @@ -200,7 +193,7 @@ converting... 1 init projA 0 adddir - $ hg --config convert.svn.trunk= convert file://$svnpath/svn-empty/trunk + $ hg --config convert.svn.trunk= convert "$SVNREPOURL/../svn-empty/trunk" assuming destination trunk-hg initializing destination trunk-hg repository scanning source... diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert-tagsbranch-topology.t --- a/tests/test-convert-tagsbranch-topology.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert-tagsbranch-topology.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,9 @@ $ "$TESTDIR/hghave" git || exit 80 + $ echo "[core]" >> $HOME/.gitconfig + $ echo "autocrlf = false" >> $HOME/.gitconfig + $ echo "[core]" >> $HOME/.gitconfig + $ echo "autocrlf = false" >> $HOME/.gitconfig $ echo "[extensions]" >> $HGRCPATH $ echo "convert=" >> $HGRCPATH $ echo 'hgext.graphlog =' >> $HGRCPATH @@ -82,3 +86,5 @@ / o 0 "rev1" files: a + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-convert.t --- a/tests/test-convert.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-convert.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - $ cat >> $HGRCPATH < [extensions] > convert= @@ -293,24 +291,27 @@ pulling from ../a searching for changes no changes found - $ touch bogusfile -should fail +conversion to existing file should fail + $ touch bogusfile $ hg convert a bogusfile initializing destination bogusfile repository abort: cannot create new bundle repository [255] + +#if unix-permissions + +conversion to dir without permissions should fail + $ mkdir bogusdir $ chmod 000 bogusdir -should fail - $ hg convert a bogusdir abort: Permission denied: bogusdir [255] -should succeed +user permissions should succeed $ chmod 700 bogusdir $ hg convert a bogusdir @@ -324,6 +325,8 @@ 1 d 0 e +#endif + test pre and post conversion actions $ echo 'include b' > filemap @@ -393,3 +396,52 @@ $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository [255] + +test revset converted() lookup + + $ hg --config convert.hg.saverev=True convert a c + initializing destination c repository + scanning source... + sorting... + converting... + 4 a + 3 b + 2 c + 1 d + 0 e + $ echo f > c/f + $ hg -R c ci -d'0 0' -Amf + adding f + created new head + $ hg -R c log -r "converted(09d945a62ce6)" + changeset: 1:98c3dd46a874 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b + + $ hg -R c log -r "converted()" + changeset: 0:31ed57b2037c + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + changeset: 1:98c3dd46a874 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b + + changeset: 2:3b9ca06ef716 + user: test + date: Thu Jan 01 00:00:02 1970 +0000 + summary: c + + changeset: 3:4e0debd37cf2 + user: test + date: Thu Jan 01 00:00:03 1970 +0000 + summary: d + + changeset: 4:9de3bc9349c5 + user: test + date: Thu Jan 01 00:00:04 1970 +0000 + summary: e + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-copy-move-merge.t --- a/tests/test-copy-move-merge.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-copy-move-merge.t Wed Jul 18 19:08:25 2012 -0500 @@ -24,7 +24,7 @@ unmatched files in other: b c - all copies found (* = to merge, ! = divergent): + all copies found (* = to merge, ! = divergent, % = renamed and deleted): c -> a * b -> a * checking for directory renames @@ -60,3 +60,5 @@ 0 1 2 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-copy.t --- a/tests/test-copy.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-copy.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,3 +1,7 @@ + + $ mkdir part1 + $ cd part1 + $ hg init $ echo a > a $ hg add a @@ -53,8 +57,8 @@ this should show a revision linked to changeset 0 $ hg debugindex a - rev offset length base linkrev nodeid p1 p2 - 0 0 3 0 0 b789fdd96dc2 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 3 ..... 0 b789fdd96dc2 000000000000 000000000000 (re) we should see one log entry for b @@ -69,8 +73,8 @@ this should show a revision linked to changeset 1 $ hg debugindex b - rev offset length base linkrev nodeid p1 p2 - 0 0 65 0 1 37d9b5d994ea 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 65 ..... 1 37d9b5d994ea 000000000000 000000000000 (re) this should show the rename information in the metadata @@ -92,3 +96,121 @@ crosschecking files in changesets and manifests checking files 2 files, 2 changesets, 2 total revisions + + $ cd .. + + + $ mkdir part2 + $ cd part2 + + $ hg init + $ echo foo > foo +should fail - foo is not managed + $ hg mv foo bar + foo: not copying - file is not managed + abort: no files to copy + [255] + $ hg st -A + ? foo + $ hg add foo +dry-run; print a warning that this is not a real copy; foo is added + $ hg mv --dry-run foo bar + foo has not been committed yet, so no copy data will be stored for bar. + $ hg st -A + A foo +should print a warning that this is not a real copy; bar is added + $ hg mv foo bar + foo has not been committed yet, so no copy data will be stored for bar. + $ hg st -A + A bar +should print a warning that this is not a real copy; foo is added + $ hg cp bar foo + bar has not been committed yet, so no copy data will be stored for foo. + $ hg rm -f bar + $ rm bar + $ hg st -A + A foo + $ hg commit -m1 + +moving a missing file + $ rm foo + $ hg mv foo foo3 + foo: deleted in working copy + foo3 does not exist! + $ hg up -qC . + +copy --after to a nonexistant target filename + $ hg cp -A foo dummy + foo: not recording copy - dummy does not exist + +dry-run; should show that foo is clean + $ hg copy --dry-run foo bar + $ hg st -A + C foo +should show copy + $ hg copy foo bar + $ hg st -C + A bar + foo + +shouldn't show copy + $ hg commit -m2 + $ hg st -C + +should match + $ hg debugindex foo + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re) + $ hg debugrename bar + bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd + + $ echo bleah > foo + $ echo quux > bar + $ hg commit -m3 + +should not be renamed + $ hg debugrename bar + bar not renamed + + $ hg copy -f foo bar +should show copy + $ hg st -C + M bar + foo + $ hg commit -m3 + +should show no parents for tip + $ hg debugindex bar + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 69 ..... 1 7711d36246cc 000000000000 000000000000 (re) + 1 69 6 ..... 2 bdf70a2b8d03 7711d36246cc 000000000000 (re) + 2 75 81 ..... 3 b2558327ea8d 000000000000 000000000000 (re) +should match + $ hg debugindex foo + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re) + 1 5 7 ..... 2 dd12c926cf16 2ed2a3912a0b 000000000000 (re) + $ hg debugrename bar + bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17 + +should show no copies + $ hg st -C + +copy --after on an added file + $ cp bar baz + $ hg add baz + $ hg cp -A bar baz + $ hg st -C + A baz + bar + +foo was clean: + $ hg st -AC foo + C foo +but it's considered modified after a copy --after --force + $ hg copy -Af bar foo + $ hg st -AC foo + M foo + bar + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-copy2.t --- a/tests/test-copy2.t Thu Jul 19 00:53:27 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ - $ hg init - $ echo foo > foo -should fail - foo is not managed - $ hg mv foo bar - foo: not copying - file is not managed - abort: no files to copy - [255] - $ hg st -A - ? foo - $ hg add foo -dry-run; print a warning that this is not a real copy; foo is added - $ hg mv --dry-run foo bar - foo has not been committed yet, so no copy data will be stored for bar. - $ hg st -A - A foo -should print a warning that this is not a real copy; bar is added - $ hg mv foo bar - foo has not been committed yet, so no copy data will be stored for bar. - $ hg st -A - A bar -should print a warning that this is not a real copy; foo is added - $ hg cp bar foo - bar has not been committed yet, so no copy data will be stored for foo. - $ hg rm -f bar - $ rm bar - $ hg st -A - A foo - $ hg commit -m1 - -moving a missing file - $ rm foo - $ hg mv foo foo3 - foo: deleted in working copy - foo3 does not exist! - $ hg up -qC . - -copy --after to a nonexistant target filename - $ hg cp -A foo dummy - foo: not recording copy - dummy does not exist - -dry-run; should show that foo is clean - $ hg copy --dry-run foo bar - $ hg st -A - C foo -should show copy - $ hg copy foo bar - $ hg st -C - A bar - foo - -shouldn't show copy - $ hg commit -m2 - $ hg st -C - -should match - $ hg debugindex foo - rev offset length base linkrev nodeid p1 p2 - 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000 - $ hg debugrename bar - bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd - - $ echo bleah > foo - $ echo quux > bar - $ hg commit -m3 - -should not be renamed - $ hg debugrename bar - bar not renamed - - $ hg copy -f foo bar -should show copy - $ hg st -C - M bar - foo - $ hg commit -m3 - -should show no parents for tip - $ hg debugindex bar - rev offset length base linkrev nodeid p1 p2 - 0 0 69 0 1 7711d36246cc 000000000000 000000000000 - 1 69 6 1 2 bdf70a2b8d03 7711d36246cc 000000000000 - 2 75 81 1 3 b2558327ea8d 000000000000 000000000000 -should match - $ hg debugindex foo - rev offset length base linkrev nodeid p1 p2 - 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000 - 1 5 7 1 2 dd12c926cf16 2ed2a3912a0b 000000000000 - $ hg debugrename bar - bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17 - -should show no copies - $ hg st -C - -copy --after on an added file - $ cp bar baz - $ hg add baz - $ hg cp -A bar baz - $ hg st -C - A baz - bar - -foo was clean: - $ hg st -AC foo - C foo -but it's considered modified after a copy --after --force - $ hg copy -Af bar foo - $ hg st -AC foo - M foo - bar diff -r 98823bd0d697 -r a06e2681dd17 tests/test-debugbundle.t --- a/tests/test-debugbundle.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-debugbundle.t Wed Jul 18 19:08:25 2012 -0500 @@ -34,3 +34,4 @@ c b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 12 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-debugcommands.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-debugcommands.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,25 @@ + $ hg init debugrevlog + $ cd debugrevlog + $ echo a > a + $ hg ci -Am adda + adding a + $ hg debugrevlog -m + format : 1 + flags : inline + + revisions : 1 + merges : 0 ( 0.00%) + normal : 1 (100.00%) + revisions : 1 + full : 1 (100.00%) + deltas : 0 ( 0.00%) + revision size : 44 + full : 44 (100.00%) + deltas : 0 ( 0.00%) + + avg chain length : 0 + compression ratio : 0 + + uncompressed data size (min/max/avg) : 43 / 43 / 43 + full revision size (min/max/avg) : 44 / 44 / 44 + delta size (min/max/avg) : 0 / 0 / 0 diff -r 98823bd0d697 -r a06e2681dd17 tests/test-debugcomplete.t --- a/tests/test-debugcomplete.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-debugcomplete.t Wed Jul 18 19:08:25 2012 -0500 @@ -86,6 +86,7 @@ debugindexdot debuginstall debugknown + debugobsolete debugpushkey debugpvec debugrebuildstate @@ -198,7 +199,7 @@ export: output, switch-parent, rev, text, git, nodates forget: include, exclude init: ssh, remotecmd, insecure - log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, style, template, include, exclude + log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, graph, style, template, include, exclude merge: force, rev, preview, tool phase: public, draft, secret, force, rev pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure @@ -236,6 +237,7 @@ debugindexdot: debuginstall: debugknown: + debugobsolete: date, user debugpushkey: debugpvec: debugrebuildstate: rev @@ -247,16 +249,16 @@ debugsub: rev debugwalk: include, exclude debugwireargs: three, four, five, ssh, remotecmd, insecure - graft: continue, edit, currentdate, currentuser, date, user, tool, dry-run + graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude heads: rev, topo, active, closed, style, template - help: extension, command + help: extension, command, keyword identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure import: strip, base, edit, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity - incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos + incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos locate: rev, print0, fullpath, include, exclude manifest: rev, all - outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos + outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos parents: rev, style, template paths: recover: diff -r 98823bd0d697 -r a06e2681dd17 tests/test-debugindexdot.t --- a/tests/test-debugindexdot.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-debugindexdot.t Wed Jul 18 19:08:25 2012 -0500 @@ -21,3 +21,5 @@ 2 -> 3 1 -> 3 } + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diff-binary-file.t --- a/tests/test-diff-binary-file.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diff-binary-file.t Wed Jul 18 19:08:25 2012 -0500 @@ -37,3 +37,4 @@ $ hg diff --git -r 0 -r 2 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diff-change.t --- a/tests/test-diff-change.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diff-change.t Wed Jul 18 19:08:25 2012 -0500 @@ -90,3 +90,4 @@ 9 10 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diff-color.t --- a/tests/test-diff-color.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diff-color.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" execbit || exit 80 - Setup $ echo "[color]" >> $HGRCPATH @@ -74,6 +72,8 @@ $ echo "[diff]" >> $HGRCPATH $ echo "git=True" >> $HGRCPATH +#if execbit + record $ chmod +x a @@ -124,3 +124,7 @@ a c \x1b[0;33mrecord this change to 'a'? [Ynesfdaq?]\x1b[0m (esc) + +#endif + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diff-hashes.t --- a/tests/test-diff-hashes.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diff-hashes.t Wed Jul 18 19:08:25 2012 -0500 @@ -43,3 +43,4 @@ -bar +foobar + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diff-subdir.t --- a/tests/test-diff-subdir.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diff-subdir.t Wed Jul 18 19:08:25 2012 -0500 @@ -44,3 +44,4 @@ @@ -0,0 +1,1 @@ +2 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diff-unified.t --- a/tests/test-diff-unified.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diff-unified.t Wed Jul 18 19:08:25 2012 -0500 @@ -89,6 +89,9 @@ abort: diff context lines count must be an integer, not 'foo' [255] + $ cd .. + + 0 lines of context hunk header matches gnu diff hunk header $ hg init diffzero @@ -191,3 +194,5 @@ @@ -1,1 +1,1 @@ -b +a + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-diffstat.t --- a/tests/test-diffstat.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-diffstat.t Wed Jul 18 19:08:25 2012 -0500 @@ -69,3 +69,4 @@ file with spaces | Bin 1 files changed, 0 insertions(+), 0 deletions(-) + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-dirstate.t --- a/tests/test-dirstate.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-dirstate.t Wed Jul 18 19:08:25 2012 -0500 @@ -14,6 +14,21 @@ moving a/b/c/d/x to z/b/c/d/x (glob) moving a/b/c/d/y to z/b/c/d/y (glob) moving a/b/c/d/z to z/b/c/d/z (glob) + +Test name collisions + + $ rm z/b/c/d/x + $ mkdir z/b/c/d/x + $ touch z/b/c/d/x/y + $ hg add z/b/c/d/x/y + abort: file 'z/b/c/d/x' in dirstate clashes with 'z/b/c/d/x/y' + [255] + $ rm -rf z/b/c/d + $ touch z/b/c/d + $ hg add z/b/c/d + abort: directory 'z/b/c/d' already in dirstate + [255] + $ cd .. Issue1790: dirstate entry locked into unset if file mtime is set into diff -r 98823bd0d697 -r a06e2681dd17 tests/test-dispatch.t --- a/tests/test-dispatch.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-dispatch.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,9 +1,5 @@ test command parsing and dispatch - $ "$TESTDIR/hghave" no-outer-repo || exit 80 - - $ dir=`pwd` - $ hg init a $ cd a @@ -48,6 +44,10 @@ a: no such file in rev 000000000000 [1] + $ cd "$TESTTMP" + +#if no-outer-repo + No repo: $ cd $dir @@ -55,3 +55,4 @@ abort: no repository found in '$TESTTMP' (.hg not found)! [255] +#endif diff -r 98823bd0d697 -r a06e2681dd17 tests/test-double-merge.t --- a/tests/test-double-merge.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-double-merge.t Wed Jul 18 19:08:25 2012 -0500 @@ -29,7 +29,7 @@ searching for copies back to rev 1 unmatched files in other: bar - all copies found (* = to merge, ! = divergent): + all copies found (* = to merge, ! = divergent, % = renamed and deleted): bar -> foo * checking for directory renames resolving manifests @@ -63,3 +63,5 @@ line 0 line 1 line 2-2 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-empty-file.t --- a/tests/test-empty-file.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-empty-file.t Wed Jul 18 19:08:25 2012 -0500 @@ -44,3 +44,4 @@ b80de5d138758541c5f05265ad144ab9fa86d1db 644 empty2 b80de5d138758541c5f05265ad144ab9fa86d1db 644 empty3 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-empty.t --- a/tests/test-empty.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-empty.t Wed Jul 18 19:08:25 2012 -0500 @@ -51,3 +51,5 @@ Should be empty: $ ls .hg/store + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-encode.t --- a/tests/test-encode.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-encode.t Wed Jul 18 19:08:25 2012 -0500 @@ -59,3 +59,5 @@ this is a test $ hg -R .. cat --decode ../a.gz | gunzip this is a test + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-encoding-align.t --- a/tests/test-encoding-align.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-encoding-align.t Wed Jul 18 19:08:25 2012 -0500 @@ -141,3 +141,5 @@ \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d 4:9259be597f19 (esc) MIDDLE_ 3:b06c5b6def9e \xe7\x9f\xad\xe5\x90\x8d 2:64a70663cee8 (esc) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-encoding-textwrap.t --- a/tests/test-encoding-textwrap.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-encoding-textwrap.t Wed Jul 18 19:08:25 2012 -0500 @@ -255,3 +255,5 @@ \xd0\x9d\xd0\xb0\xd1\x81\xd1\x82\xd1\x80\xd0\xbe\xd0\xb9\xd0\xba\xd0\xb8 (esc) use "hg -v help show_ambig_ru" to show more info + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-encoding.t --- a/tests/test-encoding.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-encoding.t Wed Jul 18 19:08:25 2012 -0500 @@ -249,3 +249,4 @@ $ HGENCODING=latin-1 hg up `cat latin-1-tag` 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-eol-clone.t --- a/tests/test-eol-clone.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-eol-clone.t Wed Jul 18 19:08:25 2012 -0500 @@ -72,3 +72,5 @@ first\r (esc) second\r (esc) third\r (esc) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-eol-hook.t --- a/tests/test-eol-hook.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-eol-hook.t Wed Jul 18 19:08:25 2012 -0500 @@ -214,3 +214,5 @@ d.txt in a7040e68714f should not have CRLF line endings b.txt in fbcf9b1025f5 should not have CRLF line endings [255] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-eol-tag.t --- a/tests/test-eol-tag.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-eol-tag.t Wed Jul 18 19:08:25 2012 -0500 @@ -35,3 +35,5 @@ Touch .hgtags file again: $ hg tag 2.0 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-eol.t --- a/tests/test-eol.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-eol.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - Test EOL extension $ cat >> $HGRCPATH <> $HGRCPATH + $ echo 'eol =' >> $HGRCPATH + +#if unix-permissions + Test issue2569 -- eol extension takes write lock on reading: - $ echo '[extensions]' >> $HGRCPATH - $ echo 'eol =' >> $HGRCPATH $ hg init repo $ cd repo $ touch .hgeol @@ -403,6 +404,8 @@ $ chmod -R u+w .hg $ cd .. +#endif + Test cleverencode: and cleverdecode: aliases for win32text extension $ echo '[encode]' >> $HGRCPATH @@ -522,3 +525,4 @@ fourth fifth + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-eolfilename.t --- a/tests/test-eolfilename.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-eolfilename.t Wed Jul 18 19:08:25 2012 -0500 @@ -68,3 +68,5 @@ \x1b[0;35;1;4mbar\x1b[0m (esc) \x1b[0;35;1;4m? foo\x1b[0m (esc) \x1b[0;35;1;4mbar.baz\x1b[0m (esc) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-excessive-merge.t --- a/tests/test-excessive-merge.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-excessive-merge.t Wed Jul 18 19:08:25 2012 -0500 @@ -64,12 +64,12 @@ summary: test $ hg debugindex --changelog - rev offset length base linkrev nodeid p1 p2 - 0 0 60 0 0 5e0375449e74 000000000000 000000000000 - 1 60 62 1 1 96155394af80 5e0375449e74 000000000000 - 2 122 62 2 2 92cc4c306b19 5e0375449e74 000000000000 - 3 184 69 3 3 e16a66a37edd 92cc4c306b19 96155394af80 - 4 253 29 3 4 2ee31f665a86 96155394af80 92cc4c306b19 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 60 ..... 0 5e0375449e74 000000000000 000000000000 (re) + 1 60 62 ..... 1 96155394af80 5e0375449e74 000000000000 (re) + 2 122 62 ..... 2 92cc4c306b19 5e0375449e74 000000000000 (re) + 3 184 69 ..... 3 e16a66a37edd 92cc4c306b19 96155394af80 (re) + 4 253 29 ..... 4 2ee31f665a86 96155394af80 92cc4c306b19 (re) revision 1 $ hg manifest --debug 1 @@ -89,9 +89,9 @@ 79d7492df40aa0fa093ec4209be78043c181f094 644 b $ hg debugindex a - rev offset length base linkrev nodeid p1 p2 - 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000 - 1 5 6 1 1 79d7492df40a 2ed2a3912a0b 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re) + 1 5 6 ..... 1 79d7492df40a 2ed2a3912a0b 000000000000 (re) $ hg verify checking changesets diff -r 98823bd0d697 -r a06e2681dd17 tests/test-export.t --- a/tests/test-export.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-export.t Wed Jul 18 19:08:25 2012 -0500 @@ -143,3 +143,5 @@ $ hg export "not all()" abort: export requires at least one changeset [255] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-extdiff.t --- a/tests/test-extdiff.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-extdiff.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" symlink execbit || exit 80 - $ echo "[extensions]" >> $HGRCPATH $ echo "extdiff=" >> $HGRCPATH @@ -94,6 +92,8 @@ diffing */extdiff.*/a.2a13a4d2da36/a a.46c0e4daeb72/a (glob) diff-like tools yield a non-zero exit code +#if execbit + Test extdiff of multiple files in tmp dir: $ hg update -C 0 > /dev/null @@ -182,6 +182,10 @@ $ cd .. +#endif + +#if symlink + Test symlinks handling (issue1909) $ hg init testsymlinks @@ -196,3 +200,5 @@ diffing testsymlinks.07f494440405 testsymlinks [1] $ cd .. + +#endif diff -r 98823bd0d697 -r a06e2681dd17 tests/test-extension.t --- a/tests/test-extension.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-extension.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,7 +1,5 @@ Test basic extension support - $ "$TESTDIR/hghave" no-outer-repo || exit 80 - $ cat > foobar.py < import os > from mercurial import commands @@ -132,6 +130,9 @@ $ cd .. +hide outer repo + $ hg init + $ cat > empty.py < '''empty cmdtable > ''' @@ -178,8 +179,7 @@ list of commands: - foo: - yet another foo command + foo yet another foo command global options: @@ -208,10 +208,8 @@ list of commands: - debugfoobar: - yet another debug command - foo: - yet another foo command + debugfoobar yet another debug command + foo yet another foo command global options: @@ -328,7 +326,7 @@ list of commands: - extdiff use external program to diff repository (or selected files) + extdiff use external program to diff repository (or selected files) use "hg -v help extdiff" to show builtin aliases and global options @@ -478,3 +476,60 @@ hg: unknown command 'foo' warning: error finding commands in $TESTTMP/hgext/forest.py (glob) [255] + + $ cat > throw.py < from mercurial import cmdutil, commands + > cmdtable = {} + > command = cmdutil.command(cmdtable) + > class Bogon(Exception): pass + > + > @command('throw', [], 'hg throw') + > def throw(ui, **opts): + > """throws an exception""" + > raise Bogon() + > commands.norepo += " throw" + > EOF +No declared supported version, extension complains: + $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' + ** Unknown exception encountered with possibly-broken third-party extension throw + ** which supports versions unknown of Mercurial. + ** Please disable throw and try your action again. + ** If that fixes the bug please report it to the extension author. + ** Python * (glob) + ** Mercurial Distributed SCM * (glob) + ** Extensions loaded: throw +If the extension specifies a buglink, show that: + $ echo 'buglink = "http://example.com/bts"' >> throw.py + $ rm -f throw.pyc throw.pyo + $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' + ** Unknown exception encountered with possibly-broken third-party extension throw + ** which supports versions unknown of Mercurial. + ** Please disable throw and try your action again. + ** If that fixes the bug please report it to http://example.com/bts + ** Python * (glob) + ** Mercurial Distributed SCM (*) (glob) + ** Extensions loaded: throw +If the extensions declare outdated versions, accuse the older extension first: + $ echo "testedwith = '1.9.3'" >> older.py + $ echo "testedwith = '2.1.1'" >> throw.py + $ rm -f throw.pyc throw.pyo + $ hg --config extensions.throw=throw.py --config extensions.older=older.py \ + > throw 2>&1 | egrep '^\*\*' + ** Unknown exception encountered with possibly-broken third-party extension older + ** which supports versions 1.9.3 of Mercurial. + ** Please disable older and try your action again. + ** If that fixes the bug please report it to the extension author. + ** Python * (glob) + ** Mercurial Distributed SCM (*) (glob) + ** Extensions loaded: throw, older + +Declare the version as supporting this hg version, show regular bts link: + $ hgver=`python -c 'from mercurial import util; print util.version().split("+")[0]'` + $ echo 'testedwith = """'"$hgver"'"""' >> throw.py + $ rm -f throw.pyc throw.pyo + $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*' + ** unknown exception encountered, please report by visiting + ** http://mercurial.selenic.com/wiki/BugTracker + ** Python * (glob) + ** Mercurial Distributed SCM (*) (glob) + ** Extensions loaded: throw diff -r 98823bd0d697 -r a06e2681dd17 tests/test-extra-filelog-entry.t --- a/tests/test-extra-filelog-entry.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-extra-filelog-entry.t Wed Jul 18 19:08:25 2012 -0500 @@ -16,6 +16,6 @@ $ hg qrefresh $ hg debugindex b - rev offset length base linkrev nodeid p1 p2 - 0 0 3 0 0 1e88685f5dde 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 3 ..... 0 1e88685f5dde 000000000000 000000000000 (re) diff -r 98823bd0d697 -r a06e2681dd17 tests/test-fetch.t --- a/tests/test-fetch.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-fetch.t Wed Jul 18 19:08:25 2012 -0500 @@ -410,3 +410,5 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-filebranch.t --- a/tests/test-filebranch.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-filebranch.t Wed Jul 18 19:08:25 2012 -0500 @@ -76,11 +76,11 @@ main: we should have a merge here: $ hg debugindex --changelog - rev offset length base linkrev nodeid p1 p2 - 0 0 73 0 0 cdca01651b96 000000000000 000000000000 - 1 73 68 1 1 f6718a9cb7f3 cdca01651b96 000000000000 - 2 141 68 2 2 bdd988058d16 cdca01651b96 000000000000 - 3 209 66 3 3 d8a521142a3c f6718a9cb7f3 bdd988058d16 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 73 ..... 0 cdca01651b96 000000000000 000000000000 (re) + 1 73 68 ..... 1 f6718a9cb7f3 cdca01651b96 000000000000 (re) + 2 141 68 ..... 2 bdd988058d16 cdca01651b96 000000000000 (re) + 3 209 66 ..... 3 d8a521142a3c f6718a9cb7f3 bdd988058d16 (re) log should show foo and quux changed: @@ -100,32 +100,32 @@ foo: we should have a merge here: $ hg debugindex foo - rev offset length base linkrev nodeid p1 p2 - 0 0 3 0 0 b8e02f643373 000000000000 000000000000 - 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000 - 2 7 4 2 2 33d1fb69067a b8e02f643373 000000000000 - 3 11 4 3 3 aa27919ee430 2ffeddde1b65 33d1fb69067a + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 3 ..... 0 b8e02f643373 000000000000 000000000000 (re) + 1 3 4 ..... 1 2ffeddde1b65 b8e02f643373 000000000000 (re) + 2 7 4 ..... 2 33d1fb69067a b8e02f643373 000000000000 (re) + 3 11 4 ..... 3 aa27919ee430 2ffeddde1b65 33d1fb69067a (re) bar: we should not have a merge here: $ hg debugindex bar - rev offset length base linkrev nodeid p1 p2 - 0 0 3 0 0 b8e02f643373 000000000000 000000000000 - 1 3 4 1 2 33d1fb69067a b8e02f643373 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 3 ..... 0 b8e02f643373 000000000000 000000000000 (re) + 1 3 4 ..... 2 33d1fb69067a b8e02f643373 000000000000 (re) baz: we should not have a merge here: $ hg debugindex baz - rev offset length base linkrev nodeid p1 p2 - 0 0 3 0 0 b8e02f643373 000000000000 000000000000 - 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 3 ..... 0 b8e02f643373 000000000000 000000000000 (re) + 1 3 4 ..... 1 2ffeddde1b65 b8e02f643373 000000000000 (re) quux: we should not have a merge here: $ hg debugindex quux - rev offset length base linkrev nodeid p1 p2 - 0 0 3 0 0 b8e02f643373 000000000000 000000000000 - 1 3 5 1 3 6128c0f33108 b8e02f643373 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 3 ..... 0 b8e02f643373 000000000000 000000000000 (re) + 1 3 5 ..... 3 6128c0f33108 b8e02f643373 000000000000 (re) Manifest entries should match tips of all files: @@ -146,3 +146,4 @@ checking files 4 files, 4 changesets, 10 total revisions + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-filecache.py --- a/tests/test-filecache.py Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-filecache.py Wed Jul 18 19:08:25 2012 -0500 @@ -1,6 +1,7 @@ import sys, os, subprocess -if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], 'cacheable']): +if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], + 'cacheable']): sys.exit(80) from mercurial import util, scmutil, extensions @@ -77,7 +78,7 @@ try: os.remove('x') - except: + except OSError: pass basic(fakerepo()) diff -r 98823bd0d697 -r a06e2681dd17 tests/test-flags.t --- a/tests/test-flags.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-flags.t Wed Jul 18 19:08:25 2012 -0500 @@ -138,12 +138,14 @@ -rwxr-x--- $ hg debugindex a - rev offset length base linkrev nodeid p1 p2 - 0 0 0 0 0 b80de5d13875 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 0 ..... 0 b80de5d13875 000000000000 000000000000 (re) $ hg debugindex -R ../test2 a - rev offset length base linkrev nodeid p1 p2 - 0 0 0 0 0 b80de5d13875 000000000000 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 0 ..... 0 b80de5d13875 000000000000 000000000000 (re) $ hg debugindex -R ../test1 a - rev offset length base linkrev nodeid p1 p2 - 0 0 0 0 0 b80de5d13875 000000000000 000000000000 - 1 0 5 1 1 7fe919cc0336 b80de5d13875 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 0 ..... 0 b80de5d13875 000000000000 000000000000 (re) + 1 0 5 ..... 1 7fe919cc0336 b80de5d13875 000000000000 (re) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-git-export.t --- a/tests/test-git-export.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-git-export.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" execbit || exit 80 - $ hg init $ echo start > start $ hg ci -Amstart @@ -58,6 +56,8 @@ $ hg ci -Amsrc adding src +#if execbit + chmod 644: $ chmod +x src @@ -94,6 +94,17 @@ old mode 100644 new mode 100755 +#else + +Dummy changes when no exec bit, mocking the execbit commit structure + + $ echo change >> src + $ hg ci -munexec + $ hg mv src dst + $ hg ci -mrenamemod + +#endif + Binary diff: $ cp "$TESTDIR/binfile.bin" . diff -r 98823bd0d697 -r a06e2681dd17 tests/test-globalopts.t --- a/tests/test-globalopts.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-globalopts.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" no-outer-repo || exit 80 - $ hg init a $ cd a $ echo a > a @@ -77,6 +75,8 @@ 8580ff50825a tip $ cd .. +#if no-outer-repo + Implicit -R: $ hg ann a/a @@ -93,6 +93,8 @@ abort: no repository found in '$TESTTMP' (.hg not found)! [255] +#endif + Abbreviation of long option: $ hg --repo c tip @@ -265,13 +267,16 @@ $ hg --cwd a --time id 8580ff50825a tip - Time: real * (glob) + time: real * (glob) Testing --version: $ hg --version -q Mercurial Distributed SCM * (glob) +hide outer repo + $ hg init + Testing -h/--help: $ hg -h diff -r 98823bd0d697 -r a06e2681dd17 tests/test-glog.t --- a/tests/test-glog.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-glog.t Wed Jul 18 19:08:25 2012 -0500 @@ -69,8 +69,6 @@ o (0) root - $ "$TESTDIR/hghave" no-outer-repo || exit 80 - $ commit() > { > rev=$1 @@ -84,13 +82,12 @@ > } $ cat > printrevset.py < from mercurial import extensions, revset, commands - > from hgext import graphlog + > from mercurial import extensions, revset, commands, cmdutil > > def uisetup(ui): > def printrevset(orig, ui, repo, *pats, **opts): > if opts.get('print_revset'): - > expr = graphlog.getlogrevs(repo, pats, opts)[1] + > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1] > if expr: > tree = revset.parse(expr)[0] > else: @@ -1136,8 +1133,11 @@ Empty revision range - display nothing: $ hg glog -r 1..0 + $ cd .. + +#if no-outer-repo + From outer space: - $ cd .. $ hg glog -l1 repo @ changeset: 34:fea3ac5810e0 | tag: tip @@ -1156,6 +1156,8 @@ | $ hg glog -l1 repo/missing +#endif + File log with revs != cset revs: $ hg init flog $ cd flog @@ -2045,7 +2047,7 @@ > def reposetup(ui, repo): > for line in repo.opener('hidden'): > ctx = repo[line.strip()] - > repo.changelog.hiddenrevs.add(ctx.rev()) + > repo.hiddenrevs.add(ctx.rev()) > EOF $ echo '[extensions]' >> .hg/hgrc $ echo "hidden=$HGTMP/testhidden.py" >> .hg/hgrc @@ -2056,3 +2058,31 @@ $ testlog --hidden [] [] + +A template without trailing newline should do something sane + + $ hg glog -r ::2 --template '{rev} {desc}' + o 2 mv b dir/b + | + o 1 copy a b + | + +Extra newlines must be preserved + + $ hg glog -r ::2 --template '\n{rev} {desc}\n\n' + o + | 2 mv b dir/b + | + o + | 1 copy a b + | + +The almost-empty template should do something sane too ... + + $ hg glog -r ::2 --template '\n' + o + | + o + | + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-gpg.t --- a/tests/test-gpg.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-gpg.t Wed Jul 18 19:08:25 2012 -0500 @@ -17,7 +17,7 @@ $ hg sigs $ hg sign 0 - Signing 0:e63c23eaa88a + signing 0:e63c23eaa88a $ hg sigs hgtest 0:e63c23eaa88ae77967edcf4ea194d31167c478b0 @@ -30,3 +30,5 @@ the main hg working dir $ "$TESTDIR/md5sum.py" "$TESTDIR/gpg/trustdb.gpg" f6b9c78c65fa9536e7512bb2ceb338ae */gpg/trustdb.gpg (glob) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-graft.t --- a/tests/test-graft.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-graft.t Wed Jul 18 19:08:25 2012 -0500 @@ -54,6 +54,18 @@ skipping ancestor revision 2 [255] +Specify revisions with -r: + + $ hg graft -r 1 -r 2 + skipping ancestor revision 1 + skipping ancestor revision 2 + [255] + + $ hg graft -r 1 2 + skipping ancestor revision 2 + skipping ancestor revision 1 + [255] + Can't graft with dirty wd: $ hg up -q 0 @@ -72,28 +84,23 @@ # HG changeset patch # User foo # Date 0 0 - # Node ID d2e44c99fd3f31c176ea4efb9eca9f6306c81756 + # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e 2 diff --git a/a b/b rename from a rename to b - --- a/a - +++ b/b - @@ -1,1 +1,1 @@ - -a - +b Look for extra:source $ hg log --debug -r tip - changeset: 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756 + changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82 tag: tip phase: draft parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e parent: -1:0000000000000000000000000000000000000000 - manifest: 7:5d59766436fd8fbcd38e7bebef0f6eaf3eebe637 + manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb user: foo date: Thu Jan 01 00:00:00 1970 +0000 files+: b @@ -123,20 +130,25 @@ searching for copies back to rev 1 unmatched files in local: b - all copies found (* = to merge, ! = divergent): + all copies found (* = to merge, ! = divergent, % = renamed and deleted): b -> a * checking for directory renames resolving manifests overwrite: False, partial: False - ancestor: 68795b066622, local: d2e44c99fd3f+, remote: 5d205f8b35b6 + ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6 b: local copied/moved to a -> m preserving b for resolve of b updating: b 1/1 files (100.00%) + picked tool 'internal:merge' for b (binary False symlink False) + merging b and a to b + my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622 + premerge successful + b grafting revision 5 searching for copies back to rev 1 resolving manifests overwrite: False, partial: False - ancestor: 4c60f11aa304, local: d2e44c99fd3f+, remote: 97f8bfe72746 + ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746 e: remote is newer -> g updating: e 1/1 files (100.00%) getting e @@ -145,7 +157,7 @@ searching for copies back to rev 1 resolving manifests overwrite: False, partial: False - ancestor: 4c60f11aa304, local: 839a7e8fcf80+, remote: 9c233e8e184d + ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d e: versions differ -> m d: remote is newer -> g preserving e for resolve of e @@ -154,7 +166,7 @@ updating: e 2/2 files (100.00%) picked tool 'internal:merge' for e (binary False symlink False) merging e - my e@839a7e8fcf80+ other e@9c233e8e184d ancestor e@68795b066622 + my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622 warning: conflicts during merge. merging e incomplete! (edit conflicts, then use 'hg resolve --mark') abort: unresolved conflicts, can't continue @@ -179,6 +191,10 @@ abort: can't specify --continue and revisions [255] + $ hg graft -c -r 6 + abort: can't specify --continue and revisions + [255] + Continue for real, clobber usernames $ hg graft -c -U @@ -200,11 +216,13 @@ View graph: $ hg --config extensions.graphlog= log -G --template '{author}@{rev}.{phase}: {desc}\n' - @ test@10.draft: 3 + @ test@11.draft: 3 + | + o test@10.draft: 4 | - o test@9.draft: 4 + o test@9.draft: 5 | - o test@8.draft: 5 + o bar@8.draft: 1 | o foo@7.draft: 2 | @@ -232,17 +250,17 @@ grafting revision 7 $ hg log -r 7 --template '{rev}:{node}\n' - 7:d2e44c99fd3f31c176ea4efb9eca9f6306c81756 + 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82 $ hg log -r 2 --template '{rev}:{node}\n' 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4 $ hg log --debug -r tip - changeset: 12:95adbe5de6b10f376b699ece9ed5a57cd7b4b0f6 + changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d tag: tip phase: draft - parent: 11:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f + parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f parent: -1:0000000000000000000000000000000000000000 - manifest: 12:9944044f82a462bbaccc9bdf7e0ac5b811db7d1b + manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637 user: foo date: Thu Jan 01 00:00:00 1970 +0000 files+: b @@ -260,7 +278,7 @@ [255] Disallow grafting already grafted csets with the same origin onto each other - $ hg up -q 12 + $ hg up -q 13 $ hg graft 2 skipping already grafted revision 2 [255] @@ -273,9 +291,19 @@ skipping already grafted revision 2 [255] $ hg graft tip - skipping already grafted revision 12 (same origin 2) + skipping already grafted revision 13 (same origin 2) [255] +Graft with --log + + $ hg up -Cq 1 + $ hg graft 3 --log -u foo + grafting revision 3 + warning: can't find ancestor for 'c' copied from 'b'! + $ hg log --template '{rev} {parents} {desc}\n' -r tip + 14 1:5d205f8b35b6 3 + (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8) + Resolve conflicted graft $ hg up -q 0 $ echo b > a @@ -330,3 +358,178 @@ diff --git a/a b/b rename from a rename to b + +Test simple origin(), with and without args + $ hg log -r 'origin()' + changeset: 1:5d205f8b35b6 + user: bar + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1 + + changeset: 2:5c095ad7e90f + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 3:4c60f11aa304 + user: baz + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 3 + + changeset: 4:9c233e8e184d + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 4 + + changeset: 5:97f8bfe72746 + branch: stable + parent: 3:4c60f11aa304 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 5 + + $ hg log -r 'origin(7)' + changeset: 2:5c095ad7e90f + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + +Now transplant a graft to test following through copies + $ hg up -q 0 + $ hg branch -q dev + $ hg ci -qm "dev branch" + $ hg --config extensions.transplant= transplant -q 7 + $ hg log -r 'origin(.)' + changeset: 2:5c095ad7e90f + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + +Test simple destination + $ hg log -r 'destination()' + changeset: 7:ef0ef43d49e7 + parent: 0:68795b066622 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 8:6b9e5368ca4e + user: bar + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1 + + changeset: 9:1905859650ec + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 5 + + changeset: 10:52dc0b4c6907 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 4 + + changeset: 11:882b35362a6b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 3 + + changeset: 13:9db0f28fd374 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 14:f64defefacee + parent: 1:5d205f8b35b6 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 3 + + changeset: 17:64ecd9071ce8 + user: bar + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1 + + changeset: 19:2e80e1351d6e + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 21:7e61b508e709 + branch: dev + tag: tip + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + $ hg log -r 'destination(2)' + changeset: 7:ef0ef43d49e7 + parent: 0:68795b066622 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 13:9db0f28fd374 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 19:2e80e1351d6e + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 21:7e61b508e709 + branch: dev + tag: tip + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + +Transplants of grafts can find a destination... + $ hg log -r 'destination(7)' + changeset: 21:7e61b508e709 + branch: dev + tag: tip + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + +... grafts of grafts unfortunately can't + $ hg graft -q 13 + $ hg log -r 'destination(13)' +All copies of a cset + $ hg log -r 'origin(13) or destination(origin(13))' + changeset: 2:5c095ad7e90f + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 7:ef0ef43d49e7 + parent: 0:68795b066622 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 13:9db0f28fd374 + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 19:2e80e1351d6e + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 21:7e61b508e709 + branch: dev + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + + changeset: 22:1313d0a825e2 + branch: dev + tag: tip + user: foo + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 2 + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-grep.t --- a/tests/test-grep.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-grep.t Wed Jul 18 19:08:25 2012 -0500 @@ -163,6 +163,8 @@ color:3:-:red color:1:+:red + $ cd .. + $ hg init a $ cd a $ cp "$TESTDIR/binfile.bin" . @@ -170,3 +172,5 @@ $ hg ci -m 'add binfile.bin' $ hg grep "MaCam" --all binfile.bin:0:+: Binary file matches + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hardlinks.t --- a/tests/test-hardlinks.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hardlinks.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,10 +1,11 @@ - $ "$TESTDIR/hghave" no-windows || exit 80 + $ "$TESTDIR/hghave" hardlink || exit 80 $ cat > nlinks.py < import os, sys + > import sys + > from mercurial import util > for f in sorted(sys.stdin.readlines()): > f = f[:-1] - > print os.lstat(f).st_nlink, f + > print util.nlinks(f), f > EOF $ nlinksdir() @@ -104,7 +105,10 @@ Create a non-inlined filelog in r3: $ cd r3/d1 - $ python -c 'for x in range(10000): print x' >> data1 + >>> f = open('data1', 'wb') + >>> for x in range(10000): + ... f.write("%s\n" % str(x)) + >>> f.close() $ for j in 0 1 2 3 4 5 6 7 8 9; do > cat data1 >> f2 > hg commit -m$j @@ -133,7 +137,7 @@ $ cd r3 $ hg push - pushing to $TESTTMP/r1 + pushing to $TESTTMP/r1 (glob) searching for changes adding changesets adding manifests @@ -344,3 +348,4 @@ $ cat ../b/.hg/localtags 4e7abb4840c46a910f6d7b4d3c3fc7e5209e684c lfoo + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-help.t --- a/tests/test-help.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-help.t Wed Jul 18 19:08:25 2012 -0500 @@ -5,46 +5,46 @@ 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 $ hg -q - 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) $ hg help Mercurial Distributed SCM @@ -211,42 +211,26 @@ basic commands: - add: - add the specified files on the next commit - annotate, blame: - show changeset information by line for each file - clone: - make a copy of an existing repository - commit, ci: - 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, history: - 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, rm: - remove the specified files on the next commit - serve: - start stand-alone webserver - status, st: - show changed files in the working directory - summary, sum: - summarize working directory state - update, up, checkout, co: - update working directory (or switch revisions) + add add the specified files on the next commit + annotate, blame + show changeset information by line for each file + clone make a copy of an existing repository + commit, ci 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, history 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, rm remove the specified files on the next commit + serve start stand-alone webserver + status, st show changed files in the working directory + summary, sum summarize working directory state + update, up, checkout, co + update working directory (or switch revisions) global options: @@ -389,8 +373,8 @@ $ hg help ad list of commands: - add add the specified files on the next commit - addremove add all new files, delete all missing files + add add the specified files on the next commit + addremove add all new files, delete all missing files use "hg -v help ad" to show builtin aliases and global options @@ -539,24 +523,24 @@ 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] @@ -567,24 +551,24 @@ 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] @@ -614,6 +598,15 @@ use "hg -v help nohelp" to show more info + $ hg help -k nohelp + Commands: + + nohelp hg nohelp + + Extension Commands: + + nohelp (no help text available) + Test that default list of commands omits extension commands $ hg help @@ -711,7 +704,7 @@ list of commands: - nohelp (no help text available) + nohelp (no help text available) use "hg -v help helpext" to show builtin aliases and global options @@ -733,13 +726,13 @@ short-form identifier is only valid if it is the prefix of exactly one full-length identifier. - Any other string is treated as a tag or branch name. A tag name is a - symbolic name associated with a revision identifier. A branch name denotes - the tipmost revision of that branch. Tag and branch names must not contain - the ":" character. + Any other string is treated as a bookmark, tag, or branch name. A bookmark + is a movable pointer to a revision. A tag is a permanent name associated + with a revision. A branch name denotes the tipmost revision of that + branch. Bookmark, tag, and branch names must not contain the ":" + character. - The reserved name "tip" is a special tag that always identifies the most - recent revision. + The reserved name "tip" always identifies the most recent revision. The reserved name "null" indicates the null revision. This is the revision of an empty repository, and the parent of revision 0. @@ -782,3 +775,30 @@ $ hg help revsets | grep helphook helphook1 helphook2 + +Test keyword search help + + $ hg help -k clone + Topics: + + config Configuration Files + extensions Using Additional Features + glossary Glossary + phases Working with Phases + subrepo Subrepositories + urls URL Paths + + Commands: + + clone make a copy of an existing repository + paths show aliases for remote repositories + update update working directory (or switch revisions) + + Extensions: + + relink recreates hardlinks between repository clones + + Extension Commands: + + qclone clone main and patch repository at same time + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgcia.t --- a/tests/test-hgcia.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgcia.t Wed Jul 18 19:08:25 2012 -0500 @@ -90,3 +90,5 @@ 0 + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgignore.t --- a/tests/test-hgignore.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgignore.t Wed Jul 18 19:08:25 2012 -0500 @@ -122,3 +122,5 @@ $ hg debugignore (?:(?:|.*/)[^/]*(?:/|$)) + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgk.t --- a/tests/test-hgk.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgk.t Wed Jul 18 19:08:25 2012 -0500 @@ -16,3 +16,5 @@ branch default adda + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgrc.t --- a/tests/test-hgrc.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgrc.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,3 +1,6 @@ +hide outer repo + $ hg init + Use hgrc within $TESTTMP $ HGRCPATH=`pwd`/hgrc @@ -63,6 +66,7 @@ make sure global options given on the cmdline take precedence $ hg showconfig --config ui.verbose=True --quiet + bundle.mainreporoot=$TESTTMP ui.verbose=False ui.debug=False ui.quiet=True @@ -93,6 +97,7 @@ $ cd .. $ hg showconfig + bundle.mainreporoot=$TESTTMP ui.username=$FAKEUSER $ unset FAKEUSER @@ -117,8 +122,6 @@ HGPLAIN - $ cd .. - $ p=`pwd` $ echo "[ui]" > $HGRC $ echo "debug=true" >> $HGRC $ echo "fallbackencoding=ASCII" >> $HGRC @@ -138,6 +141,7 @@ $ hg showconfig read config from: $TESTTMP/hgrc $TESTTMP/hgrc:13: alias.log=log -g + none: bundle.mainreporoot=$TESTTMP $TESTTMP/hgrc:11: defaults.identify=-n $TESTTMP/hgrc:2: ui.debug=true $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII @@ -153,6 +157,7 @@ $ HGPLAIN=; export HGPLAIN $ hg showconfig --config ui.traceback=True --debug read config from: $TESTTMP/hgrc + none: bundle.mainreporoot=$TESTTMP none: ui.traceback=True none: ui.verbose=False none: ui.debug=True @@ -170,6 +175,7 @@ $ hg showconfig --config ui.traceback=True --debug plain: True read config from: $TESTTMP/hgrc + none: bundle.mainreporoot=$TESTTMP $TESTTMP/hgrc:15: extensions.plain=./plain.py none: ui.traceback=True none: ui.verbose=False @@ -179,6 +185,7 @@ $ hg showconfig --config ui.traceback=True --debug plain: True read config from: $TESTTMP/hgrc + none: bundle.mainreporoot=$TESTTMP $TESTTMP/hgrc:15: extensions.plain=./plain.py none: ui.traceback=True none: ui.verbose=False @@ -188,6 +195,7 @@ $ hg showconfig --config ui.traceback=True --debug plain: True read config from: $TESTTMP/hgrc + none: bundle.mainreporoot=$TESTTMP $TESTTMP/hgrc:15: extensions.plain=./plain.py none: ui.traceback=True none: ui.verbose=False diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-commands.t Wed Jul 18 19:08:25 2012 -0500 @@ -37,7 +37,7 @@ Logs and changes - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/?style=atom' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log/?style=atom' 200 Script output follows @@ -115,7 +115,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log/1/?style=atom' 200 Script output follows @@ -193,7 +193,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log/1/foo/?style=atom' 200 Script output follows @@ -221,7 +221,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'shortlog/' 200 Script output follows @@ -326,7 +326,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'rev/0/' 200 Script output follows @@ -453,7 +453,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'rev/1/?style=raw' 200 Script output follows @@ -470,7 +470,7 @@ @@ -0,0 +1,1 @@ +2ef0ac749a14e4f57a5a822464a0902c6f7f448f 1.0 - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log?rev=base' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'log?rev=base' 200 Script output follows @@ -548,11 +548,11 @@ File-related - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo/?style=raw' 200 Script output follows foo - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'annotate/1/foo/?style=raw' 200 Script output follows @@ -561,7 +561,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/?style=raw' 200 Script output follows @@ -570,7 +570,7 @@ -rw-r--r-- 4 foo - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/1/foo' 200 Script output follows @@ -605,6 +605,7 @@
  • file
  • latest
  • diff
  • +
  • comparison
  • annotate
  • file log
  • raw
  • @@ -663,7 +664,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/0/foo/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'filediff/0/foo/?style=raw' 200 Script output follows @@ -677,7 +678,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'filediff/1/foo/?style=raw' 200 Script output follows @@ -688,23 +689,23 @@ Overviews - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'raw-tags' 200 Script output follows tip ba87b23d29ca67a305625d81a20ac279c1e3f444 1.0 2ef0ac749a14e4f57a5a822464a0902c6f7f448f - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'raw-branches' 200 Script output follows unstable ba87b23d29ca67a305625d81a20ac279c1e3f444 open stable 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe inactive default a4f92ed23982be056b9852de5dfe873eaac7f0de inactive - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-bookmarks' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'raw-bookmarks' 200 Script output follows anotherthing 2ef0ac749a14e4f57a5a822464a0902c6f7f448f something ba87b23d29ca67a305625d81a20ac279c1e3f444 - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'summary/?style=gitweb' 200 Script output follows @@ -906,7 +907,7 @@ - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'graph/?style=gitweb' 200 Script output follows @@ -1047,6 +1048,55 @@ +raw graph + + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'graph/?style=raw' + 200 Script output follows + + + # HG graph + # Node ID ba87b23d29ca67a305625d81a20ac279c1e3f444 + # Rows shown 4 + + changeset: ba87b23d29ca + user: test + date: 1970-01-01 + summary: branch + branch: unstable + tag: tip + bookmark: something + + node: (0, 0) (color 1) + edge: (0, 0) -> (0, 1) (color 1) + + changeset: 1d22e65f027e + user: test + date: 1970-01-01 + summary: branch + branch: stable + + node: (0, 1) (color 1) + edge: (0, 1) -> (0, 2) (color 1) + + changeset: a4f92ed23982 + user: test + date: 1970-01-01 + summary: Added tag 1.0 for changeset 2ef0ac749a14 + branch: default + + node: (0, 2) (color 1) + edge: (0, 2) -> (0, 3) (color 1) + + changeset: 2ef0ac749a14 + user: test + date: 1970-01-01 + summary: base + tag: 1.0 + bookmark: anotherthing + + node: (0, 3) (color 1) + + capabilities @@ -1098,7 +1148,7 @@ Static files - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'static/style.css' 200 Script output follows a { text-decoration:none; } @@ -1221,7 +1271,7 @@ Graph json escape of multibyte character - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/' \ + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'graph/' \ > | grep '^var data =' var data = [["548001d11f45", [0, 1], [[0, 0, 1, -1, ""]], "\u80fd", "test", "1970-01-01", ["unstable", true], ["tip"], ["something"]], ["ba87b23d29ca", [0, 1], [[0, 0, 1, 3, "FF0000"]], "branch", "test", "1970-01-01", ["unstable", false], [], []], ["1d22e65f027e", [0, 1], [[0, 0, 1, 3, ""]], "branch", "test", "1970-01-01", ["stable", true], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1, 3, ""]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]]; @@ -1237,3 +1287,5 @@ ERRORS ENCOUNTERED $ cat errors.log + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-descend-empties.t --- a/tests/test-hgweb-descend-empties.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-descend-empties.t Wed Jul 18 19:08:25 2012 -0500 @@ -29,7 +29,7 @@ manifest with descending - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file' 200 Script output follows @@ -140,3 +140,5 @@ $ cat errors.log + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-diffs.t --- a/tests/test-hgweb-diffs.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-diffs.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,4 +1,4 @@ - $ "$TESTDIR/hghave" serve execbit || exit 80 + $ "$TESTDIR/hghave" serve || exit 80 setting up repo @@ -12,9 +12,22 @@ change permissions for git diffs - $ chmod +x a - $ hg rm b - $ hg ci -Amb + $ hg import -q --bypass - < # HG changeset patch + > # User test + > # Date 0 0 + > b + > + > diff --git a/a b/a + > old mode 100644 + > new mode 100755 + > diff --git a/b b/b + > deleted file mode 100644 + > --- a/b + > +++ /dev/null + > @@ -1,1 +0,0 @@ + > -b + > EOF set up hgweb @@ -23,7 +36,7 @@ revision - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/0' 200 Script output follows @@ -153,7 +166,7 @@ raw revision - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/raw-rev/0' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'raw-rev/0' 200 Script output follows @@ -178,7 +191,7 @@ diff removed file - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/b' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/tip/b' 200 Script output follows @@ -214,6 +227,7 @@
  • file
  • latest
  • diff
  • +
  • comparison
  • annotate
  • file log
  • raw
  • @@ -283,7 +297,7 @@ revision - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/0' 200 Script output follows @@ -415,7 +429,7 @@ revision - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/raw-rev/0' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'raw-rev/0' 200 Script output follows @@ -442,7 +456,7 @@ diff removed file - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/tip/a' 200 Script output follows @@ -478,6 +492,7 @@
  • file
  • latest
  • diff
  • +
  • comparison
  • annotate
  • file log
  • raw
  • @@ -536,6 +551,378 @@ + +comparison new file + + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/0/a' + 200 Script output follows + + + + + + + + + + test: a comparison + + + +
    + + +
    +

    test

    +

    comparison a @ 0:0cd96de13884

    + + + +
    a
    + + + + + + + + + + + + + + + + + + + +
    authortest
    dateThu, 01 Jan 1970 00:00:00 +0000
    parents
    children559edbd9ed20
    + +
    +
    comparison
    +
    + equal + deleted + inserted + replaced +
    + + + + + + + + + + + + + + + + + +
    -1:0000000000000:b789fdd96dc2
    1 a
    + +
    +
    +
    + + + + + + + + +comparison existing file + + $ hg up + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ echo a >> a + $ hg ci -mc + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/tip/a' + 200 Script output follows + + + + + + + + + + test: a comparison + + + +
    + + +
    +

    test

    +

    comparison a @ 2:d73db4d812ff

    + + + +
    c
    + + + + + + + + + + + + + + + + + + + +
    authortest
    dateThu, 01 Jan 1970 00:00:00 +0000
    parents559edbd9ed20
    children
    + +
    +
    comparison
    +
    + equal + deleted + inserted + replaced +
    + + + + + + + + + + + + + + + + + + + + + +
    0:b789fdd96dc21:a80d06849b33
    1 a 1 a
    2 a
    + +
    +
    +
    + + + + + + + + +comparison removed file + + $ hg rm a + $ hg ci -md + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/tip/a' + 200 Script output follows + + + + + + + + + + test: a comparison + + + +
    + + +
    +

    test

    +

    comparison a @ 3:20e80271eb7a

    + + + +
    d
    + + + + + + + + + + + + + + + + + + + +
    authortest
    dateThu, 01 Jan 1970 00:00:00 +0000
    parentsd73db4d812ff
    children
    + +
    +
    comparison
    +
    + equal + deleted + inserted + replaced +
    + + + + + + + + + + + + + + + + + + + + + +
    1:a80d06849b33-1:000000000000
    1 a
    2 a
    + +
    +
    +
    + + + + + + + + $ cd .. test import rev as raw-rev @@ -548,7 +935,7 @@ updating to branch default 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd test1 - $ hg import -q --exact http://localhost:$HGPORT/rev/1 + $ hg import -q --bypass --exact http://localhost:$HGPORT/rev/1 raw revision with diff block numbers @@ -572,7 +959,7 @@ > EOF $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/raw-rev/0' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'raw-rev/0' 200 Script output follows Block: 1 @@ -598,3 +985,5 @@ errors $ cat ../test/errors.log + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-empty.t --- a/tests/test-hgweb-empty.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-empty.t Wed Jul 18 19:08:25 2012 -0500 @@ -6,7 +6,7 @@ $ cd test $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/shortlog') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'shortlog') 200 Script output follows @@ -91,7 +91,7 @@ - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log') 200 Script output follows @@ -176,7 +176,7 @@ - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/graph') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'graph') 200 Script output follows @@ -322,7 +322,7 @@ - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file') 200 Script output follows @@ -395,3 +395,5 @@ + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-filelog.t --- a/tests/test-hgweb-filelog.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-filelog.t Wed Jul 18 19:08:25 2012 -0500 @@ -109,7 +109,7 @@ tip - two revisions - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log/tip/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/tip/a') 200 Script output follows @@ -148,6 +148,7 @@
    • file
    • diff
    • +
    • comparison
    • annotate
    • file log
    • raw
    • @@ -210,7 +211,7 @@ second version - two revisions - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log/3/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/3/a') 200 Script output follows @@ -249,6 +250,7 @@
      • file
      • diff
      • +
      • comparison
      • annotate
      • file log
      • raw
      • @@ -311,7 +313,7 @@ first deleted - one revision - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log/2/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/2/a') 200 Script output follows @@ -350,6 +352,7 @@
        • file
        • diff
        • +
        • comparison
        • annotate
        • file log
        • raw
        • @@ -407,7 +410,7 @@ first version - one revision - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log/1/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/1/a') 200 Script output follows @@ -446,6 +449,7 @@
          • file
          • diff
          • +
          • comparison
          • annotate
          • file log
          • raw
          • @@ -503,7 +507,7 @@ before addition - error - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log/0/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/0/a') 404 Not Found @@ -567,7 +571,7 @@ should show base link, use spartan because it shows it - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/log/tip/c?style=spartan') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'log/tip/c?style=spartan') 200 Script output follows @@ -676,7 +680,7 @@ rss log - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rss-log/tip/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rss-log/tip/a') 200 Script output follows @@ -707,7 +711,7 @@ atom log - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/atom-log/tip/a') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'atom-log/tip/a') 200 Script output follows @@ -755,3 +759,5 @@ errors $ cat errors.log + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-no-path-info.t --- a/tests/test-hgweb-no-path-info.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-no-path-info.t Wed Jul 18 19:08:25 2012 -0500 @@ -105,3 +105,5 @@ ---- ERRORS + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-no-request-uri.t --- a/tests/test-hgweb-no-request-uri.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-no-request-uri.t Wed Jul 18 19:08:25 2012 -0500 @@ -139,3 +139,5 @@ ---- ERRORS + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-non-interactive.t --- a/tests/test-hgweb-non-interactive.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-non-interactive.t Wed Jul 18 19:08:25 2012 -0500 @@ -78,3 +78,5 @@ [] ---- request.ENVIRON wsgi variables ['wsgi.errors', 'wsgi.input', 'wsgi.multiprocess', 'wsgi.multithread', 'wsgi.run_once', 'wsgi.url_scheme', 'wsgi.version'] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-raw.t --- a/tests/test-hgweb-raw.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-raw.t Wed Jul 18 19:08:25 2012 -0500 @@ -5,20 +5,19 @@ $ hg init test $ cd test $ mkdir sub - $ cat >'sub/some "text".txt' <'sub/some text%.txt' < This is just some random text > that will go inside the file and take a few lines. > It is very boring to read, but computers don't > care about things like that. > ENDSOME - $ hg add 'sub/some "text".txt' - warning: filename contains '"', which is reserved on Windows: 'sub/some "text".txt' + $ hg add 'sub/some text%.txt' $ hg commit -d "1 0" -m "Just some text" $ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw' content-type content-length content-disposition) >getoutput.txt + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type content-length content-disposition) >getoutput.txt $ while kill `cat hg.pid` 2>/dev/null; do sleep 0; done @@ -26,33 +25,34 @@ 200 Script output follows content-type: application/binary content-length: 157 - content-disposition: inline; filename="some \"text\".txt" + content-disposition: inline; filename="some text%.txt" This is just some random text that will go inside the file and take a few lines. It is very boring to read, but computers don't care about things like that. $ cat access.log error.log - 127.0.0.1 - - [*] "GET /?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw HTTP/1.1" 200 - (glob) + 127.0.0.1 - - [*] "GET /?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw HTTP/1.1" 200 - (glob) $ rm access.log error.log $ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid \ > --config web.guessmime=True $ cat hg.pid >> $DAEMON_PIDS - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw' content-type content-length content-disposition) >getoutput.txt + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type content-length content-disposition) >getoutput.txt $ while kill `cat hg.pid` 2>/dev/null; do sleep 0; done $ cat getoutput.txt 200 Script output follows content-type: text/plain; charset="ascii" content-length: 157 - content-disposition: inline; filename="some \"text\".txt" + content-disposition: inline; filename="some text%.txt" This is just some random text that will go inside the file and take a few lines. It is very boring to read, but computers don't care about things like that. $ cat access.log error.log - 127.0.0.1 - - [*] "GET /?f=a23bf1310f6e;file=sub/some%20%22text%22.txt;style=raw HTTP/1.1" 200 - (glob) + 127.0.0.1 - - [*] "GET /?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw HTTP/1.1" 200 - (glob) + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb-removed.t --- a/tests/test-hgweb-removed.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb-removed.t Wed Jul 18 19:08:25 2012 -0500 @@ -17,7 +17,7 @@ revision - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/tip' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip' 200 Script output follows @@ -135,7 +135,7 @@ diff removed file - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/tip/a' 200 Script output follows @@ -171,6 +171,7 @@
          • file
          • latest
          • diff
          • +
          • comparison
          • annotate
          • file log
          • raw
          • @@ -231,3 +232,5 @@ + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgweb.t --- a/tests/test-hgweb.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgweb.t Wed Jul 18 19:08:25 2012 -0500 @@ -15,7 +15,7 @@ manifest - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/?style=raw') 200 Script output follows @@ -23,7 +23,7 @@ -rw-r--r-- 4 foo - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/da?style=raw') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/da?style=raw') 200 Script output follows @@ -33,14 +33,14 @@ plain file - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/foo?style=raw' 200 Script output follows foo should give a 404 - static file that does not exist - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/bogus' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'static/bogus' 404 Not Found @@ -104,7 +104,7 @@ should give a 404 - bad revision - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/spam/foo?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/spam/foo?style=raw' 404 Not Found @@ -113,7 +113,7 @@ should give a 400 - bad command - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?cmd=spam&style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/foo?cmd=spam&style=raw' 400* (glob) @@ -122,13 +122,13 @@ should give a 404 - file does not exist - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/bork?style=raw' 404 Not Found error: bork@2ef0ac749a14: not found in manifest [1] - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/bork' 404 Not Found @@ -189,7 +189,7 @@ [1] - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/bork?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/tip/bork?style=raw' 404 Not Found @@ -198,7 +198,7 @@ try bad style - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=foobar') + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/?style=foobar') 200 Script output follows @@ -306,7 +306,7 @@ static file - $ "$TESTDIR/get-with-headers.py" --twice localhost:$HGPORT '/static/style-gitweb.css' + $ "$TESTDIR/get-with-headers.py" --twice localhost:$HGPORT 'static/style-gitweb.css' 200 Script output follows body { font-family: sans-serif; font-size: 12px; margin:0px; border:solid #d9d8d1; border-width:1px; margin:10px; } @@ -437,9 +437,51 @@ top: -3px; font-style: italic; } + + /* Comparison */ + .legend { + padding: 1.5% 0 1.5% 0; + } + + .legendinfo { + border: 1px solid #d9d8d1; + font-size: 80%; + text-align: center; + padding: 0.5%; + } + + .equal { + background-color: #ffffff; + } + + .delete { + background-color: #ffc5ce; + } + + .insert { + background-color: #c5ffc4; + } + + .replace { + background-color: #ffff99; + } + + .comparison { + overflow-x: auto; + } + + .header th { + text-align: center; + } + + .block { + border-top: 1px solid #d9d8d1; + } 304 Not Modified errors $ cat errors.log + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgwebdir.t --- a/tests/test-hgwebdir.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgwebdir.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,10 @@ $ "$TESTDIR/hghave" serve || exit 80 +hide outer repo and work in dir without '.hg' + $ hg init + $ mkdir dir + $ cd dir + Tests some basic hgwebdir functionality. Tests setting up paths and collection, different forms of 404s and the subdirectory support. @@ -59,6 +64,8 @@ $ rm -R nostore/.hg/store $ root=`pwd` $ cd .. + +serve $ cat > paths.conf < [paths] > a=$root/a @@ -70,7 +77,7 @@ should give a 404 - file does not exist - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/bork?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'a/file/tip/bork?style=raw' 404 Not Found @@ -79,25 +86,25 @@ should succeed - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '?style=raw' 200 Script output follows /a/ /b/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'a/file/tip/a?style=raw' 200 Script output follows a - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'b/file/tip/b?style=raw' 200 Script output follows b should give a 404 - repo is not published - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'c/file/tip/c?style=raw' 404 Not Found @@ -106,14 +113,14 @@ atom-log without basedir - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/atom-log' | grep ' (glob) (glob) (glob) rss-log without basedir - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/rss-log' | grep 'http://*:$HGPORT/a/rev/8580ff50825a (glob) $ cat > paths.conf < [paths] @@ -131,7 +138,7 @@ should succeed, slashy names - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '?style=raw' 200 Script output follows @@ -170,7 +177,7 @@ /astar/ /astar/.hg/patches/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=paper' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '?style=paper' 200 Script output follows @@ -483,19 +490,19 @@ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't?style=raw' 200 Script output follows /t/a/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/?style=raw' 200 Script output follows /t/a/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=paper' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/?style=paper' 200 Script output follows @@ -544,7 +551,7 @@ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/a?style=atom' 200 Script output follows @@ -574,7 +581,7 @@ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/a/?style=atom' 200 Script output follows @@ -604,14 +611,14 @@ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/a/file/tip/a?style=raw' 200 Script output follows a Test [paths] '*' extension - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'coll/?style=raw' 200 Script output follows @@ -622,14 +629,14 @@ /coll/notrepo/e/ /coll/notrepo/f/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/a/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'coll/a/file/tip/a?style=raw' 200 Script output follows a Test [paths] '**' extension - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/?style=raw' 200 Script output follows @@ -643,7 +650,7 @@ /rcoll/notrepo/f/ /rcoll/notrepo/f/f2/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/b/d/file/tip/d?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw' 200 Script output follows d @@ -658,7 +665,7 @@ $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \ > -A access-paths.log -E error-paths-3.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'coll/?style=raw' 200 Script output follows @@ -668,11 +675,11 @@ /coll/c/ /coll/notrepo/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/a/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'coll/a/file/tip/a?style=raw' 200 Script output follows a - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/?style=raw' 200 Script output follows @@ -683,14 +690,14 @@ /rcoll/c/ /rcoll/notrepo/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/b/d/file/tip/d?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw' 200 Script output follows d Test intermediate directories - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/notrepo/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/notrepo/?style=raw' 200 Script output follows @@ -702,14 +709,14 @@ Test repositories inside intermediate directories - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/notrepo/e/file/tip/e?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/notrepo/e/file/tip/e?style=raw' 200 Script output follows e Test subrepositories inside intermediate directories - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/notrepo/f/f2/file/tip/f2?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/notrepo/f/f2/file/tip/f2?style=raw' 200 Script output follows f2 @@ -723,7 +730,7 @@ $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \ > -A access-paths.log -E error-paths-4.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'coll/?style=raw' 200 Script output follows @@ -731,11 +738,11 @@ /coll/b/ /coll/c/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/a/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'coll/a/file/tip/a?style=raw' 200 Script output follows a - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/?style=raw' 200 Script output follows @@ -743,14 +750,14 @@ /rcoll/b/ /rcoll/c/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/b/d/file/tip/d?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw' 200 Script output follows d Test intermediate directories - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/notrepo/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/notrepo/?style=raw' 200 Script output follows @@ -760,14 +767,14 @@ Test repositories inside intermediate directories - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/notrepo/e/file/tip/e?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/notrepo/e/file/tip/e?style=raw' 200 Script output follows e Test subrepositories inside intermediate directories - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/notrepo/f/f2/file/tip/f2?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 'rcoll/notrepo/f/f2/file/tip/f2?style=raw' 200 Script output follows f2 @@ -787,7 +794,7 @@ $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \ > -A access-paths.log -E error-paths-5.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '?style=raw' 200 Script output follows @@ -795,7 +802,7 @@ /t/b/ /c/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/?style=raw' 200 Script output follows @@ -813,14 +820,14 @@ $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \ > -A access-paths.log -E error-paths-6.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '?style=raw' 200 Script output follows /t/ /c/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/?style=raw' 200 Script output follows @@ -837,13 +844,13 @@ $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \ > -A access-paths.log -E error-paths-7.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '?style=raw' 200 Script output follows /c/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 't/?style=raw' 200 Script output follows @@ -862,7 +869,7 @@ test inexistent and inaccessible repo should be ignored silently - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '' 200 Script output follows @@ -914,7 +921,7 @@ collections: should succeed - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '?style=raw' 200 Script output follows @@ -925,29 +932,29 @@ /notrepo/e/ /notrepo/f/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 'a/file/tip/a?style=raw' 200 Script output follows a - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 'b/file/tip/b?style=raw' 200 Script output follows b - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 'c/file/tip/c?style=raw' 200 Script output follows c atom-log with basedir / - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/atom-log' | grep ' rss-log with basedir / - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/rss-log' | grep 'http://hg.example.com:8080/a/rev/8580ff50825a $ "$TESTDIR/killdaemons.py" $ hg serve --config web.baseurl=http://hg.example.com:8080/foo/ -p $HGPORT2 -d \ @@ -957,14 +964,14 @@ atom-log with basedir /foo/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/atom-log' | grep ' rss-log with basedir /foo/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/rss-log' | grep 'http://hg.example.com:8080/foo/a/rev/8580ff50825a paths errors 1 diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hgwebdirsym.t --- a/tests/test-hgwebdirsym.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hgwebdirsym.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,6 +1,10 @@ Tests whether or not hgwebdir properly handles various symlink topologies. $ "$TESTDIR/hghave" serve symlink || exit 80 + +hide outer repo + $ hg init + $ hg init a $ echo a > a/a $ hg --cwd a ci -Ama -d'1 0' @@ -29,7 +33,7 @@ should succeed - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '?style=raw' 200 Script output follows @@ -37,34 +41,34 @@ /b/ /c/ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/al/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'al/file/tip/a?style=raw' 200 Script output follows a - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'b/file/tip/b?style=raw' 200 Script output follows b - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'c/file/tip/c?style=raw' 200 Script output follows c should fail - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/al/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'circle/al/file/tip/a?style=raw' 404 Not Found error: repository circle/al/file/tip/a not found [1] - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/b/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'circle/b/file/tip/a?style=raw' 404 Not Found error: repository circle/b/file/tip/a not found [1] - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/circle/c/file/tip/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'circle/c/file/tip/a?style=raw' 404 Not Found diff -r 98823bd0d697 -r a06e2681dd17 tests/test-highlight.t --- a/tests/test-highlight.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-highlight.t Wed Jul 18 19:08:25 2012 -0500 @@ -55,7 +55,7 @@ hgweb filerevision, html - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/primes.py') \ + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/primes.py') \ > | sed "s/class=\"k\"/class=\"kn\"/g" | sed "s/class=\"mf\"/class=\"mi\"/g" 200 Script output follows @@ -92,6 +92,7 @@
          • file
          • latest
          • diff
          • +
          • comparison
          • annotate
          • file log
          • raw
          • @@ -183,7 +184,7 @@ hgweb fileannotate, html - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/annotate/tip/primes.py') \ + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/primes.py') \ > | sed "s/class=\"k\"/class=\"kn\"/g" | sed "s/class=\"mi\"/class=\"mf\"/g" 200 Script output follows @@ -222,6 +223,7 @@
          • file
          • latest
          • diff
          • +
          • comparison
          • annotate
          • file log
          • raw
          • @@ -509,7 +511,7 @@ hgweb fileannotate, raw - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/annotate/tip/primes.py?style=raw') \ + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/primes.py?style=raw') \ > | sed "s/test@//" > a $ echo "200 Script output follows" > b $ echo "" >> b @@ -523,7 +525,7 @@ hgweb filerevision, raw - $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/primes.py?style=raw') \ + $ ("$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/primes.py?style=raw') \ > > a $ echo "200 Script output follows" > b $ echo "" >> b @@ -532,7 +534,7 @@ hgweb highlightcss friendly - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/highlightcss' > out + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'highlightcss' > out $ head -n 4 out 200 Script output follows @@ -559,7 +561,7 @@ hgweb highlightcss fruity - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/highlightcss' > out + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'highlightcss' > out $ head -n 4 out 200 Script output follows @@ -583,7 +585,7 @@ > cat hg.pid >> $DAEMON_PIDS > > echo % hgweb filerevision, html - > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/file/tip/$2" \ + > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "file/tip/$2" \ > | grep '
            ' > echo % errors encountered > cat errors.log @@ -603,3 +605,5 @@ % hgweb filerevision, html
            1 ??
            % errors encountered + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-bookmark-motion.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-bookmark-motion.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,187 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ hg init r + $ cd r + + $ for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + + $ hg book -r 1 will-move-backwards + $ hg book -r 2 two + $ hg book -r 2 also-two + $ hg book -r 3 three + $ hg book -r 4 four + $ hg book -r tip five + $ hg log --graph + @ changeset: 5:652413bf663e + | bookmark: five + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | bookmark: four + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | bookmark: three + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | bookmark: also-two + | bookmark: two + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | bookmark: will-move-backwards + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + $ HGEDITOR=cat hg histedit 1 + pick d2ae7f538514 1 b + pick 177f92b77385 2 c + pick 055a42cdd887 3 d + pick e860deea161a 4 e + pick 652413bf663e 5 f + + # Edit history between d2ae7f538514 and 652413bf663e + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cat >> commands.txt < pick 177f92b77385 2 c + > drop d2ae7f538514 1 b + > pick 055a42cdd887 3 d + > fold e860deea161a 4 e + > pick 652413bf663e 5 f + > EOF + $ hg histedit 1 --commands commands.txt --verbose | grep histedit + histedit: Should update metadata for the following changes: + histedit: 055a42cdd887 to ae467701c500 + histedit: moving bookmarks three + histedit: 177f92b77385 to d36c0562f908 + histedit: moving bookmarks also-two, two + histedit: 652413bf663e to 0efacef7cb48 + histedit: moving bookmarks five + histedit: d2ae7f538514 to cb9a9f314b8b + histedit: moving bookmarks will-move-backwards + histedit: e860deea161a to ae467701c500 + histedit: moving bookmarks four + saved backup bundle to $TESTTMP/r/.hg/strip-backup/d2ae7f538514-backup.hg (glob) + saved backup bundle to $TESTTMP/r/.hg/strip-backup/34a9919932c1-backup.hg (glob) + $ hg log --graph + @ changeset: 3:0efacef7cb48 + | bookmark: five + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 2:ae467701c500 + | bookmark: four + | bookmark: three + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 1:d36c0562f908 + | bookmark: also-two + | bookmark: two + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 0:cb9a9f314b8b + bookmark: will-move-backwards + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + $ HGEDITOR=cat hg histedit 1 + pick d36c0562f908 1 c + pick ae467701c500 2 d + pick 0efacef7cb48 3 f + + # Edit history between d36c0562f908 and 0efacef7cb48 + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cat > commands.txt << EOF + > pick d36c0562f908 1 c + > pick 0efacef7cb48 3 f + > pick ae467701c500 2 d + > EOF + $ hg histedit 1 --commands commands.txt --verbose | grep histedit + histedit: Should update metadata for the following changes: + histedit: 0efacef7cb48 to 1be9c35b4cb2 + histedit: moving bookmarks five + histedit: 0efacef7cb48 to 7c044e3e33a9 + histedit: ae467701c500 to 1be9c35b4cb2 + histedit: moving bookmarks four, three + saved backup bundle to $TESTTMP/r/.hg/strip-backup/ae467701c500-backup.hg (glob) + +We expect 'five' to stay at tip, since the tipmost bookmark is most +likely the useful signal. + + $ hg log --graph + @ changeset: 3:1be9c35b4cb2 + | bookmark: five + | bookmark: four + | bookmark: three + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:7c044e3e33a9 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 1:d36c0562f908 + | bookmark: also-two + | bookmark: two + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 0:cb9a9f314b8b + bookmark: will-move-backwards + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-commute.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-commute.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,359 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick 177f92b77385 c + > pick e860deea161a e + > pick 652413bf663e f + > pick 055a42cdd887 d + > EOF + $ initrepo () + > { + > hg init r + > cd r + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > } + + $ initrepo + +log before edit + $ hg log --graph + @ changeset: 5:652413bf663e + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +show the edit commands offered + $ HGEDITOR=cat hg histedit 177f92b77385 + pick 177f92b77385 2 c + pick 055a42cdd887 3 d + pick e860deea161a 4 e + pick 652413bf663e 5 f + + # Edit history between 177f92b77385 and 652413bf663e + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +rules should end up in .hg/histedit-last-edit.txt: + $ cat .hg/histedit-last-edit.txt + pick 177f92b77385 c + pick e860deea161a e + pick 652413bf663e f + pick 055a42cdd887 d + +log after edit + $ hg log --graph + @ changeset: 5:853c68da763f + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 4:26f6a030ae82 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 3:b069cc29fb22 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +put things back + + $ cat > $EDITED < pick 177f92b77385 c + > pick 853c68da763f d + > pick b069cc29fb22 e + > pick 26f6a030ae82 f + > EOF + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ hg log --graph + @ changeset: 5:652413bf663e + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +slightly different this time + + $ cat > $EDITED < pick 055a42cdd887 d + > pick 652413bf663e f + > pick e860deea161a e + > pick 177f92b77385 c + > EOF + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log --graph + @ changeset: 5:99a62755c625 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 4:7c6fdd608667 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:c4f52e213402 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 2:bfe4a5a76b37 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +keep prevents stripping dead revs + $ cat > $EDITED < pick bfe4a5a76b37 d + > pick c4f52e213402 f + > pick 99a62755c625 c + > pick 7c6fdd608667 e + > EOF + $ HGEDITOR="cat \"$EDITED\" > " hg histedit bfe4a5a76b37 --keep 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log --graph + > cat > $EDITED < pick 7c6fdd608667 e + > pick 99a62755c625 c + > EOF + @ changeset: 7:99e266581538 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 6:5ad36efb0653 + | parent: 3:c4f52e213402 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + | o changeset: 5:99a62755c625 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: c + | | + | o changeset: 4:7c6fdd608667 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:c4f52e213402 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 2:bfe4a5a76b37 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +try with --rev + $ hg histedit --commands "$EDITED" --rev -2 2>&1 | fixbundle + abort: may not use changesets other than the ones listed + $ hg log --graph + @ changeset: 7:99e266581538 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 6:5ad36efb0653 + | parent: 3:c4f52e213402 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + | o changeset: 5:99a62755c625 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: c + | | + | o changeset: 4:7c6fdd608667 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:c4f52e213402 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 2:bfe4a5a76b37 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +should also work if a commit message is missing + $ BUNDLE="$TESTDIR/missing-comment.hg" + $ hg init missing + $ cd missing + $ hg unbundle $BUNDLE + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 1 files + (run 'hg update' to get a working copy) + $ hg co tip + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log --graph + @ changeset: 2:bd22688093b3 + | tag: tip + | user: Robert Altman + | date: Mon Nov 28 16:40:04 2011 +0000 + | summary: Update file. + | + o changeset: 1:3b3e956f9171 + | user: Robert Altman + | date: Mon Nov 28 16:37:57 2011 +0000 + | + o changeset: 0:141947992243 + user: Robert Altman + date: Mon Nov 28 16:35:28 2011 +0000 + summary: Checked in text file + + $ hg histedit 0 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-drop.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-drop.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,107 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < drop 177f92b77385 c + > pick e860deea161a e + > pick 652413bf663e f + > pick 055a42cdd887 d + > EOF + $ initrepo () + > { + > hg init r + > cd r + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > } + + $ initrepo + +log before edit + $ hg log --graph + @ changeset: 5:652413bf663e + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +log after edit + $ hg log --graph + @ changeset: 4:708943196e52 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 3:75cbdffecadb + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 2:493dc0964412 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +manifest after edit + $ hg manifest + a + b + d + e + f + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-edit.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-edit.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,178 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick 177f92b77385 c + > pick 055a42cdd887 d + > edit e860deea161a e + > pick 652413bf663e f + > EOF + $ initrepo () + > { + > hg init r + > cd r + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > } + + $ initrepo + +log before edit + $ hg log --graph + @ changeset: 5:652413bf663e + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + abort: Make changes as needed, you may commit or record as needed now. + When you are finished, run hg histedit --continue to resume. + +commit, then edit the revision + $ hg ci -m 'wat' + created new head + $ echo a > e + $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ hg log --graph + @ changeset: 6:bf757c081cd0 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 5:d6b15fed32d4 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: foobaz + | + o changeset: 4:1a60820cd1f6 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: wat + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + + $ hg cat e + a + + $ cat > $EDITED < edit bf757c081cd0 f + > EOF + $ HGEDITOR="cat \"$EDITED\" > " hg histedit tip 2>&1 | fixbundle + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + abort: Make changes as needed, you may commit or record as needed now. + When you are finished, run hg histedit --continue to resume. + $ hg status + A f + $ HGEDITOR='true' hg histedit --continue + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg status + +log after edit + $ hg log --limit 1 + changeset: 6:bf757c081cd0 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: f + + +say we'll change the message, but don't. + $ cat > ../edit.sh < cat "\$1" | sed s/pick/mess/ > tmp + > mv tmp "\$1" + > EOF + $ HGEDITOR="sh ../edit.sh" hg histedit tip 2>&1 | fixbundle + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg status + $ hg log --limit 1 + changeset: 6:bf757c081cd0 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: f + + +modify the message + $ cat > $EDITED < mess bf757c081cd0 f + > EOF + $ HGEDITOR="cat \"$EDITED\" > " hg histedit tip 2>&1 | fixbundle + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg status + $ hg log --limit 1 + changeset: 6:0b16746f8e89 + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: mess bf757c081cd0 f + + +rollback should not work after a histedit + $ hg rollback + no rollback information available + [1] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-fold-non-commute.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-fold-non-commute.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,146 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick 177f92b77385 c + > pick 055a42cdd887 d + > fold bfa474341cc9 does not commute with e + > pick e860deea161a e + > pick 652413bf663e f + > EOF + $ initrepo () + > { + > hg init $1 + > cd $1 + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > echo a >> e + > hg ci -m 'does not commute with e' + > cd .. + > } + + $ initrepo r + $ cd r + +log before edit + $ hg log --graph + @ changeset: 6:bfa474341cc9 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: does not commute with e + | + o changeset: 5:652413bf663e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +fix up + $ echo a > e + $ hg add e + $ cat > cat.py < import sys + > print open(sys.argv[1]).read() + > print + > print + > EOF + $ HGEDITOR="python cat.py" hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed' + d + *** + does not commute with e + + + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + file e already exists + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +just continue this time + $ hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +log after edit + $ hg log --graph + @ changeset: 4:f768fd60ca34 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 3:671efe372e33 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +contents of e + $ hg cat e + a + +manifest + $ hg manifest + a + b + c + d + e + f + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-fold.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-fold.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,185 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick e860deea161a e + > pick 652413bf663e f + > fold 177f92b77385 c + > pick 055a42cdd887 d + > EOF + $ initrepo () + > { + > hg init r + > cd r + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > } + + $ initrepo + +log before edit + $ hg log --graph + @ changeset: 5:652413bf663e + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +log after edit + $ hg log --graph + @ changeset: 4:82b0c1ff1777 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 3:150aafb44a91 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: pick e860deea161a e + | + o changeset: 2:493dc0964412 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +post-fold manifest + $ hg manifest + a + b + c + d + e + f + + $ cd .. + +folding and creating no new change doesn't break: + $ mkdir fold-to-empty-test + $ cd fold-to-empty-test + $ hg init + $ printf "1\n2\n3\n" > file + $ hg add file + $ hg commit -m '1+2+3' + $ echo 4 >> file + $ hg commit -m '+4' + $ echo 5 >> file + $ hg commit -m '+5' + $ echo 6 >> file + $ hg commit -m '+6' + $ hg log --graph + @ changeset: 3:251d831eeec5 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: +6 + | + o changeset: 2:888f9082bf99 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: +5 + | + o changeset: 1:617f94f13c0f + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: +4 + | + o changeset: 0:0189ba417d34 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1+2+3 + + + $ cat > editor.py < import re, sys + > rules = sys.argv[1] + > data = open(rules).read() + > data = re.sub(r'pick ([0-9a-f]{12} 2 \+5)', r'drop \1', data) + > data = re.sub(r'pick ([0-9a-f]{12} 2 \+6)', r'fold \1', data) + > open(rules, 'w').write(data) + > EOF + + $ HGEDITOR='python editor.py' hg histedit 1 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + patching file file + Hunk #1 FAILED at 2 + 1 out of 1 hunks FAILED -- saving rejects to file file.rej + abort: Fix up the change and run hg histedit --continue + [255] +There were conflicts, but we'll continue without resolving. This +should effectively drop the changes from +6. + $ hg status + ? editor.py + ? file.rej + $ hg histedit --continue + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/*-backup.hg (glob) + $ hg log --graph + @ changeset: 1:617f94f13c0f + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: +4 + | + o changeset: 0:0189ba417d34 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: 1+2+3 + + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-no-change.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-no-change.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,187 @@ +test for old histedit issue #6: +editing a changeset without any actual change would corrupt the repository + + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ initrepo () + > { + > dir="$1" + > comment="$2" + > if [ -n "${comment}" ]; then + > echo % ${comment} + > echo % ${comment} | sed 's:.:-:g' + > fi + > hg init ${dir} + > cd ${dir} + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > cd .. + > } + + $ geneditor () + > { + > # generate an editor script for selecting changesets to be edited + > choice=$1 # changesets that should be edited (using sed line ranges) + > cat < # editing the rules, replacing 'pick' with 'edit' for the chosen lines + > sed '${choice}s:^pick:edit:' "\$1" > "\${1}.tmp" + > mv "\${1}.tmp" "\$1" + > # displaying the resulting rules, minus comments and empty lines + > sed '/^#/d;/^$/d;s:^:| :' "\$1" >&2 + > EOF + > } + + $ startediting () + > { + > # begin an editing session + > choice="$1" # changesets that should be edited + > number="$2" # number of changesets considered (from tip) + > comment="$3" + > geneditor "${choice}" > edit.sh + > echo % start editing the history ${comment} + > HGEDITOR="sh ./edit.sh" hg histedit -- -${number} 2>&1 | fixbundle + > } + + $ continueediting () + > { + > # continue an edit already in progress + > editor="$1" # message editor when finalizing editing + > comment="$2" + > echo % finalize changeset editing ${comment} + > HGEDITOR=${editor} hg histedit --continue 2>&1 | fixbundle + > } + + $ graphlog () + > { + > comment="${1:-log}" + > echo % "${comment}" + > hg glog --template '{rev} {node} \"{desc|firstline}\"\n' + > } + + + $ initrepo r1 "test editing with no change" + % test editing with no change + ----------------------------- + $ cd r1 + $ graphlog "log before editing" + % log before editing + @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f" + | + o 4 e860deea161a2f77de56603b340ebbb4536308ae "e" + | + o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d" + | + o 2 177f92b773850b59254aa5e923436f921b55483b "c" + | + o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" + | + o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" + + $ startediting 2 3 "(not changing anything)" # edit the 2nd of 3 changesets + % start editing the history (not changing anything) + | pick 055a42cdd887 3 d + | edit e860deea161a 4 e + | pick 652413bf663e 5 f + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + abort: Make changes as needed, you may commit or record as needed now. + When you are finished, run hg histedit --continue to resume. + $ continueediting true "(leaving commit message unaltered)" + % finalize changeset editing (leaving commit message unaltered) + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + +check state of working copy + $ hg id + 652413bf663e tip + + $ graphlog "log after history editing" + % log after history editing + @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f" + | + o 4 e860deea161a2f77de56603b340ebbb4536308ae "e" + | + o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d" + | + o 2 177f92b773850b59254aa5e923436f921b55483b "c" + | + o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" + | + o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" + + + $ cd .. + + $ initrepo r2 "test editing with no change, then abort" + % test editing with no change, then abort + ----------------------------------------- + $ cd r2 + $ graphlog "log before editing" + % log before editing + @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f" + | + o 4 e860deea161a2f77de56603b340ebbb4536308ae "e" + | + o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d" + | + o 2 177f92b773850b59254aa5e923436f921b55483b "c" + | + o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" + | + o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" + + $ startediting 1,2 3 "(not changing anything)" # edit the 1st two of 3 changesets + % start editing the history (not changing anything) + | edit 055a42cdd887 3 d + | edit e860deea161a 4 e + | pick 652413bf663e 5 f + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + abort: Make changes as needed, you may commit or record as needed now. + When you are finished, run hg histedit --continue to resume. + $ continueediting true "(leaving commit message unaltered)" + % finalize changeset editing (leaving commit message unaltered) + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + abort: Make changes as needed, you may commit or record as needed now. + When you are finished, run hg histedit --continue to resume. + $ graphlog "log after first edit" + % log after first edit + o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f" + | + o 4 e860deea161a2f77de56603b340ebbb4536308ae "e" + | + @ 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d" + | + o 2 177f92b773850b59254aa5e923436f921b55483b "c" + | + o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" + | + o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" + + +abort editing session + $ hg histedit --abort 2>&1 | fixbundle + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ graphlog "log after abort" + % log after abort + @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f" + | + o 4 e860deea161a2f77de56603b340ebbb4536308ae "e" + | + o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d" + | + o 2 177f92b773850b59254aa5e923436f921b55483b "c" + | + o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b" + | + o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a" + + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-non-commute-abort.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-non-commute-abort.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,131 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick 177f92b77385 c + > pick 055a42cdd887 d + > pick bfa474341cc9 does not commute with e + > pick e860deea161a e + > pick 652413bf663e f + > EOF + $ initrepo () + > { + > hg init r + > cd r + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > echo a >> e + > hg ci -m 'does not commute with e' + > cd .. + > } + + $ initrepo + $ cd r + +log before edit + $ hg log --graph + @ changeset: 6:bfa474341cc9 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: does not commute with e + | + o changeset: 5:652413bf663e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +fix up (pre abort) + $ echo a > e + $ hg add e + $ hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + file e already exists + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +abort the edit + $ hg histedit --abort 2>&1 | fixbundle + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + +log after abort + $ hg log --graph + @ changeset: 6:bfa474341cc9 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: does not commute with e + | + o changeset: 5:652413bf663e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-non-commute.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-non-commute.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,244 @@ + $ . "$TESTDIR/histedit-helpers.sh" + + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick 177f92b77385 c + > pick 055a42cdd887 d + > pick bfa474341cc9 does not commute with e + > pick e860deea161a e + > pick 652413bf663e f + > EOF + $ initrepo () + > { + > hg init $1 + > cd $1 + > for x in a b c d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > echo a >> e + > hg ci -m 'does not commute with e' + > cd .. + > } + + $ initrepo r1 + $ cd r1 + +log before edit + $ hg log --graph + @ changeset: 6:bfa474341cc9 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: does not commute with e + | + o changeset: 5:652413bf663e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +abort the edit + $ hg histedit --abort 2>&1 | fixbundle + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + +second edit set + + $ hg log --graph + @ changeset: 6:bfa474341cc9 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: does not commute with e + | + o changeset: 5:652413bf663e + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:e860deea161a + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +edit the history + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +fix up + $ echo a > e + $ hg add e + $ hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + file e already exists + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + +just continue this time + $ hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +log after edit + $ hg log --graph + @ changeset: 5:9ab84894b459 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:1fff3ae8199d + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: does not commute with e + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + +start over + + $ cd .. + + $ initrepo r2 + $ cd r2 + $ cat > $EDITED < pick 177f92b77385 c + > pick 055a42cdd887 d + > mess bfa474341cc9 does not commute with e + > pick e860deea161a e + > pick 652413bf663e f + > EOF + +edit the history, this time with a fold action + $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue + + $ echo a > e + $ hg add e + $ HGEDITOR="cat \"$EDITED\" > " hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + file e already exists + 1 out of 1 hunks FAILED -- saving rejects to file e.rej + abort: Fix up the change and run hg histedit --continue +second edit also fails, but just continue + $ hg histedit --continue 2>&1 | fixbundle + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + +post message fix + $ hg log --graph + @ changeset: 5:6459970fb49b + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: f + | + o changeset: 4:556f27c874b0 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: pick 177f92b77385 c + | + o changeset: 3:055a42cdd887 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + o changeset: 2:177f92b77385 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + o changeset: 1:d2ae7f538514 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:cb9a9f314b8b + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-outgoing.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-outgoing.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,91 @@ + $ cat >> $HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + + $ EDITED="$TESTTMP/editedhistory" + $ cat > $EDITED < pick 177f92b77385 c + > pick e860deea161a e + > pick 652413bf663e f + > pick 055a42cdd887 d + > EOF + $ initrepos () + > { + > hg init r + > cd r + > for x in a b c ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > cd .. + > hg clone r r2 | grep -v updating + > cd r2 + > for x in d e f ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > cd .. + > hg init r3 + > cd r3 + > for x in g h i ; do + > echo $x > $x + > hg add $x + > hg ci -m $x + > done + > cd .. + > } + + $ initrepos + 3 files updated, 0 files merged, 0 files removed, 0 files unresolved + +show the edit commands offered by outgoing + $ cd r2 + $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching + pick 055a42cdd887 3 d + pick e860deea161a 4 e + pick 652413bf663e 5 f + + # Edit history between 055a42cdd887 and 652413bf663e + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd .. + +show the error from unrelated repos + $ cd r3 + $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching + abort: repository is unrelated + [1] + $ cd .. + +show the error from unrelated repos + $ cd r3 + $ HGEDITOR=cat hg histedit --force --outgoing ../r + comparing with ../r + searching for changes + warning: repository is unrelated + pick 2a4042b45417 0 g + pick 68c46b4927ce 1 h + pick 51281e65ba79 2 i + + # Edit history between 2a4042b45417 and 51281e65ba79 + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-histedit-revspec.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-histedit-revspec.t Wed Jul 18 19:08:25 2012 -0500 @@ -0,0 +1,62 @@ +This test requires parentrevspec support in revsets, so check for that +and skip the test if we're on an unusual hg that supports .t tests but +not parentrevspec. + $ python -c 'from mercurial import revset ; revset.methods["parentpost"]' || exit 80 + +Enable extensions used by this test. + $ cat >>$HGRCPATH < [extensions] + > graphlog= + > histedit= + > EOF + +Repo setup. + $ hg init foo + $ cd foo + $ echo alpha >> alpha + $ hg addr + adding alpha + $ hg ci -m one + $ echo alpha >> alpha + $ hg ci -m two + $ echo alpha >> alpha + $ hg ci -m three + $ echo alpha >> alpha + $ hg ci -m four + $ echo alpha >> alpha + $ hg ci -m five + + $ hg log --style compact --graph + @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test + | five + | + o 3 c8e68270e35a 1970-01-01 00:00 +0000 test + | four + | + o 2 eb57da33312f 1970-01-01 00:00 +0000 test + | three + | + o 1 579e40513370 1970-01-01 00:00 +0000 test + | two + | + o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test + one + + +Run a dummy edit to make sure we get tip^^ correctly via revsingle. + $ HGEDITOR=cat hg histedit "tip^^" + pick eb57da33312f 2 three + pick c8e68270e35a 3 four + pick 08d98a8350f3 4 five + + # Edit history between eb57da33312f and 08d98a8350f3 + # + # Commands: + # p, pick = use commit + # e, edit = use commit, but stop for amending + # f, fold = use commit, but fold into previous commit (combines N and N-1) + # d, drop = remove commit from history + # m, mess = edit message without changing commit content + # + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-hook.t --- a/tests/test-hook.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-hook.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,26 +1,26 @@ - $ "$TESTDIR/hghave" system-sh || exit 80 - commit hooks can see env vars $ hg init a $ cd a - $ echo "[hooks]" > .hg/hgrc - $ echo 'commit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit' >> .hg/hgrc - $ echo 'commit.b = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit.b' >> .hg/hgrc - $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python "$TESTDIR"/printenv.py precommit' >> .hg/hgrc - $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py pretxncommit' >> .hg/hgrc - $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc - $ echo 'pre-identify = python "$TESTDIR"/printenv.py pre-identify 1' >> .hg/hgrc - $ echo 'pre-cat = python "$TESTDIR"/printenv.py pre-cat' >> .hg/hgrc - $ echo 'post-cat = python "$TESTDIR"/printenv.py post-cat' >> .hg/hgrc + $ cat > .hg/hgrc < [hooks] + > commit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit" + > commit.b = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" commit.b" + > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= python \"$TESTDIR/printenv.py\" precommit" + > pretxncommit = sh -c "HG_LOCAL= HG_TAG= python \"$TESTDIR/printenv.py\" pretxncommit" + > pretxncommit.tip = hg -q tip + > pre-identify = python "$TESTDIR/printenv.py" pre-identify 1 + > pre-cat = python "$TESTDIR/printenv.py" pre-cat + > post-cat = python "$TESTDIR/printenv.py" post-cat + > EOF $ echo a > a $ hg add a $ hg commit -m a - precommit hook: HG_PARENT1=0000000000000000000000000000000000000000 - pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a + precommit hook: HG_PARENT1=0000000000000000000000000000000000000000 + pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a 0:cb9a9f314b8b - commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 - commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 + commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 + commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 $ hg clone . ../b updating to branch default @@ -29,114 +29,118 @@ changegroup hooks can see env vars - $ echo '[hooks]' > .hg/hgrc - $ echo 'prechangegroup = python "$TESTDIR"/printenv.py prechangegroup' >> .hg/hgrc - $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc - $ echo 'incoming = python "$TESTDIR"/printenv.py incoming' >> .hg/hgrc + $ cat > .hg/hgrc < [hooks] + > prechangegroup = python "$TESTDIR/printenv.py" prechangegroup + > changegroup = python "$TESTDIR/printenv.py" changegroup + > incoming = python "$TESTDIR/printenv.py" incoming + > EOF pretxncommit and commit hooks can see both parents of merge $ cd ../a $ echo b >> a $ hg commit -m a1 -d "1 0" - precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b - pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a + precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a 1:ab228980c14d - commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b - commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b $ hg update -C 0 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo b > b $ hg add b $ hg commit -m b -d '1 0' - precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b - pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a + precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a 2:ee9deb46ab31 - commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b - commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b created new head $ hg merge 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ hg commit -m merge -d '2 0' - precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd - pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a + precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd + pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a 3:07f3376c1e65 - commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd - commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd + commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd + commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd test generic hooks $ hg id - pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[] + pre-identify hook: HG_ARGS=id HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None} HG_PATS=[] warning: pre-identify hook exited with status 1 [1] $ hg cat b - pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] + pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] b - post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0 + post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0 $ cd ../b $ hg pull ../a pulling from ../a searching for changes - prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a + prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a adding changesets adding manifests adding file changes added 3 changesets with 2 changes to 2 files - changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a - incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a - incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a - incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a + changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a + incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a + incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a + incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a (run 'hg update' to get a working copy) tag hooks can see env vars $ cd ../a - $ echo 'pretag = python "$TESTDIR"/printenv.py pretag' >> .hg/hgrc - $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python "$TESTDIR"/printenv.py tag' >> .hg/hgrc + $ cat >> .hg/hgrc < pretag = python "$TESTDIR/printenv.py" pretag + > tag = sh -c "HG_PARENT1= HG_PARENT2= python \"$TESTDIR/printenv.py\" tag" + > EOF $ hg tag -d '3 0' a - pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a - precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 - pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a + pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a + precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 + pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a 4:539e4b31b6dc - tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a - commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 - commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 + tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a + commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 + commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 $ hg tag -l la - pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la - tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la + pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la + tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la pretag hook can forbid tagging - $ echo 'pretag.forbid = python "$TESTDIR"/printenv.py pretag.forbid 1' >> .hg/hgrc + $ echo "pretag.forbid = python \"$TESTDIR/printenv.py\" pretag.forbid 1" >> .hg/hgrc $ hg tag -d '4 0' fa - pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa - pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa + pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa + pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa abort: pretag.forbid hook exited with status 1 [255] $ hg tag -l fla - pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla - pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla + pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla + pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla abort: pretag.forbid hook exited with status 1 [255] pretxncommit hook can see changeset, can roll back txn, changeset no more there after - $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc - $ echo 'pretxncommit.forbid1 = python "$TESTDIR"/printenv.py pretxncommit.forbid 1' >> .hg/hgrc + $ echo "pretxncommit.forbid0 = hg tip -q" >> .hg/hgrc + $ echo "pretxncommit.forbid1 = python \"$TESTDIR/printenv.py\" pretxncommit.forbid 1" >> .hg/hgrc $ echo z > z $ hg add z $ hg -q tip 4:539e4b31b6dc $ hg commit -m 'fail' -d '4 0' - precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 - pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a + precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 + pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a 5:6f611f8018c1 5:6f611f8018c1 - pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a + pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a transaction abort! rollback completed abort: pretxncommit.forbid1 hook exited with status 1 @@ -146,10 +150,10 @@ precommit hook can prevent commit - $ echo 'precommit.forbid = python "$TESTDIR"/printenv.py precommit.forbid 1' >> .hg/hgrc + $ echo "precommit.forbid = python \"$TESTDIR/printenv.py\" precommit.forbid 1" >> .hg/hgrc $ hg commit -m 'fail' -d '4 0' - precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 - precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 + precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 + precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 abort: precommit.forbid hook exited with status 1 [255] $ hg -q tip @@ -157,22 +161,22 @@ preupdate hook can prevent update - $ echo 'preupdate = python "$TESTDIR"/printenv.py preupdate' >> .hg/hgrc + $ echo "preupdate = python \"$TESTDIR/printenv.py\" preupdate" >> .hg/hgrc $ hg update 1 - preupdate hook: HG_PARENT1=ab228980c14d + preupdate hook: HG_PARENT1=ab228980c14d 0 files updated, 0 files merged, 2 files removed, 0 files unresolved update hook - $ echo 'update = python "$TESTDIR"/printenv.py update' >> .hg/hgrc + $ echo "update = python \"$TESTDIR/printenv.py\" update" >> .hg/hgrc $ hg update - preupdate hook: HG_PARENT1=539e4b31b6dc - update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc + preupdate hook: HG_PARENT1=539e4b31b6dc + update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc 2 files updated, 0 files merged, 0 files removed, 0 files unresolved pushkey hook - $ echo 'pushkey = python "$TESTDIR"/printenv.py pushkey' >> .hg/hgrc + $ echo "pushkey = python \"$TESTDIR/printenv.py\" pushkey" >> .hg/hgrc $ cd ../b $ hg bookmark -r null foo $ hg push -B foo ../a @@ -180,50 +184,53 @@ searching for changes no changes found exporting bookmark foo - pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1 + pushkey hook: HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_RET=1 [1] $ cd ../a listkeys hook - $ echo 'listkeys = python "$TESTDIR"/printenv.py listkeys' >> .hg/hgrc + $ echo "listkeys = python \"$TESTDIR/printenv.py\" listkeys" >> .hg/hgrc $ hg bookmark -r null bar $ cd ../b $ hg pull -B bar ../a pulling from ../a - listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} + listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} no changes found - listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} - listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} + listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} + listkeys hook: HG_NAMESPACE=obsolete HG_VALUES={} + listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} + adding remote bookmark bar importing bookmark bar $ cd ../a test that prepushkey can prevent incoming keys - $ echo 'prepushkey = python "$TESTDIR"/printenv.py prepushkey.forbid 1' >> .hg/hgrc + $ echo "prepushkey = python \"$TESTDIR/printenv.py\" prepushkey.forbid 1" >> .hg/hgrc $ cd ../b $ hg bookmark -r null baz $ hg push -B baz ../a pushing to ../a searching for changes no changes found - listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} - listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} - listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} + listkeys hook: HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'} + listkeys hook: HG_NAMESPACE=namespaces HG_VALUES={'bookmarks': '', 'namespaces': '', 'obsolete': '', 'phases': ''} + listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} + listkeys hook: HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'} exporting bookmark baz - prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 + prepushkey.forbid hook: HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 abort: prepushkey hook exited with status 1 [255] $ cd ../a test that prelistkeys can prevent listing keys - $ echo 'prelistkeys = python "$TESTDIR"/printenv.py prelistkeys.forbid 1' >> .hg/hgrc + $ echo "prelistkeys = python \"$TESTDIR/printenv.py\" prelistkeys.forbid 1" >> .hg/hgrc $ hg bookmark -r null quux $ cd ../b $ hg pull -B quux ../a pulling from ../a - prelistkeys.forbid hook: HG_NAMESPACE=bookmarks + prelistkeys.forbid hook: HG_NAMESPACE=bookmarks abort: prelistkeys hook exited with status 1 [255] $ cd ../a @@ -233,21 +240,25 @@ $ cd ../b $ hg -q tip 3:07f3376c1e65 - $ echo '[hooks]' > .hg/hgrc - $ echo 'prechangegroup.forbid = python "$TESTDIR"/printenv.py prechangegroup.forbid 1' >> .hg/hgrc + $ cat > .hg/hgrc < [hooks] + > prechangegroup.forbid = python "$TESTDIR/printenv.py" prechangegroup.forbid 1 + > EOF $ hg pull ../a pulling from ../a searching for changes - prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a + prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a abort: prechangegroup.forbid hook exited with status 1 [255] pretxnchangegroup hook can see incoming changes, can roll back txn, incoming changes no longer there after - $ echo '[hooks]' > .hg/hgrc - $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc - $ echo 'pretxnchangegroup.forbid1 = python "$TESTDIR"/printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc + $ cat > .hg/hgrc < [hooks] + > pretxnchangegroup.forbid0 = hg tip -q + > pretxnchangegroup.forbid1 = python "$TESTDIR/printenv.py" pretxnchangegroup.forbid 1 + > EOF $ hg pull ../a pulling from ../a searching for changes @@ -256,7 +267,7 @@ adding file changes added 1 changesets with 1 changes to 1 files 4:539e4b31b6dc - pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a + pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a transaction abort! rollback completed abort: pretxnchangegroup.forbid1 hook exited with status 1 @@ -267,55 +278,61 @@ outgoing hooks can see env vars $ rm .hg/hgrc - $ echo '[hooks]' > ../a/.hg/hgrc - $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> ../a/.hg/hgrc - $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> ../a/.hg/hgrc + $ cat > ../a/.hg/hgrc < [hooks] + > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing + > outgoing = python "$TESTDIR/printenv.py" outgoing + > EOF $ hg pull ../a pulling from ../a searching for changes - preoutgoing hook: HG_SOURCE=pull + preoutgoing hook: HG_SOURCE=pull adding changesets - outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull + outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull adding manifests adding file changes added 1 changesets with 1 changes to 1 files + adding remote bookmark quux (run 'hg update' to get a working copy) $ hg rollback repository tip rolled back to revision 3 (undo pull) preoutgoing hook can prevent outgoing changes - $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc + $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> ../a/.hg/hgrc $ hg pull ../a pulling from ../a searching for changes - preoutgoing hook: HG_SOURCE=pull - preoutgoing.forbid hook: HG_SOURCE=pull + preoutgoing hook: HG_SOURCE=pull + preoutgoing.forbid hook: HG_SOURCE=pull abort: preoutgoing.forbid hook exited with status 1 [255] outgoing hooks work for local clones $ cd .. - $ echo '[hooks]' > a/.hg/hgrc - $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> a/.hg/hgrc - $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> a/.hg/hgrc + $ cat > a/.hg/hgrc < [hooks] + > preoutgoing = python "$TESTDIR/printenv.py" preoutgoing + > outgoing = python "$TESTDIR/printenv.py" outgoing + > EOF $ hg clone a c - preoutgoing hook: HG_SOURCE=clone - outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone + preoutgoing hook: HG_SOURCE=clone + outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone updating to branch default 3 files updated, 0 files merged, 0 files removed, 0 files unresolved $ rm -rf c preoutgoing hook can prevent outgoing changes for local clones - $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc + $ echo "preoutgoing.forbid = python \"$TESTDIR/printenv.py\" preoutgoing.forbid 1" >> a/.hg/hgrc $ hg clone a zzz - preoutgoing hook: HG_SOURCE=clone - preoutgoing.forbid hook: HG_SOURCE=clone + preoutgoing hook: HG_SOURCE=clone + preoutgoing.forbid hook: HG_SOURCE=clone abort: preoutgoing.forbid hook exited with status 1 [255] - $ cd b + + $ cd "$TESTTMP/b" $ cat > hooktests.py < from mercurial import util @@ -362,7 +379,11 @@ test python hooks - $ PYTHONPATH="`pwd`:$PYTHONPATH" +#if windows + $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH" +#else + $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH" +#endif $ export PYTHONPATH $ echo '[hooks]' > ../a/.hg/hgrc @@ -447,6 +468,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files + adding remote bookmark quux (run 'hg update' to get a working copy) make sure --traceback works @@ -508,6 +530,20 @@ nothing changed [1] + $ echo '[hooks]' > .hg/hgrc + $ echo "update.ne = python:`pwd`/nonexisting.py:testhook" >> .hg/hgrc + $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc + + $ hg up null + loading update.ne hook failed: + abort: No such file or directory: $TESTTMP/d/repo/nonexisting.py + [255] + + $ hg id + loading pre-identify.npmd hook failed: + abort: No module named repo! + [255] + $ cd ../../b make sure --traceback works on hook import failure @@ -522,7 +558,7 @@ $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc $ echo a >> a - $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)' + $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| {4}[a-zA-Z(])' exception from first failed import attempt: Traceback (most recent call last): ImportError: No module named somebogusmodule @@ -530,6 +566,8 @@ Traceback (most recent call last): ImportError: No module named hgext_importfail Traceback (most recent call last): + Abort: precommit.importfail hook is invalid (import of "importfail" failed) + abort: precommit.importfail hook is invalid (import of "importfail" failed) Issue1827: Hooks Update & Commit not completely post operation @@ -594,7 +632,7 @@ $ echo aa >> from/a $ hg --cwd from ci -mb $ hg --cwd from push - pushing to $TESTTMP/to + pushing to $TESTTMP/to (glob) searching for changes adding changesets adding manifests diff -r 98823bd0d697 -r a06e2681dd17 tests/test-http-proxy.t --- a/tests/test-http-proxy.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-http-proxy.t Wed Jul 18 19:08:25 2012 -0500 @@ -105,20 +105,24 @@ * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob) + * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob) + * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob) + * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629 (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob) + * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=obsolete (glob) * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=bookmarks (glob) diff -r 98823bd0d697 -r a06e2681dd17 tests/test-http.t --- a/tests/test-http.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-http.t Wed Jul 18 19:08:25 2012 -0500 @@ -17,9 +17,15 @@ Test server address cannot be reused +#if windows + $ hg serve -p $HGPORT1 2>&1 + abort: cannot start server at ':$HGPORT1': * (glob) + [255] +#else $ hg serve -p $HGPORT1 2>&1 abort: cannot start server at ':$HGPORT1': Address already in use [255] +#endif $ cd .. $ cat hg1.pid hg2.pid >> $DAEMON_PIDS @@ -93,7 +99,7 @@ $ cd copy-pull $ echo '[hooks]' >> .hg/hgrc - $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc + $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc $ hg pull pulling from http://localhost:$HGPORT1/ searching for changes @@ -101,7 +107,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files - changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT1/ + changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT1/ (run 'hg update' to get a working copy) $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-https.t --- a/tests/test-https.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-https.t Wed Jul 18 19:08:25 2012 -0500 @@ -104,9 +104,15 @@ Test server address cannot be reused +#if windows + $ hg serve -p $HGPORT --certificate=$PRIV 2>&1 + abort: cannot start server at ':$HGPORT': (glob) + [255] +#else $ hg serve -p $HGPORT --certificate=$PRIV 2>&1 abort: cannot start server at ':$HGPORT': Address already in use [255] +#endif $ cd .. clone via pull @@ -119,6 +125,7 @@ adding file changes added 1 changesets with 4 changes to 4 files warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) + warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) updating to branch default 4 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg verify -R copy-pull @@ -137,7 +144,7 @@ $ cd copy-pull $ echo '[hooks]' >> .hg/hgrc - $ echo "changegroup = python '$TESTDIR'/printenv.py changegroup" >> .hg/hgrc + $ echo "changegroup = python \"$TESTDIR/printenv.py\" changegroup" >> .hg/hgrc $ hg pull warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) pulling from https://localhost:$HGPORT/ @@ -147,7 +154,8 @@ adding file changes added 1 changesets with 1 changes to 1 files warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) - changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/ + changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/ + warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) (run 'hg update' to get a working copy) $ cd .. @@ -176,6 +184,7 @@ pulling from https://localhost:$HGPORT/ searching for changes no changes found + warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) cacert mismatch @@ -188,6 +197,7 @@ pulling from https://127.0.0.1:$HGPORT/ searching for changes no changes found + warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) $ hg -R copy-pull pull --config web.cacerts=pub-other.pem abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob) [255] @@ -196,6 +206,7 @@ pulling from https://localhost:$HGPORT/ searching for changes no changes found + warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) Test server cert which isn't valid yet @@ -253,6 +264,7 @@ pulling from https://localhost:$HGPORT/ searching for changes no changes found + warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting) Test https with cacert and fingerprint through proxy diff -r 98823bd0d697 -r a06e2681dd17 tests/test-i18n.t --- a/tests/test-i18n.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-i18n.t Wed Jul 18 19:08:25 2012 -0500 @@ -2,6 +2,8 @@ $ "$TESTDIR/hghave" gettext || exit 80 +#if no-outer-repo + Test that translations are compiled and installed correctly. Default encoding in tests is "ascii" and the translation is encoded @@ -22,3 +24,17 @@ $ HGENCODING=Latin-1 LANGUAGE=pt_BR hg tip abortado: n\xe3o foi encontrado um reposit\xf3rio em '$TESTTMP' (.hg n\xe3o encontrado)! (esc) [255] + +#endif + +Test keyword search in translated help text: + + $ HGENCODING=UTF-8 LANGUAGE=de hg help -k blättern + Topics: + + extensions Benutzung erweiterter Funktionen + + Erweiterungen: + + pager Verwendet einen externen Pager zum Bl\xc3\xa4ttern in der Ausgabe von Befehlen (esc) + diff -r 98823bd0d697 -r a06e2681dd17 tests/test-identify.t --- a/tests/test-identify.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-identify.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,4 +1,6 @@ - $ "$TESTDIR/hghave" no-outer-repo serve || exit 80 + $ "$TESTDIR/hghave" serve || exit 80 + +#if no-outer-repo no repo @@ -6,6 +8,8 @@ abort: there is no Mercurial repository here (.hg not found) [255] +#endif + create repo $ hg init test @@ -51,8 +55,10 @@ $ cd .. $ hg -R test id cb9a9f314b8b+ tip +#if no-outer-repo $ hg id test cb9a9f314b8b+ tip +#endif with remote http repo @@ -111,7 +117,8 @@ [255] $ cd .. +#if no-outer-repo $ hg id test abort: unknown repository format: requires features 'fake' (upgrade Mercurial)! [255] - +#endif diff -r 98823bd0d697 -r a06e2681dd17 tests/test-impexp-branch.t --- a/tests/test-impexp-branch.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-impexp-branch.t Wed Jul 18 19:08:25 2012 -0500 @@ -69,3 +69,5 @@ >>> file('../r1-ws.patch', 'wb').write(p) $ hg import --exact ../r1-ws.patch applying ../r1-ws.patch + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-import-bypass.t --- a/tests/test-import-bypass.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-import-bypass.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" symlink execbit || exit 80 - $ echo "[extensions]" >> $HGRCPATH $ echo "purge=" >> $HGRCPATH $ echo "graphlog=" >> $HGRCPATH @@ -218,6 +216,8 @@ $ cd .. +#if symlink execbit + Test complicated patch with --exact $ hg init repo-exact @@ -265,3 +265,6 @@ | o 0:a0e19e636a43 test 0 0 - default - t +#endif + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-import-context.t --- a/tests/test-import-context.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-import-context.t Wed Jul 18 19:08:25 2012 -0500 @@ -123,3 +123,4 @@ $ python ../cat.py d 'A\nA\nA\nA\n' + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-import-git.t --- a/tests/test-import-git.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-import-git.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,6 +1,5 @@ - $ "$TESTDIR/hghave" symlink || exit 80 - - $ hg init + $ hg init repo + $ cd repo New file: @@ -41,14 +40,19 @@ > EOF applying patch from stdin +#if execbit $ hg tip -q 2:3a34410f282e - $ test -x new + $ hg rollback -q +#else + $ hg tip -q + 1:ab199dc869b5 +#endif -Copy: +Copy and removing x bit: - $ hg import -d "1000000 0" -mcopy - < diff --git a/new b/copy > old mode 100755 > new mode 100644 @@ -62,15 +66,37 @@ > EOF applying patch from stdin + $ test -f copy +#if execbit + $ test ! -x copy + $ test -x copyx $ hg tip -q - 3:37bacb7ca14d + 2:21dfaae65c71 +#else + $ hg tip -q + 2:0efdaa8e3bf3 +#endif + + $ hg up -qCr1 + $ hg rollback -q + +Copy (like above but independent of execbit): - $ if "$TESTDIR/hghave" -q execbit; then - > test -f copy -a ! -x copy || echo bad - > test -x copyx || echo bad - > else - > test -f copy || echo bad - > fi + $ hg import -d "1000000 0" -mcopy - < diff --git a/new b/copy + > similarity index 100% + > copy from new + > copy to copy + > diff --git a/new b/copyx + > similarity index 100% + > copy from new + > copy to copyx + > EOF + applying patch from stdin + + $ hg tip -q + 2:0efdaa8e3bf3 + $ test -f copy $ cat copy a @@ -89,7 +115,7 @@ applying patch from stdin $ hg tip -q - 4:47b81a94361d + 3:b1f57753fad2 $ hg locate copyx @@ -111,7 +137,7 @@ applying patch from stdin $ hg tip -q - 5:d9b001d98336 + 4:1bd1da94b9b2 $ hg locate empty @@ -138,7 +164,7 @@ applying patch from stdin $ hg tip -q - 6:ebe901e7576b + 5:46fe99cb3035 Copy and modify: @@ -161,7 +187,7 @@ applying patch from stdin $ hg tip -q - 7:18f368958ecd + 6:ffeb3197c12d $ hg cat copy2 a @@ -191,7 +217,7 @@ applying patch from stdin $ hg tip -q - 8:c32b0d7e6f44 + 7:401aede9e6bb $ hg locate copy2 [1] @@ -215,10 +241,10 @@ applying patch from stdin $ hg tip -q - 9:034a6bf95330 + 8:2ef727e684e8 $ hg log -vr. --template '{rev} {files} / {file_copies}\n' - 9 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2) + 8 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2) $ hg locate rename2 rename3 rename3-2 rename3 @@ -259,7 +285,7 @@ applying patch from stdin $ hg tip -q - 11:c39bce63e786 + 10:27377172366e $ cat foo2 foo @@ -288,7 +314,7 @@ applying patch from stdin $ hg tip -q - 12:30b530085242 + 11:18b73a84b4ab $ hg manifest --debug | grep mbinary 045c85ba38952325e126c70962cc0f9d9077bc67 644 mbinary1 @@ -308,7 +334,7 @@ applying patch from stdin $ hg tip -q - 13:04750ef42fb3 + 12:47500ce1614e $ cat "foo bar" foo @@ -331,7 +357,7 @@ applying patch from stdin $ hg tip -q - 14:c4cd9cdeaa74 + 13:6757efb07ea9 $ cat foo3 foo @@ -366,8 +392,8 @@ Invalid base85 content $ hg rollback - repository tip rolled back to revision 15 (undo import) - working directory now based on revision 15 + repository tip rolled back to revision 14 (undo import) + working directory now based on revision 14 $ hg revert -aq $ hg import -d "1000000 0" -m invalid-binary - <<"EOF" > diff --git a/text2 b/binary2 @@ -532,6 +558,8 @@ $ cat b b +#if symlink + $ ln -s b linkb $ hg add linkb $ hg ci -m addlinkb @@ -554,6 +582,8 @@ ? b.rej ? linkb.rej +#endif + Test corner case involving copies and multiple hunks (issue3384) $ hg revert -qa diff -r 98823bd0d697 -r a06e2681dd17 tests/test-import-merge.t --- a/tests/test-import-merge.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-import-merge.t Wed Jul 18 19:08:25 2012 -0500 @@ -112,3 +112,4 @@ 3:102a90ea7b4a addb $ hg strip --no-backup tip + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-import-unknown.t --- a/tests/test-import-unknown.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-import-unknown.t Wed Jul 18 19:08:25 2012 -0500 @@ -65,3 +65,5 @@ applying ../unknown.diff abort: cannot create copied: destination already exists [255] + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-import.t --- a/tests/test-import.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-import.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - $ hg init a $ mkdir a/d1 $ mkdir a/d1/d2 @@ -54,7 +52,6 @@ > print 'patching file a' > file('a', 'wb').write('line2\n') > EOF - $ chmod +x dummypatch.py $ hg clone -r0 a b adding changesets adding manifests @@ -632,7 +629,7 @@ > rename to bar > EOF applying patch from stdin - abort: path contains illegal component: ../outside/foo + abort: path contains illegal component: ../outside/foo (glob) [255] $ cd .. @@ -757,6 +754,7 @@ $ cat foo a + $ cd .. Issue1859: first line mistaken for email headers @@ -791,7 +789,7 @@ $ cd .. ---- in commit message +in commit message $ hg init commitconfusion $ cd commitconfusion @@ -907,12 +905,16 @@ > new mode 100755 > EOF applying patch from stdin + +#if execbit + $ hg sum parent: 1:d59915696727 tip help management of empty pkg and lib directories in perforce branch: default commit: (clean) update: (current) + $ hg diff --git -c tip diff --git a/lib/place-holder b/lib/place-holder new file mode 100644 @@ -931,6 +933,39 @@ diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash old mode 100644 new mode 100755 + +#else + + $ hg sum + parent: 1:28f089cc9ccc tip + help management of empty pkg and lib directories in perforce + branch: default + commit: (clean) + update: (current) + + $ hg diff --git -c tip + diff --git a/lib/place-holder b/lib/place-holder + new file mode 100644 + --- /dev/null + +++ b/lib/place-holder + @@ -0,0 +1,2 @@ + +perforce does not maintain empty directories. + +this file helps. + diff --git a/pkg/place-holder b/pkg/place-holder + new file mode 100644 + --- /dev/null + +++ b/pkg/place-holder + @@ -0,0 +1,2 @@ + +perforce does not maintain empty directories. + +this file helps. + +/* The mode change for mksys.bash is missing here, because on platforms */ +/* that don't support execbits, mode changes in patches are ignored when */ +/* they are imported. This is obviously also the reason for why the hash */ +/* in the created changeset is different to the one you see above the */ +/* #else clause */ + +#endif $ cd .. @@ -997,6 +1032,8 @@ c3 c4 + $ cd .. + no segfault while importing a unified diff which start line is zero but chunk size is non-zero @@ -1115,3 +1152,4 @@ 4 line + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-inherit-mode.t --- a/tests/test-inherit-mode.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-inherit-mode.t Wed Jul 18 19:08:25 2012 -0500 @@ -146,4 +146,7 @@ $ dirmode=`python ../mode.py .hg/store/data/dir` $ if [ "$storemode" != "$dirmode" ]; then > echo "$storemode != $dirmode" - $ fi + > fi + $ cd .. + + $ cd .. # g-s dir diff -r 98823bd0d697 -r a06e2681dd17 tests/test-init.t --- a/tests/test-init.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-init.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" no-windows || exit 80 - This test tries to exercise the ssh functionality with a dummy script $ checknewrepo() @@ -124,7 +122,7 @@ check names for repositories (clashes with URL schemes, special chars) - $ for i in bundle file hg http https old-http ssh static-http " " "with space"; do + $ for i in bundle file hg http https old-http ssh static-http "with space"; do > printf "hg init \"$i\"... " > hg init "$i" > test -d "$i" -a -d "$i/.hg" && echo "ok" || echo "failed" @@ -137,8 +135,13 @@ hg init "old-http"... ok hg init "ssh"... ok hg init "static-http"... ok - hg init " "... ok hg init "with space"... ok +#if eol-in-paths +/* " " is not a valid name for a directory on Windows */ + $ hg init " " + $ test -d " " + $ test -d " /.hg" +#endif creating 'local/sub/repo' diff -r 98823bd0d697 -r a06e2681dd17 tests/test-inotify-dirty-dirstate.t --- a/tests/test-inotify-dirty-dirstate.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-inotify-dirty-dirstate.t Wed Jul 18 19:08:25 2012 -0500 @@ -68,3 +68,5 @@ $ hg status $ hg qrefresh $ hg status + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-inotify-issue1208.t --- a/tests/test-inotify-issue1208.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-inotify-issue1208.t Wed Jul 18 19:08:25 2012 -0500 @@ -34,3 +34,5 @@ abort: child process failed to start [255] $ kill `cat hg.pid` + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-inotify.t --- a/tests/test-inotify.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-inotify.t Wed Jul 18 19:08:25 2012 -0500 @@ -158,3 +158,5 @@ $ hg st $ kill `cat hg.pid` + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-install.t --- a/tests/test-install.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-install.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,19 +1,19 @@ hg debuginstall $ hg debuginstall - Checking encoding (ascii)... - Checking installed modules (*mercurial)... (glob) - Checking templates (*mercurial?templates)... (glob) - Checking commit editor... - Checking username... - No problems detected + checking encoding (ascii)... + checking installed modules (*mercurial)... (glob) + checking templates (*mercurial?templates)... (glob) + checking commit editor... + checking username... + no problems detected hg debuginstall with no username $ HGUSER= hg debuginstall - Checking encoding (ascii)... - Checking installed modules (*mercurial)... (glob) - Checking templates (*mercurial?templates)... (glob) - Checking commit editor... - Checking username... + checking encoding (ascii)... + checking installed modules (*mercurial)... (glob) + checking templates (*mercurial?templates)... (glob) + checking commit editor... + checking username... no username supplied (see "hg help config") (specify a username in your configuration file) 1 problems detected, please check your install! diff -r 98823bd0d697 -r a06e2681dd17 tests/test-interhg.t --- a/tests/test-interhg.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-interhg.t Wed Jul 18 19:08:25 2012 -0500 @@ -23,9 +23,11 @@ log - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/' | grep bts + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '' | grep bts Issue123: fixed the bug!default tip errors $ cat errors.log + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue1089.t --- a/tests/test-issue1089.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue1089.t Wed Jul 18 19:08:25 2012 -0500 @@ -23,3 +23,4 @@ $ hg ci -m m ../a + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue1802.t --- a/tests/test-issue1802.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue1802.t Wed Jul 18 19:08:25 2012 -0500 @@ -69,3 +69,5 @@ $ hg manifest -v 755 * a 644 b + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue1877.t --- a/tests/test-issue1877.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue1877.t Wed Jul 18 19:08:25 2012 -0500 @@ -43,3 +43,4 @@ $ hg book main 2:d36c0562f908 + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue2137.t --- a/tests/test-issue2137.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue2137.t Wed Jul 18 19:08:25 2012 -0500 @@ -52,3 +52,5 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: one more commit to demonstrate the bug + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue3084.t --- a/tests/test-issue3084.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue3084.t Wed Jul 18 19:08:25 2012 -0500 @@ -106,3 +106,5 @@ $ cat foo large + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue522.t --- a/tests/test-issue522.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue522.t Wed Jul 18 19:08:25 2012 -0500 @@ -49,8 +49,8 @@ c6fc755d7e68f49f880599da29f15add41f42f5a 644 foo $ hg debugindex foo - rev offset length base linkrev nodeid p1 p2 - 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000 - 1 5 9 1 1 6f4310b00b9a 2ed2a3912a0b 000000000000 - 2 14 5 2 2 c6fc755d7e68 6f4310b00b9a 000000000000 + rev offset length ..... linkrev nodeid p1 p2 (re) + 0 0 5 ..... 0 2ed2a3912a0b 000000000000 000000000000 (re) + 1 5 9 ..... 1 6f4310b00b9a 2ed2a3912a0b 000000000000 (re) + 2 14 5 ..... 2 c6fc755d7e68 6f4310b00b9a 000000000000 (re) diff -r 98823bd0d697 -r a06e2681dd17 tests/test-issue672.t --- a/tests/test-issue672.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-issue672.t Wed Jul 18 19:08:25 2012 -0500 @@ -28,7 +28,7 @@ searching for copies back to rev 1 unmatched files in other: 1a - all copies found (* = to merge, ! = divergent): + all copies found (* = to merge, ! = divergent, % = renamed and deleted): 1a -> 1 checking for directory renames resolving manifests @@ -59,7 +59,7 @@ searching for copies back to rev 1 unmatched files in local: 1a - all copies found (* = to merge, ! = divergent): + all copies found (* = to merge, ! = divergent, % = renamed and deleted): 1a -> 1 * checking for directory renames resolving manifests @@ -82,7 +82,7 @@ searching for copies back to rev 1 unmatched files in other: 1a - all copies found (* = to merge, ! = divergent): + all copies found (* = to merge, ! = divergent, % = renamed and deleted): 1a -> 1 * checking for directory renames resolving manifests diff -r 98823bd0d697 -r a06e2681dd17 tests/test-journal-exists.t --- a/tests/test-journal-exists.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-journal-exists.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - $ hg init $ echo a > a $ hg ci -Am0 @@ -24,6 +22,7 @@ Check that zero-size journals are correctly aborted: +#if unix-permissions $ hg bundle -qa repo.hg $ chmod -w foo/.hg/store/00changelog.i @@ -33,4 +32,5 @@ [255] $ if test -f foo/.hg/store/journal; then echo 'journal exists :-('; fi +#endif diff -r 98823bd0d697 -r a06e2681dd17 tests/test-keyword.t --- a/tests/test-keyword.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-keyword.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80 - $ cat <> $HGRCPATH > [extensions] > keyword = @@ -11,6 +9,9 @@ > interactive = true > EOF +hide outer repo + $ hg init + Run kwdemo before [keyword] files are set up as it would succeed without uisetup otherwise @@ -294,16 +295,20 @@ $ echo '$Id$' > c $ echo 'tests for different changenodes' >> c +#if unix-permissions $ chmod 600 c $ ls -l c | cut -b 1-10 -rw------- +#endif commit file c $ hg commit -A -mcndiff -d '1 0' -u 'User Name ' adding c +#if unix-permissions $ ls -l c | cut -b 1-10 -rw------- +#endif force expansion @@ -327,11 +332,11 @@ record chunk - >>> lines = open('a').readlines() + >>> lines = open('a', 'rb').readlines() >>> lines.insert(1, 'foo\n') >>> lines.append('bar\n') - >>> open('a', 'w').writelines(lines) - $ hg record -d '1 10' -m rectest a<>> open('a', 'wb').writelines(lines) + $ hg record -d '10 1' -m rectest a< y > y > n @@ -352,7 +357,7 @@ record change 2/2 to 'a'? [Ynesfdaq?] $ hg identify - d17e03c92c97+ tip + 5f5eb23505c3+ tip $ hg status M a A r @@ -360,7 +365,7 @@ Cat modified file a $ cat a - expand $Id: a,v d17e03c92c97 1970/01/01 00:00:01 test $ + expand $Id: a,v 5f5eb23505c3 1970/01/01 00:00:10 test $ foo do not process $Id: xxx $ @@ -369,8 +374,8 @@ Diff remaining chunk $ hg diff a - diff -r d17e03c92c97 a - --- a/a Wed Dec 31 23:59:51 1969 -0000 + diff -r 5f5eb23505c3 a + --- a/a Thu Jan 01 00:00:09 1970 -0000 +++ b/a * (glob) @@ -2,3 +2,4 @@ foo @@ -388,7 +393,7 @@ - do not use "hg record -m" here! - $ hg record -l msg -d '1 11' a< y > y > y @@ -416,7 +421,7 @@ rollback and revert expansion $ cat a - expand $Id: a,v 59f969a3b52c 1970/01/01 00:00:01 test $ + expand $Id: a,v 78e0a02d76aa 1970/01/01 00:00:11 test $ foo do not process $Id: xxx $ @@ -457,14 +462,14 @@ record added file alone - $ hg -v record -l msg -d '1 12' r< y > EOF diff --git a/r b/r new file mode 100644 examine changes to 'r'? [Ynesfdaq?] r - committed changeset 3:899491280810 + committed changeset 3:82a2f715724d overwriting r expanding keywords - status call required for dirstate.normallookup() check $ hg status r @@ -481,20 +486,35 @@ $ echo '$Id$' > i $ hg add i - $ hg --verbose record -d '1 13' -m recignored< y > EOF diff --git a/i b/i new file mode 100644 examine changes to 'i'? [Ynesfdaq?] i - committed changeset 3:5f40fe93bbdc + committed changeset 3:9f40ceb5a072 $ cat i $Id$ $ hg -q rollback $ hg forget i $ rm i +amend + + $ echo amend >> a + $ echo amend >> b + $ hg -q commit -d '14 1' -m 'prepare amend' + + $ hg --debug commit --amend -d '15 1' -m 'amend without changes' | grep keywords + overwriting a expanding keywords + $ hg -q id + 577e60613a88 + $ head -1 a + expand $Id: a,v 577e60613a88 1970/01/01 00:00:15 test $ + + $ hg -q strip -n tip + Test patch queue repo $ hg init --mq @@ -558,6 +578,7 @@ $ hg --debug commit -ma2c -d '1 0' -u 'User Name ' c c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292 + removing unknown node 40a904bbbe4c from 1-phase boundary overwriting c expanding keywords committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d $ cat a c @@ -593,6 +614,7 @@ cp symlink file; hg cp -A symlink file (part1) - copied symlink points to kwfile: overwrite +#if symlink $ cp sym i $ ls -l i -rw-r--r--* (glob) @@ -605,6 +627,7 @@ expand $Id$ $ hg forget i $ rm i +#endif Test different options of hg kwfiles @@ -641,6 +664,8 @@ $ hg update --clean 0 files updated, 0 files merged, 0 files removed, 0 files unresolved +#if symlink + cp symlink file; hg cp -A symlink file (part2) - copied symlink points to kw ignored file: do not overwrite @@ -662,6 +687,8 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ rm i symignored +#endif + Custom keywordmaps as argument to kwdemo $ hg --quiet kwdemo "Xinfo = {author}: {desc}" @@ -722,6 +749,7 @@ $ hg --debug commit -l log -d '2 0' -u 'User Name ' a + removing unknown node 40a904bbbe4c from 1-phase boundary overwriting a expanding keywords committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83 $ rm log @@ -900,6 +928,7 @@ nonexistent:* (glob) +#if serve hg serve - expand with hgweb file - no expansion with hgweb annotate/changeset/filediff @@ -907,14 +936,14 @@ $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log $ cat hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/a/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'file/tip/a/?style=raw' 200 Script output follows expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $ do not process $Id: xxx $ $Xinfo: User Name : firstline $ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/annotate/tip/a/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'annotate/tip/a/?style=raw' 200 Script output follows @@ -926,7 +955,7 @@ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/tip/?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'rev/tip/?style=raw' 200 Script output follows @@ -946,7 +975,7 @@ +xxx $ +$Xinfo$ - $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/bb948857c743/a?style=raw' + $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'diff/bb948857c743/a?style=raw' 200 Script output follows @@ -963,6 +992,7 @@ $ cat errors.log +#endif Prepare merge and resolve tests @@ -1105,3 +1135,5 @@ $Xinfo$ ignore $Id$ a + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-largefiles-cache.t --- a/tests/test-largefiles-cache.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-largefiles-cache.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" unix-permissions || exit 80 - Create user cache directory $ USERCACHE=`pwd`/cache; export USERCACHE @@ -49,7 +47,7 @@ $ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved getting changed largefiles - large: Can't get file locally + large: can't get file locally (no default or default-push path set in hgrc) 0 largefiles updated, 0 removed $ hg status @@ -67,15 +65,17 @@ $ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved getting changed largefiles - large: Can't get file locally + large: can't get file locally (no default or default-push path set in hgrc) 0 largefiles updated, 0 removed $ hg status ! large + $ cd .. + +#if unix-permissions Portable way to print file permissions: - $ cd .. $ cat > ls-l.py < #!/usr/bin/env python > import sys, os @@ -119,3 +119,7 @@ $ ../ls-l.py ../src/.hg/largefiles/b734e14a0971e370408ab9bce8d56d8485e368a9 640 + + $ cd .. + +#endif diff -r 98823bd0d697 -r a06e2681dd17 tests/test-largefiles.t --- a/tests/test-largefiles.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-largefiles.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,6 +1,5 @@ - $ "$TESTDIR/hghave" symlink unix-permissions serve || exit 80 - $ USERCACHE=`pwd`/cache; export USERCACHE - $ mkdir -p ${USERCACHE} + $ USERCACHE="$TESTTMP/cache"; export USERCACHE + $ mkdir "${USERCACHE}" $ cat >> $HGRCPATH < [extensions] > largefiles= @@ -14,7 +13,7 @@ > patterns=glob:**.dat > usercache=${USERCACHE} > [hooks] - > precommit=echo "Invoking status precommit hook"; hg status + > precommit=sh -c "echo \"Invoking status precommit hook\"; hg status" > EOF Create the repo with a couple of revisions of both large and normal @@ -142,11 +141,12 @@ $ cat sub/large4 large22 +#if hgweb Test display of largefiles in hgweb $ hg serve -d -p $HGPORT --pid-file ../hg.pid $ cat ../hg.pid >> $DAEMON_PIDS - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/?style=raw' 200 Script output follows @@ -155,7 +155,7 @@ -rw-r--r-- 9 normal3 - $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/tip/sub/?style=raw' + $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'file/tip/sub/?style=raw' 200 Script output follows @@ -164,6 +164,7 @@ $ "$TESTDIR/killdaemons.py" +#endif Test archiving the various revisions. These hit corner cases known with archiving. @@ -432,11 +433,52 @@ large11 $ cat sub/large2 large22 + $ cd .. + +Test cloning with --all-largefiles flag + + $ rm "${USERCACHE}"/* + $ hg clone --all-largefiles a a-backup + updating to branch default + 5 files updated, 0 files merged, 0 files removed, 0 files unresolved + getting changed largefiles + 3 largefiles updated, 0 removed + 8 additional largefiles cached + + $ hg clone --all-largefiles a ssh://localhost/a + abort: --all-largefiles is incompatible with non-local destination ssh://localhost/a + [255] + +Test pulling with --all-largefiles flag + + $ rm -Rf a-backup + $ hg clone -r 1 a a-backup + adding changesets + adding manifests + adding file changes + added 2 changesets with 8 changes to 4 files + updating to branch default + 4 files updated, 0 files merged, 0 files removed, 0 files unresolved + getting changed largefiles + 2 largefiles updated, 0 removed + $ rm "${USERCACHE}"/* + $ cd a-backup + $ hg pull --all-largefiles + pulling from $TESTTMP/a (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 6 changesets with 16 changes to 8 files + (run 'hg update' to get a working copy) + caching new largefiles + 3 largefiles cached + 3 additional largefiles cached + $ cd .. Rebasing between two repositories does not revert largefiles to old revisions (this was a very bad bug that took a lot of work to fix). - $ cd .. $ hg clone a d updating to branch default 5 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -473,7 +515,7 @@ Invoking status precommit hook M sub/normal4 M sub2/large6 - saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg + saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-backup.hg (glob) nothing to rebase $ hg log --template '{rev}:{node|short} {desc|firstline}\n' 9:598410d3eb9a modify normal file largefile in repo d @@ -511,7 +553,7 @@ Invoking status precommit hook M sub/normal4 M sub2/large6 - saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg + saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-backup.hg (glob) $ hg log --template '{rev}:{node|short} {desc|firstline}\n' 9:598410d3eb9a modify normal file largefile in repo d 8:a381d2c8c80e modify normal file and largefile in repo b @@ -686,7 +728,7 @@ 3 largefiles updated, 0 removed # Delete the largefiles in the largefiles system cache so that we have an # opportunity to test that caching after a pull works. - $ rm ${USERCACHE}/* + $ rm "${USERCACHE}"/* $ cd f $ echo "large4-merge-test" > sub/large4 $ hg commit -m "Modify large4 to test merge" @@ -804,7 +846,7 @@ normal3-modified $ hg cat sub/large4 large4-modified - $ rm ${USERCACHE}/* + $ rm "${USERCACHE}"/* $ hg cat -r a381d2c8c80e -o cat.out sub/large4 $ cat cat.out large4-modified @@ -839,6 +881,7 @@ (use 'hg revert new-largefile' to cancel the pending addition) $ cd .. +#if serve vanilla clients not locked out from largefiles servers on vanilla repos $ mkdir r1 $ cd r1 @@ -871,6 +914,8 @@ added 1 changesets with 1 changes to 1 files updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +#endif + vanilla clients locked out from largefiles http repos $ mkdir r4 @@ -882,6 +927,8 @@ Invoking status precommit hook A f1 $ cd .. + +#if serve $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid $ cat hg.pid >> $DAEMON_PIDS $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5 @@ -894,6 +941,7 @@ used all HGPORTs, kill all daemons $ "$TESTDIR/killdaemons.py" +#endif vanilla clients locked out from largefiles ssh repos $ hg --config extensions.largefiles=! clone -e "python \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5 @@ -904,6 +952,8 @@ Please enable it in your Mercurial config file. [255] +#if serve + largefiles clients refuse to push largefiles repos to vanilla servers $ mkdir r6 $ cd r6 @@ -939,7 +989,7 @@ putlfile errors are shown (issue3123) Corrupt the cached largefile in r7 - $ echo corruption > $USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8 + $ echo corruption > "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8" $ hg init empty $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \ > --config 'web.allow_push=*' --config web.push_ssl=False @@ -948,7 +998,7 @@ pushing to http://localhost:$HGPORT1/ searching for changes remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash - abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/ + abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/ (glob) [255] $ rm -rf empty @@ -963,7 +1013,7 @@ $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \ > --config 'web.allow_push=*' --config web.push_ssl=False $ cat hg.pid >> $DAEMON_PIDS - $ rm ${USERCACHE}/* + $ rm "${USERCACHE}"/* $ hg push -R r8 http://localhost:$HGPORT2 pushing to http://localhost:$HGPORT2/ searching for changes @@ -977,6 +1027,11 @@ used all HGPORTs, kill all daemons $ "$TESTDIR/killdaemons.py" +#endif + + +#if unix-permissions + Clone a local repository owned by another user We have to simulate that here by setting $HOME and removing write permissions $ ORIGHOME="$HOME" @@ -1010,6 +1065,10 @@ $ chmod -R u+w alice/pubrepo $ HOME="$ORIGHOME" +#endif + +#if symlink + Symlink to a large largefile should behave the same as a symlink to a normal file $ hg init largesymlink $ cd largesymlink @@ -1035,6 +1094,8 @@ $ test -L largelink $ cd .. +#endif + test for pattern matching on 'hg status': to boost performance, largefiles checks whether specified patterns are related to largefiles in working directory (NOT to STANDIN) or not. @@ -1136,4 +1197,37 @@ abort: uncommitted changes in subrepo subrepo (use --subrepos for recursive commit) [255] + +Add a normal file to the subrepo, then test archiving + + $ echo 'normal file' > subrepo/normal.txt + $ hg -R subrepo add subrepo/normal.txt + +Lock in subrepo, otherwise the change isn't archived + + $ hg ci -S -m "add normal file to top level" + committing subrepository subrepo + Invoking status precommit hook + M large.txt + A normal.txt + Invoking status precommit hook + M .hgsubstate + $ hg archive -S lf_subrepo_archive + $ find lf_subrepo_archive | sort + lf_subrepo_archive + lf_subrepo_archive/.hg_archival.txt + lf_subrepo_archive/.hgsub + lf_subrepo_archive/.hgsubstate + lf_subrepo_archive/a + lf_subrepo_archive/a/b + lf_subrepo_archive/a/b/c + lf_subrepo_archive/a/b/c/d + lf_subrepo_archive/a/b/c/d/e.large.txt + lf_subrepo_archive/a/b/c/d/e.normal.txt + lf_subrepo_archive/a/b/c/x + lf_subrepo_archive/a/b/c/x/y.normal.txt + lf_subrepo_archive/subrepo + lf_subrepo_archive/subrepo/large.txt + lf_subrepo_archive/subrepo/normal.txt + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-lfconvert.t --- a/tests/test-lfconvert.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-lfconvert.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" symlink || exit 80 - $ cat >> $HGRCPATH < [extensions] > largefiles = @@ -33,6 +31,7 @@ adding sub/normal2 $ hg commit -m"add large, normal1" large normal1 $ hg commit -m"add sub/*" sub + Test tag parsing $ cat >> .hgtags < IncorrectlyFormattedTag! @@ -41,10 +40,8 @@ > EOF $ hg add .hgtags $ hg commit -m"add large2" large2 .hgtags - $ hg rename large2 large3 + Test link+rename largefile codepath - $ ln -sf large large3 - $ hg commit -m"make large2 a symlink" large2 large3 $ [ -d .hg/largefiles ] && echo fail || echo pass pass $ cd .. @@ -53,13 +50,24 @@ skipping incorrectly formatted tag IncorrectlyFormattedTag! skipping incorrectly formatted id invalidhash no mapping for id 0123456789abcdef +#if symlink + $ hg --cwd bigfile-repo rename large2 large3 + $ ln -sf large bigfile-repo/large3 + $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3 + $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink + initializing destination largefiles-repo-symlink + skipping incorrectly formatted tag IncorrectlyFormattedTag! + skipping incorrectly formatted id invalidhash + no mapping for id 0123456789abcdef abort: renamed/copied largefile large3 becomes symlink [255] +#endif $ cd bigfile-repo $ hg strip --no-backup 2 0 files updated, 0 files merged, 2 files removed, 0 files unresolved $ cd .. - $ rm -rf largefiles-repo + $ rm -rf largefiles-repo largefiles-repo-symlink + $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo initializing destination largefiles-repo @@ -260,3 +268,5 @@ stuff/normal2 $ [ -d .hg/largefiles ] && echo fail || echo pass pass + + $ cd .. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-locate.t --- a/tests/test-locate.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-locate.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,5 @@ - $ hg init t - $ cd t + $ hg init repo + $ cd repo $ echo 0 > a $ echo 0 > b $ echo 0 > t.h @@ -118,3 +118,4 @@ ../t.h (glob) ../t/e.h (glob) + $ cd ../.. diff -r 98823bd0d697 -r a06e2681dd17 tests/test-log.t --- a/tests/test-log.t Thu Jul 19 00:53:27 2012 +0200 +++ b/tests/test-log.t Wed Jul 18 19:08:25 2012 -0500 @@ -1,5 +1,3 @@ - $ "$TESTDIR/hghave" execbit || exit 80 - The g is crafted to have 2 filelog topological heads in a linear changeset graph @@ -275,10 +273,12 @@ log copies, execute bit set +#if execbit $ chmod +x e $ hg ci -me3 -d '7 0' $ hg log -v --template '{rev} {file_copies}\n' -r 6 6 +#endif log -p d @@ -331,12 +331,12 @@ a - + $ cd .. log --follow tests - $ hg init ../follow - $ cd ../follow + $ hg init follow + $ cd follow $ echo base > base $ hg ci -Ambase -d '1 0' @@ -539,34 +539,6 @@ date: Thu Jan 01 00:00:01 1970 +0000 summary: r1 -log -d " " (whitespaces only) - - $ hg log -d " " - abort: dates cannot consist entirely of whitespace - [255] - -log -d -1 - - $ hg log -d -1 - -log -d ">" - - $ hg log -d ">" - abort: invalid day spec, use '>DATE' - [255] - -log -d "<" - - $ hg log -d "<" - abort: invalid day spec, use '