##// END OF EJS Templates
merge with mpm
Benoit Boissinot -
r7433:c4ce828e merge default
parent child Browse files
Show More
@@ -1,65 +1,72 b''
1 #!/bin/sh
1 #!/bin/sh
2 #
2 #
3 # Build a Mercurial RPM in place.
3 # Build a Mercurial RPM in place.
4 # Known to work on:
4 # Known to work on:
5 # - Fedora 9
5 # - Fedora 9
6 # - Fedora 10
6 #
7 #
7 # Bryan O'Sullivan <bos@serpentine.com>
8 # Bryan O'Sullivan <bos@serpentine.com>
8
9
10 if hg --version > /dev/null 2>&1; then :
11 else
12 echo 'hg command not available!' 1>&2
13 exit 1
14 fi
15
9 root="`hg root 2>/dev/null`"
16 root="`hg root 2>/dev/null`"
10 specfile=contrib/mercurial.spec
17 specfile=contrib/mercurial.spec
11
18
12 if [ -z "$root" ]; then
19 if [ -z "$root" ]; then
13 echo 'You are not inside a Mercurial repository!' 1>&2
20 echo 'You are not inside a Mercurial repository!' 1>&2
14 exit 1
21 exit 1
15 fi
22 fi
16
23
17 rpmdir=/tmp/"`basename $root | sed 's/ /_/'`"-rpm # FIXME: Insecure /tmp handling
24 rpmdir=/tmp/"`basename $root | sed 's/ /_/'`"-rpm # FIXME: Insecure /tmp handling
18
25
19 cd "$root"
26 cd "$root"
20 rm -rf $rpmdir
27 rm -rf $rpmdir
21 mkdir -p $rpmdir/RPMS
28 mkdir -p $rpmdir/RPMS
22 hg clone "$root" $rpmdir/BUILD
29 hg clone "$root" $rpmdir/BUILD
23
30
24 if [ ! -f $specfile ]; then
31 if [ ! -f $specfile ]; then
25 echo "Cannot find $specfile!" 1>&2
32 echo "Cannot find $specfile!" 1>&2
26 exit 1
33 exit 1
27 fi
34 fi
28
35
29 tmpspec=/tmp/`basename "$specfile"`.$$ # FIXME: Insecure /tmp handling
36 tmpspec=/tmp/`basename "$specfile"`.$$ # FIXME: Insecure /tmp handling
30 # Use the most recent tag as the version.
37 # Use the most recent tag as the version.
31 version=`hg tags | perl -e 'while(<STDIN>){if(/^(\d\S+)/){print$1;exit}}'`
38 version=`hg tags | perl -e 'while(<STDIN>){if(/^(\d\S+)/){print$1;exit}}'`
32 # Compute the release number as the difference in revision numbers
39 # Compute the release number as the difference in revision numbers
33 # between the tip and the most recent tag.
40 # between the tip and the most recent tag.
34 release=`hg tags | perl -e 'while(<STDIN>){($tag,$id)=/^(\S+)\s+(\d+)/;if($tag eq "tip"){$tip = $id}elsif($tag=~/^\d/){print $tip-$id+1;exit}}'`
41 release=`hg tags | perl -e 'while(<STDIN>){($tag,$id)=/^(\S+)\s+(\d+)/;if($tag eq "tip"){$tip = $id}elsif($tag=~/^\d/){print $tip-$id+1;exit}}'`
35 tip=`hg -q tip`
42 tip=`hg -q tip`
36
43
37 # Beat up the spec file
44 # Beat up the spec file
38 sed -e 's,^Source:.*,Source: /dev/null,' \
45 sed -e 's,^Source:.*,Source: /dev/null,' \
39 -e "s,^Version:.*,Version: $version," \
46 -e "s,^Version:.*,Version: $version," \
40 -e "s,^Release:.*,Release: $release," \
47 -e "s,^Release:.*,Release: $release," \
41 -e "s,^%prep.*,Changeset: $tip\n\0," \
48 -e "s,^%prep.*,Changeset: $tip\n\0," \
42 -e 's,^%setup.*,,' \
49 -e 's,^%setup.*,,' \
43 $specfile > $tmpspec
50 $specfile > $tmpspec
44
51
45 cat <<EOF >> $tmpspec
52 cat <<EOF >> $tmpspec
46 %changelog
53 %changelog
47 * `date +'%a %b %d %Y'` `hg showconfig ui.username` $version-$release
54 * `date +'%a %b %d %Y'` `hg showconfig ui.username` $version-$release
48 - Automatically built via $0
55 - Automatically built via $0
49
56
50 EOF
57 EOF
51 hg log \
58 hg log \
52 --template '* {date|rfc822date} {author}\n- {desc|firstline}\n\n' \
59 --template '* {date|rfc822date} {author}\n- {desc|firstline}\n\n' \
53 .hgtags \
60 .hgtags \
54 | sed -e 's/^\(\* [MTWFS][a-z][a-z]\), \([0-3][0-9]\) \([A-Z][a-z][a-z]\) /\1 \3 \2 /' \
61 | sed -e 's/^\(\* [MTWFS][a-z][a-z]\), \([0-3][0-9]\) \([A-Z][a-z][a-z]\) /\1 \3 \2 /' \
55 -e '/^\* [MTWFS][a-z][a-z] /{s/ [012][0-9]:[0-9][0-9]:[0-9][0-9] [+-][0-9]\{4\}//}' \
62 -e '/^\* [MTWFS][a-z][a-z] /{s/ [012][0-9]:[0-9][0-9]:[0-9][0-9] [+-][0-9]\{4\}//}' \
56 >> $tmpspec
63 >> $tmpspec
57
64
58 rpmbuild --define "_topdir $rpmdir" -bb $tmpspec
65 rpmbuild --define "_topdir $rpmdir" -bb $tmpspec
59 if [ $? = 0 ]; then
66 if [ $? = 0 ]; then
60 rm -rf $tmpspec $rpmdir/BUILD
67 rm -rf $tmpspec $rpmdir/BUILD
61 mv $rpmdir/RPMS/*/* $rpmdir && rm -r $rpmdir/RPMS
68 mv $rpmdir/RPMS/*/* $rpmdir && rm -r $rpmdir/RPMS
62 echo
69 echo
63 echo "Packages are in $rpmdir:"
70 echo "Packages are in $rpmdir:"
64 ls -l $rpmdir/*.rpm
71 ls -l $rpmdir/*.rpm
65 fi
72 fi
@@ -1,78 +1,76 b''
1 Summary: Mercurial -- a distributed SCM
1 Summary: Mercurial -- a distributed SCM
2 Name: mercurial
2 Name: mercurial
3 Version: snapshot
3 Version: snapshot
4 Release: 0
4 Release: 0
5 License: GPL
5 License: GPL
6 Group: Development/Tools
6 Group: Development/Tools
7 Source: http://www.selenic.com/mercurial/release/%{name}-%{version}.tar.gz
7 Source: http://www.selenic.com/mercurial/release/%{name}-%{version}.tar.gz
8 URL: http://www.selenic.com/mercurial
8 URL: http://www.selenic.com/mercurial
9 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
9 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
10
10
11 # From the README:
11 # From the README:
12 #
12 #
13 # Note: some distributions fails to include bits of distutils by
13 # Note: some distributions fails to include bits of distutils by
14 # default, you'll need python-dev to install. You'll also need a C
14 # default, you'll need python-dev to install. You'll also need a C
15 # compiler and a 3-way merge tool like merge, tkdiff, or kdiff3.
15 # compiler and a 3-way merge tool like merge, tkdiff, or kdiff3.
16 #
16 #
17 # python-devel provides an adequate python-dev. The merge tool is a
17 # python-devel provides an adequate python-dev. The merge tool is a
18 # run-time dependency.
18 # run-time dependency.
19 #
19 #
20 BuildRequires: python >= 2.3, python-devel, make, gcc, asciidoc, xmlto
20 BuildRequires: python >= 2.3, python-devel, make, gcc, asciidoc, xmlto
21
21
22 %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
22 %define pythonver %(python -c 'import sys;print ".".join(map(str, sys.version_info[:2]))')
23 %define pythonlib %{_libdir}/python%{pythonver}/site-packages/%{name}
23 %define pythonlib %{_libdir}/python%{pythonver}/site-packages/%{name}
24 %define hgext %{_libdir}/python%{pythonver}/site-packages/hgext
24 %define hgext %{_libdir}/python%{pythonver}/site-packages/hgext
25
25
26 %description
26 %description
27 Mercurial is a fast, lightweight source control management system designed
27 Mercurial is a fast, lightweight source control management system designed
28 for efficient handling of very large distributed projects.
28 for efficient handling of very large distributed projects.
29
29
30 %prep
30 %prep
31 %setup -q
31 %setup -q
32
32
33 %build
33 %build
34 make all
34 make all
35
35
36 %install
36 %install
37 rm -rf $RPM_BUILD_ROOT
37 rm -rf $RPM_BUILD_ROOT
38 python setup.py install --root $RPM_BUILD_ROOT --prefix %{_prefix}
38 python setup.py install --root $RPM_BUILD_ROOT --prefix %{_prefix}
39 make install-doc DESTDIR=$RPM_BUILD_ROOT MANDIR=%{_mandir}
39 make install-doc DESTDIR=$RPM_BUILD_ROOT MANDIR=%{_mandir}
40
40
41 install contrib/hgk $RPM_BUILD_ROOT%{_bindir}
41 install contrib/hgk $RPM_BUILD_ROOT%{_bindir}
42 install contrib/convert-repo $RPM_BUILD_ROOT%{_bindir}/mercurial-convert-repo
42 install contrib/convert-repo $RPM_BUILD_ROOT%{_bindir}/mercurial-convert-repo
43 install contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}
43 install contrib/hg-ssh $RPM_BUILD_ROOT%{_bindir}
44 install contrib/git-viz/{hg-viz,git-rev-tree} $RPM_BUILD_ROOT%{_bindir}
44 install contrib/git-viz/{hg-viz,git-rev-tree} $RPM_BUILD_ROOT%{_bindir}
45
45
46 bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
46 bash_completion_dir=$RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
47 mkdir -p $bash_completion_dir
47 mkdir -p $bash_completion_dir
48 install contrib/bash_completion $bash_completion_dir/mercurial.sh
48 install contrib/bash_completion $bash_completion_dir/mercurial.sh
49
49
50 zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions
50 zsh_completion_dir=$RPM_BUILD_ROOT%{_datadir}/zsh/site-functions
51 mkdir -p $zsh_completion_dir
51 mkdir -p $zsh_completion_dir
52 install contrib/zsh_completion $zsh_completion_dir/_mercurial
52 install contrib/zsh_completion $zsh_completion_dir/_mercurial
53
53
54 lisp_dir=$RPM_BUILD_ROOT%{_datadir}/emacs/site-lisp
54 lisp_dir=$RPM_BUILD_ROOT%{_datadir}/emacs/site-lisp
55 mkdir -p $lisp_dir
55 mkdir -p $lisp_dir
56 install contrib/mercurial.el $lisp_dir
56 install contrib/mercurial.el $lisp_dir
57
57
58 # We don't want this, do we?
59 rm -f $RPM_BUILD_ROOT%{pythonlib}/../mercurial-*-py2.5.egg-info
60
61 %clean
58 %clean
62 rm -rf $RPM_BUILD_ROOT
59 rm -rf $RPM_BUILD_ROOT
63
60
64 %files
61 %files
65 %defattr(-,root,root,-)
62 %defattr(-,root,root,-)
66 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html doc/ja *.cgi
63 %doc CONTRIBUTORS COPYING doc/README doc/hg*.txt doc/hg*.html doc/ja *.cgi
67 %{_mandir}/man?/hg*.gz
64 %{_mandir}/man?/hg*.gz
68 %{_sysconfdir}/bash_completion.d/mercurial.sh
65 %{_sysconfdir}/bash_completion.d/mercurial.sh
69 %{_datadir}/zsh/site-functions/_mercurial
66 %{_datadir}/zsh/site-functions/_mercurial
70 %{_datadir}/emacs/site-lisp/mercurial.el
67 %{_datadir}/emacs/site-lisp/mercurial.el
71 %{_bindir}/hg
68 %{_bindir}/hg
72 %{_bindir}/hgk
69 %{_bindir}/hgk
73 %{_bindir}/hg-ssh
70 %{_bindir}/hg-ssh
74 %{_bindir}/hg-viz
71 %{_bindir}/hg-viz
75 %{_bindir}/git-rev-tree
72 %{_bindir}/git-rev-tree
76 %{_bindir}/mercurial-convert-repo
73 %{_bindir}/mercurial-convert-repo
74 %{_libdir}/python%{pythonver}/site-packages/%{name}-*-py2.5.egg-info
77 %{pythonlib}
75 %{pythonlib}
78 %{hgext}
76 %{hgext}
@@ -1,1980 +1,1980 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp
18 import imp
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 subprocess.Popen # trigger ImportError early
54 subprocess.Popen # trigger ImportError early
55 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
56 def popen2(cmd, mode='t', bufsize=-1):
56 def popen2(cmd, mode='t', bufsize=-1):
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 close_fds=closefds,
58 close_fds=closefds,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 return p.stdin, p.stdout
60 return p.stdin, p.stdout
61 def popen3(cmd, mode='t', bufsize=-1):
61 def popen3(cmd, mode='t', bufsize=-1):
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 close_fds=closefds,
63 close_fds=closefds,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stderr=subprocess.PIPE)
65 stderr=subprocess.PIPE)
66 return p.stdin, p.stdout, p.stderr
66 return p.stdin, p.stdout, p.stderr
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 stderr = capturestderr and subprocess.PIPE or None
68 stderr = capturestderr and subprocess.PIPE or None
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 close_fds=closefds,
70 close_fds=closefds,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 stderr=stderr)
72 stderr=stderr)
73 p.fromchild = p.stdout
73 p.fromchild = p.stdout
74 p.tochild = p.stdin
74 p.tochild = p.stdin
75 p.childerr = p.stderr
75 p.childerr = p.stderr
76 return p
76 return p
77 except ImportError:
77 except ImportError:
78 subprocess = None
78 subprocess = None
79 from popen2 import Popen3
79 from popen2 import Popen3
80 popen2 = os.popen2
80 popen2 = os.popen2
81 popen3 = os.popen3
81 popen3 = os.popen3
82
82
83
83
84 try:
84 try:
85 _encoding = os.environ.get("HGENCODING")
85 _encoding = os.environ.get("HGENCODING")
86 if sys.platform == 'darwin' and not _encoding:
86 if sys.platform == 'darwin' and not _encoding:
87 # On darwin, getpreferredencoding ignores the locale environment and
87 # On darwin, getpreferredencoding ignores the locale environment and
88 # always returns mac-roman. We override this if the environment is
88 # always returns mac-roman. We override this if the environment is
89 # not C (has been customized by the user).
89 # not C (has been customized by the user).
90 locale.setlocale(locale.LC_CTYPE, '')
90 locale.setlocale(locale.LC_CTYPE, '')
91 _encoding = locale.getlocale()[1]
91 _encoding = locale.getlocale()[1]
92 if not _encoding:
92 if not _encoding:
93 _encoding = locale.getpreferredencoding() or 'ascii'
93 _encoding = locale.getpreferredencoding() or 'ascii'
94 except locale.Error:
94 except locale.Error:
95 _encoding = 'ascii'
95 _encoding = 'ascii'
96 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
96 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
97 _fallbackencoding = 'ISO-8859-1'
97 _fallbackencoding = 'ISO-8859-1'
98
98
99 def tolocal(s):
99 def tolocal(s):
100 """
100 """
101 Convert a string from internal UTF-8 to local encoding
101 Convert a string from internal UTF-8 to local encoding
102
102
103 All internal strings should be UTF-8 but some repos before the
103 All internal strings should be UTF-8 but some repos before the
104 implementation of locale support may contain latin1 or possibly
104 implementation of locale support may contain latin1 or possibly
105 other character sets. We attempt to decode everything strictly
105 other character sets. We attempt to decode everything strictly
106 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
106 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
107 replace unknown characters.
107 replace unknown characters.
108 """
108 """
109 for e in ('UTF-8', _fallbackencoding):
109 for e in ('UTF-8', _fallbackencoding):
110 try:
110 try:
111 u = s.decode(e) # attempt strict decoding
111 u = s.decode(e) # attempt strict decoding
112 return u.encode(_encoding, "replace")
112 return u.encode(_encoding, "replace")
113 except LookupError, k:
113 except LookupError, k:
114 raise Abort(_("%s, please check your locale settings") % k)
114 raise Abort(_("%s, please check your locale settings") % k)
115 except UnicodeDecodeError:
115 except UnicodeDecodeError:
116 pass
116 pass
117 u = s.decode("utf-8", "replace") # last ditch
117 u = s.decode("utf-8", "replace") # last ditch
118 return u.encode(_encoding, "replace")
118 return u.encode(_encoding, "replace")
119
119
120 def fromlocal(s):
120 def fromlocal(s):
121 """
121 """
122 Convert a string from the local character encoding to UTF-8
122 Convert a string from the local character encoding to UTF-8
123
123
124 We attempt to decode strings using the encoding mode set by
124 We attempt to decode strings using the encoding mode set by
125 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
125 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
126 characters will cause an error message. Other modes include
126 characters will cause an error message. Other modes include
127 'replace', which replaces unknown characters with a special
127 'replace', which replaces unknown characters with a special
128 Unicode character, and 'ignore', which drops the character.
128 Unicode character, and 'ignore', which drops the character.
129 """
129 """
130 try:
130 try:
131 return s.decode(_encoding, _encodingmode).encode("utf-8")
131 return s.decode(_encoding, _encodingmode).encode("utf-8")
132 except UnicodeDecodeError, inst:
132 except UnicodeDecodeError, inst:
133 sub = s[max(0, inst.start-10):inst.start+10]
133 sub = s[max(0, inst.start-10):inst.start+10]
134 raise Abort("decoding near '%s': %s!" % (sub, inst))
134 raise Abort("decoding near '%s': %s!" % (sub, inst))
135 except LookupError, k:
135 except LookupError, k:
136 raise Abort(_("%s, please check your locale settings") % k)
136 raise Abort(_("%s, please check your locale settings") % k)
137
137
138 def locallen(s):
138 def locallen(s):
139 """Find the length in characters of a local string"""
139 """Find the length in characters of a local string"""
140 return len(s.decode(_encoding, "replace"))
140 return len(s.decode(_encoding, "replace"))
141
141
142 # used by parsedate
142 # used by parsedate
143 defaultdateformats = (
143 defaultdateformats = (
144 '%Y-%m-%d %H:%M:%S',
144 '%Y-%m-%d %H:%M:%S',
145 '%Y-%m-%d %I:%M:%S%p',
145 '%Y-%m-%d %I:%M:%S%p',
146 '%Y-%m-%d %H:%M',
146 '%Y-%m-%d %H:%M',
147 '%Y-%m-%d %I:%M%p',
147 '%Y-%m-%d %I:%M%p',
148 '%Y-%m-%d',
148 '%Y-%m-%d',
149 '%m-%d',
149 '%m-%d',
150 '%m/%d',
150 '%m/%d',
151 '%m/%d/%y',
151 '%m/%d/%y',
152 '%m/%d/%Y',
152 '%m/%d/%Y',
153 '%a %b %d %H:%M:%S %Y',
153 '%a %b %d %H:%M:%S %Y',
154 '%a %b %d %I:%M:%S%p %Y',
154 '%a %b %d %I:%M:%S%p %Y',
155 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
155 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
156 '%b %d %H:%M:%S %Y',
156 '%b %d %H:%M:%S %Y',
157 '%b %d %I:%M:%S%p %Y',
157 '%b %d %I:%M:%S%p %Y',
158 '%b %d %H:%M:%S',
158 '%b %d %H:%M:%S',
159 '%b %d %I:%M:%S%p',
159 '%b %d %I:%M:%S%p',
160 '%b %d %H:%M',
160 '%b %d %H:%M',
161 '%b %d %I:%M%p',
161 '%b %d %I:%M%p',
162 '%b %d %Y',
162 '%b %d %Y',
163 '%b %d',
163 '%b %d',
164 '%H:%M:%S',
164 '%H:%M:%S',
165 '%I:%M:%SP',
165 '%I:%M:%SP',
166 '%H:%M',
166 '%H:%M',
167 '%I:%M%p',
167 '%I:%M%p',
168 )
168 )
169
169
170 extendeddateformats = defaultdateformats + (
170 extendeddateformats = defaultdateformats + (
171 "%Y",
171 "%Y",
172 "%Y-%m",
172 "%Y-%m",
173 "%b",
173 "%b",
174 "%b %Y",
174 "%b %Y",
175 )
175 )
176
176
177 class SignalInterrupt(Exception):
177 class SignalInterrupt(Exception):
178 """Exception raised on SIGTERM and SIGHUP."""
178 """Exception raised on SIGTERM and SIGHUP."""
179
179
180 # differences from SafeConfigParser:
180 # differences from SafeConfigParser:
181 # - case-sensitive keys
181 # - case-sensitive keys
182 # - allows values that are not strings (this means that you may not
182 # - allows values that are not strings (this means that you may not
183 # be able to save the configuration to a file)
183 # be able to save the configuration to a file)
184 class configparser(ConfigParser.SafeConfigParser):
184 class configparser(ConfigParser.SafeConfigParser):
185 def optionxform(self, optionstr):
185 def optionxform(self, optionstr):
186 return optionstr
186 return optionstr
187
187
188 def set(self, section, option, value):
188 def set(self, section, option, value):
189 return ConfigParser.ConfigParser.set(self, section, option, value)
189 return ConfigParser.ConfigParser.set(self, section, option, value)
190
190
191 def _interpolate(self, section, option, rawval, vars):
191 def _interpolate(self, section, option, rawval, vars):
192 if not isinstance(rawval, basestring):
192 if not isinstance(rawval, basestring):
193 return rawval
193 return rawval
194 return ConfigParser.SafeConfigParser._interpolate(self, section,
194 return ConfigParser.SafeConfigParser._interpolate(self, section,
195 option, rawval, vars)
195 option, rawval, vars)
196
196
197 def cachefunc(func):
197 def cachefunc(func):
198 '''cache the result of function calls'''
198 '''cache the result of function calls'''
199 # XXX doesn't handle keywords args
199 # XXX doesn't handle keywords args
200 cache = {}
200 cache = {}
201 if func.func_code.co_argcount == 1:
201 if func.func_code.co_argcount == 1:
202 # we gain a small amount of time because
202 # we gain a small amount of time because
203 # we don't need to pack/unpack the list
203 # we don't need to pack/unpack the list
204 def f(arg):
204 def f(arg):
205 if arg not in cache:
205 if arg not in cache:
206 cache[arg] = func(arg)
206 cache[arg] = func(arg)
207 return cache[arg]
207 return cache[arg]
208 else:
208 else:
209 def f(*args):
209 def f(*args):
210 if args not in cache:
210 if args not in cache:
211 cache[args] = func(*args)
211 cache[args] = func(*args)
212 return cache[args]
212 return cache[args]
213
213
214 return f
214 return f
215
215
216 def pipefilter(s, cmd):
216 def pipefilter(s, cmd):
217 '''filter string S through command CMD, returning its output'''
217 '''filter string S through command CMD, returning its output'''
218 (pin, pout) = popen2(cmd, 'b')
218 (pin, pout) = popen2(cmd, 'b')
219 def writer():
219 def writer():
220 try:
220 try:
221 pin.write(s)
221 pin.write(s)
222 pin.close()
222 pin.close()
223 except IOError, inst:
223 except IOError, inst:
224 if inst.errno != errno.EPIPE:
224 if inst.errno != errno.EPIPE:
225 raise
225 raise
226
226
227 # we should use select instead on UNIX, but this will work on most
227 # we should use select instead on UNIX, but this will work on most
228 # systems, including Windows
228 # systems, including Windows
229 w = threading.Thread(target=writer)
229 w = threading.Thread(target=writer)
230 w.start()
230 w.start()
231 f = pout.read()
231 f = pout.read()
232 pout.close()
232 pout.close()
233 w.join()
233 w.join()
234 return f
234 return f
235
235
236 def tempfilter(s, cmd):
236 def tempfilter(s, cmd):
237 '''filter string S through a pair of temporary files with CMD.
237 '''filter string S through a pair of temporary files with CMD.
238 CMD is used as a template to create the real command to be run,
238 CMD is used as a template to create the real command to be run,
239 with the strings INFILE and OUTFILE replaced by the real names of
239 with the strings INFILE and OUTFILE replaced by the real names of
240 the temporary files generated.'''
240 the temporary files generated.'''
241 inname, outname = None, None
241 inname, outname = None, None
242 try:
242 try:
243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
244 fp = os.fdopen(infd, 'wb')
244 fp = os.fdopen(infd, 'wb')
245 fp.write(s)
245 fp.write(s)
246 fp.close()
246 fp.close()
247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
248 os.close(outfd)
248 os.close(outfd)
249 cmd = cmd.replace('INFILE', inname)
249 cmd = cmd.replace('INFILE', inname)
250 cmd = cmd.replace('OUTFILE', outname)
250 cmd = cmd.replace('OUTFILE', outname)
251 code = os.system(cmd)
251 code = os.system(cmd)
252 if sys.platform == 'OpenVMS' and code & 1:
252 if sys.platform == 'OpenVMS' and code & 1:
253 code = 0
253 code = 0
254 if code: raise Abort(_("command '%s' failed: %s") %
254 if code: raise Abort(_("command '%s' failed: %s") %
255 (cmd, explain_exit(code)))
255 (cmd, explain_exit(code)))
256 return open(outname, 'rb').read()
256 return open(outname, 'rb').read()
257 finally:
257 finally:
258 try:
258 try:
259 if inname: os.unlink(inname)
259 if inname: os.unlink(inname)
260 except: pass
260 except: pass
261 try:
261 try:
262 if outname: os.unlink(outname)
262 if outname: os.unlink(outname)
263 except: pass
263 except: pass
264
264
265 filtertable = {
265 filtertable = {
266 'tempfile:': tempfilter,
266 'tempfile:': tempfilter,
267 'pipe:': pipefilter,
267 'pipe:': pipefilter,
268 }
268 }
269
269
270 def filter(s, cmd):
270 def filter(s, cmd):
271 "filter a string through a command that transforms its input to its output"
271 "filter a string through a command that transforms its input to its output"
272 for name, fn in filtertable.iteritems():
272 for name, fn in filtertable.iteritems():
273 if cmd.startswith(name):
273 if cmd.startswith(name):
274 return fn(s, cmd[len(name):].lstrip())
274 return fn(s, cmd[len(name):].lstrip())
275 return pipefilter(s, cmd)
275 return pipefilter(s, cmd)
276
276
277 def binary(s):
277 def binary(s):
278 """return true if a string is binary data"""
278 """return true if a string is binary data"""
279 if s and '\0' in s:
279 if s and '\0' in s:
280 return True
280 return True
281 return False
281 return False
282
282
283 def unique(g):
283 def unique(g):
284 """return the uniq elements of iterable g"""
284 """return the uniq elements of iterable g"""
285 return dict.fromkeys(g).keys()
285 return dict.fromkeys(g).keys()
286
286
287 def sort(l):
287 def sort(l):
288 if not isinstance(l, list):
288 if not isinstance(l, list):
289 l = list(l)
289 l = list(l)
290 l.sort()
290 l.sort()
291 return l
291 return l
292
292
293 def increasingchunks(source, min=1024, max=65536):
293 def increasingchunks(source, min=1024, max=65536):
294 '''return no less than min bytes per chunk while data remains,
294 '''return no less than min bytes per chunk while data remains,
295 doubling min after each chunk until it reaches max'''
295 doubling min after each chunk until it reaches max'''
296 def log2(x):
296 def log2(x):
297 if not x:
297 if not x:
298 return 0
298 return 0
299 i = 0
299 i = 0
300 while x:
300 while x:
301 x >>= 1
301 x >>= 1
302 i += 1
302 i += 1
303 return i - 1
303 return i - 1
304
304
305 buf = []
305 buf = []
306 blen = 0
306 blen = 0
307 for chunk in source:
307 for chunk in source:
308 buf.append(chunk)
308 buf.append(chunk)
309 blen += len(chunk)
309 blen += len(chunk)
310 if blen >= min:
310 if blen >= min:
311 if min < max:
311 if min < max:
312 min = min << 1
312 min = min << 1
313 nmin = 1 << log2(blen)
313 nmin = 1 << log2(blen)
314 if nmin > min:
314 if nmin > min:
315 min = nmin
315 min = nmin
316 if min > max:
316 if min > max:
317 min = max
317 min = max
318 yield ''.join(buf)
318 yield ''.join(buf)
319 blen = 0
319 blen = 0
320 buf = []
320 buf = []
321 if buf:
321 if buf:
322 yield ''.join(buf)
322 yield ''.join(buf)
323
323
324 class Abort(Exception):
324 class Abort(Exception):
325 """Raised if a command needs to print an error and exit."""
325 """Raised if a command needs to print an error and exit."""
326
326
327 class UnexpectedOutput(Abort):
327 class UnexpectedOutput(Abort):
328 """Raised to print an error with part of output and exit."""
328 """Raised to print an error with part of output and exit."""
329
329
330 def always(fn): return True
330 def always(fn): return True
331 def never(fn): return False
331 def never(fn): return False
332
332
333 def expand_glob(pats):
333 def expand_glob(pats):
334 '''On Windows, expand the implicit globs in a list of patterns'''
334 '''On Windows, expand the implicit globs in a list of patterns'''
335 if os.name != 'nt':
335 if os.name != 'nt':
336 return list(pats)
336 return list(pats)
337 ret = []
337 ret = []
338 for p in pats:
338 for p in pats:
339 kind, name = patkind(p, None)
339 kind, name = patkind(p, None)
340 if kind is None:
340 if kind is None:
341 globbed = glob.glob(name)
341 globbed = glob.glob(name)
342 if globbed:
342 if globbed:
343 ret.extend(globbed)
343 ret.extend(globbed)
344 continue
344 continue
345 # if we couldn't expand the glob, just keep it around
345 # if we couldn't expand the glob, just keep it around
346 ret.append(p)
346 ret.append(p)
347 return ret
347 return ret
348
348
349 def patkind(name, default):
349 def patkind(name, default):
350 """Split a string into an optional pattern kind prefix and the
350 """Split a string into an optional pattern kind prefix and the
351 actual pattern."""
351 actual pattern."""
352 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
352 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
353 if name.startswith(prefix + ':'): return name.split(':', 1)
353 if name.startswith(prefix + ':'): return name.split(':', 1)
354 return default, name
354 return default, name
355
355
356 def globre(pat, head='^', tail='$'):
356 def globre(pat, head='^', tail='$'):
357 "convert a glob pattern into a regexp"
357 "convert a glob pattern into a regexp"
358 i, n = 0, len(pat)
358 i, n = 0, len(pat)
359 res = ''
359 res = ''
360 group = 0
360 group = 0
361 def peek(): return i < n and pat[i]
361 def peek(): return i < n and pat[i]
362 while i < n:
362 while i < n:
363 c = pat[i]
363 c = pat[i]
364 i = i+1
364 i = i+1
365 if c == '*':
365 if c == '*':
366 if peek() == '*':
366 if peek() == '*':
367 i += 1
367 i += 1
368 res += '.*'
368 res += '.*'
369 else:
369 else:
370 res += '[^/]*'
370 res += '[^/]*'
371 elif c == '?':
371 elif c == '?':
372 res += '.'
372 res += '.'
373 elif c == '[':
373 elif c == '[':
374 j = i
374 j = i
375 if j < n and pat[j] in '!]':
375 if j < n and pat[j] in '!]':
376 j += 1
376 j += 1
377 while j < n and pat[j] != ']':
377 while j < n and pat[j] != ']':
378 j += 1
378 j += 1
379 if j >= n:
379 if j >= n:
380 res += '\\['
380 res += '\\['
381 else:
381 else:
382 stuff = pat[i:j].replace('\\','\\\\')
382 stuff = pat[i:j].replace('\\','\\\\')
383 i = j + 1
383 i = j + 1
384 if stuff[0] == '!':
384 if stuff[0] == '!':
385 stuff = '^' + stuff[1:]
385 stuff = '^' + stuff[1:]
386 elif stuff[0] == '^':
386 elif stuff[0] == '^':
387 stuff = '\\' + stuff
387 stuff = '\\' + stuff
388 res = '%s[%s]' % (res, stuff)
388 res = '%s[%s]' % (res, stuff)
389 elif c == '{':
389 elif c == '{':
390 group += 1
390 group += 1
391 res += '(?:'
391 res += '(?:'
392 elif c == '}' and group:
392 elif c == '}' and group:
393 res += ')'
393 res += ')'
394 group -= 1
394 group -= 1
395 elif c == ',' and group:
395 elif c == ',' and group:
396 res += '|'
396 res += '|'
397 elif c == '\\':
397 elif c == '\\':
398 p = peek()
398 p = peek()
399 if p:
399 if p:
400 i += 1
400 i += 1
401 res += re.escape(p)
401 res += re.escape(p)
402 else:
402 else:
403 res += re.escape(c)
403 res += re.escape(c)
404 else:
404 else:
405 res += re.escape(c)
405 res += re.escape(c)
406 return head + res + tail
406 return head + res + tail
407
407
408 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
408 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
409
409
410 def pathto(root, n1, n2):
410 def pathto(root, n1, n2):
411 '''return the relative path from one place to another.
411 '''return the relative path from one place to another.
412 root should use os.sep to separate directories
412 root should use os.sep to separate directories
413 n1 should use os.sep to separate directories
413 n1 should use os.sep to separate directories
414 n2 should use "/" to separate directories
414 n2 should use "/" to separate directories
415 returns an os.sep-separated path.
415 returns an os.sep-separated path.
416
416
417 If n1 is a relative path, it's assumed it's
417 If n1 is a relative path, it's assumed it's
418 relative to root.
418 relative to root.
419 n2 should always be relative to root.
419 n2 should always be relative to root.
420 '''
420 '''
421 if not n1: return localpath(n2)
421 if not n1: return localpath(n2)
422 if os.path.isabs(n1):
422 if os.path.isabs(n1):
423 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
423 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
424 return os.path.join(root, localpath(n2))
424 return os.path.join(root, localpath(n2))
425 n2 = '/'.join((pconvert(root), n2))
425 n2 = '/'.join((pconvert(root), n2))
426 a, b = splitpath(n1), n2.split('/')
426 a, b = splitpath(n1), n2.split('/')
427 a.reverse()
427 a.reverse()
428 b.reverse()
428 b.reverse()
429 while a and b and a[-1] == b[-1]:
429 while a and b and a[-1] == b[-1]:
430 a.pop()
430 a.pop()
431 b.pop()
431 b.pop()
432 b.reverse()
432 b.reverse()
433 return os.sep.join((['..'] * len(a)) + b) or '.'
433 return os.sep.join((['..'] * len(a)) + b) or '.'
434
434
435 def canonpath(root, cwd, myname):
435 def canonpath(root, cwd, myname):
436 """return the canonical path of myname, given cwd and root"""
436 """return the canonical path of myname, given cwd and root"""
437 if root == os.sep:
437 if root == os.sep:
438 rootsep = os.sep
438 rootsep = os.sep
439 elif endswithsep(root):
439 elif endswithsep(root):
440 rootsep = root
440 rootsep = root
441 else:
441 else:
442 rootsep = root + os.sep
442 rootsep = root + os.sep
443 name = myname
443 name = myname
444 if not os.path.isabs(name):
444 if not os.path.isabs(name):
445 name = os.path.join(root, cwd, name)
445 name = os.path.join(root, cwd, name)
446 name = os.path.normpath(name)
446 name = os.path.normpath(name)
447 audit_path = path_auditor(root)
447 audit_path = path_auditor(root)
448 if name != rootsep and name.startswith(rootsep):
448 if name != rootsep and name.startswith(rootsep):
449 name = name[len(rootsep):]
449 name = name[len(rootsep):]
450 audit_path(name)
450 audit_path(name)
451 return pconvert(name)
451 return pconvert(name)
452 elif name == root:
452 elif name == root:
453 return ''
453 return ''
454 else:
454 else:
455 # Determine whether `name' is in the hierarchy at or beneath `root',
455 # Determine whether `name' is in the hierarchy at or beneath `root',
456 # by iterating name=dirname(name) until that causes no change (can't
456 # by iterating name=dirname(name) until that causes no change (can't
457 # check name == '/', because that doesn't work on windows). For each
457 # check name == '/', because that doesn't work on windows). For each
458 # `name', compare dev/inode numbers. If they match, the list `rel'
458 # `name', compare dev/inode numbers. If they match, the list `rel'
459 # holds the reversed list of components making up the relative file
459 # holds the reversed list of components making up the relative file
460 # name we want.
460 # name we want.
461 root_st = os.stat(root)
461 root_st = os.stat(root)
462 rel = []
462 rel = []
463 while True:
463 while True:
464 try:
464 try:
465 name_st = os.stat(name)
465 name_st = os.stat(name)
466 except OSError:
466 except OSError:
467 break
467 break
468 if samestat(name_st, root_st):
468 if samestat(name_st, root_st):
469 if not rel:
469 if not rel:
470 # name was actually the same as root (maybe a symlink)
470 # name was actually the same as root (maybe a symlink)
471 return ''
471 return ''
472 rel.reverse()
472 rel.reverse()
473 name = os.path.join(*rel)
473 name = os.path.join(*rel)
474 audit_path(name)
474 audit_path(name)
475 return pconvert(name)
475 return pconvert(name)
476 dirname, basename = os.path.split(name)
476 dirname, basename = os.path.split(name)
477 rel.append(basename)
477 rel.append(basename)
478 if dirname == name:
478 if dirname == name:
479 break
479 break
480 name = dirname
480 name = dirname
481
481
482 raise Abort('%s not under root' % myname)
482 raise Abort('%s not under root' % myname)
483
483
484 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
484 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
485 """build a function to match a set of file patterns
485 """build a function to match a set of file patterns
486
486
487 arguments:
487 arguments:
488 canonroot - the canonical root of the tree you're matching against
488 canonroot - the canonical root of the tree you're matching against
489 cwd - the current working directory, if relevant
489 cwd - the current working directory, if relevant
490 names - patterns to find
490 names - patterns to find
491 inc - patterns to include
491 inc - patterns to include
492 exc - patterns to exclude
492 exc - patterns to exclude
493 dflt_pat - if a pattern in names has no explicit type, assume this one
493 dflt_pat - if a pattern in names has no explicit type, assume this one
494 src - where these patterns came from (e.g. .hgignore)
494 src - where these patterns came from (e.g. .hgignore)
495
495
496 a pattern is one of:
496 a pattern is one of:
497 'glob:<glob>' - a glob relative to cwd
497 'glob:<glob>' - a glob relative to cwd
498 're:<regexp>' - a regular expression
498 're:<regexp>' - a regular expression
499 'path:<path>' - a path relative to canonroot
499 'path:<path>' - a path relative to canonroot
500 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
500 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
501 'relpath:<path>' - a path relative to cwd
501 'relpath:<path>' - a path relative to cwd
502 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
502 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
503 '<something>' - one of the cases above, selected by the dflt_pat argument
503 '<something>' - one of the cases above, selected by the dflt_pat argument
504
504
505 returns:
505 returns:
506 a 3-tuple containing
506 a 3-tuple containing
507 - list of roots (places where one should start a recursive walk of the fs);
507 - list of roots (places where one should start a recursive walk of the fs);
508 this often matches the explicit non-pattern names passed in, but also
508 this often matches the explicit non-pattern names passed in, but also
509 includes the initial part of glob: patterns that has no glob characters
509 includes the initial part of glob: patterns that has no glob characters
510 - a bool match(filename) function
510 - a bool match(filename) function
511 - a bool indicating if any patterns were passed in
511 - a bool indicating if any patterns were passed in
512 """
512 """
513
513
514 # a common case: no patterns at all
514 # a common case: no patterns at all
515 if not names and not inc and not exc:
515 if not names and not inc and not exc:
516 return [], always, False
516 return [], always, False
517
517
518 def contains_glob(name):
518 def contains_glob(name):
519 for c in name:
519 for c in name:
520 if c in _globchars: return True
520 if c in _globchars: return True
521 return False
521 return False
522
522
523 def regex(kind, name, tail):
523 def regex(kind, name, tail):
524 '''convert a pattern into a regular expression'''
524 '''convert a pattern into a regular expression'''
525 if not name:
525 if not name:
526 return ''
526 return ''
527 if kind == 're':
527 if kind == 're':
528 return name
528 return name
529 elif kind == 'path':
529 elif kind == 'path':
530 return '^' + re.escape(name) + '(?:/|$)'
530 return '^' + re.escape(name) + '(?:/|$)'
531 elif kind == 'relglob':
531 elif kind == 'relglob':
532 return globre(name, '(?:|.*/)', tail)
532 return globre(name, '(?:|.*/)', tail)
533 elif kind == 'relpath':
533 elif kind == 'relpath':
534 return re.escape(name) + '(?:/|$)'
534 return re.escape(name) + '(?:/|$)'
535 elif kind == 'relre':
535 elif kind == 'relre':
536 if name.startswith('^'):
536 if name.startswith('^'):
537 return name
537 return name
538 return '.*' + name
538 return '.*' + name
539 return globre(name, '', tail)
539 return globre(name, '', tail)
540
540
541 def matchfn(pats, tail):
541 def matchfn(pats, tail):
542 """build a matching function from a set of patterns"""
542 """build a matching function from a set of patterns"""
543 if not pats:
543 if not pats:
544 return
544 return
545 try:
545 try:
546 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
546 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
547 if len(pat) > 20000:
547 if len(pat) > 20000:
548 raise OverflowError()
548 raise OverflowError()
549 return re.compile(pat).match
549 return re.compile(pat).match
550 except OverflowError:
550 except OverflowError:
551 # We're using a Python with a tiny regex engine and we
551 # We're using a Python with a tiny regex engine and we
552 # made it explode, so we'll divide the pattern list in two
552 # made it explode, so we'll divide the pattern list in two
553 # until it works
553 # until it works
554 l = len(pats)
554 l = len(pats)
555 if l < 2:
555 if l < 2:
556 raise
556 raise
557 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
557 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
558 return lambda s: a(s) or b(s)
558 return lambda s: a(s) or b(s)
559 except re.error:
559 except re.error:
560 for k, p in pats:
560 for k, p in pats:
561 try:
561 try:
562 re.compile('(?:%s)' % regex(k, p, tail))
562 re.compile('(?:%s)' % regex(k, p, tail))
563 except re.error:
563 except re.error:
564 if src:
564 if src:
565 raise Abort("%s: invalid pattern (%s): %s" %
565 raise Abort("%s: invalid pattern (%s): %s" %
566 (src, k, p))
566 (src, k, p))
567 else:
567 else:
568 raise Abort("invalid pattern (%s): %s" % (k, p))
568 raise Abort("invalid pattern (%s): %s" % (k, p))
569 raise Abort("invalid pattern")
569 raise Abort("invalid pattern")
570
570
571 def globprefix(pat):
571 def globprefix(pat):
572 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
572 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
573 root = []
573 root = []
574 for p in pat.split('/'):
574 for p in pat.split('/'):
575 if contains_glob(p): break
575 if contains_glob(p): break
576 root.append(p)
576 root.append(p)
577 return '/'.join(root) or '.'
577 return '/'.join(root) or '.'
578
578
579 def normalizepats(names, default):
579 def normalizepats(names, default):
580 pats = []
580 pats = []
581 roots = []
581 roots = []
582 anypats = False
582 anypats = False
583 for kind, name in [patkind(p, default) for p in names]:
583 for kind, name in [patkind(p, default) for p in names]:
584 if kind in ('glob', 'relpath'):
584 if kind in ('glob', 'relpath'):
585 name = canonpath(canonroot, cwd, name)
585 name = canonpath(canonroot, cwd, name)
586 elif kind in ('relglob', 'path'):
586 elif kind in ('relglob', 'path'):
587 name = normpath(name)
587 name = normpath(name)
588
588
589 pats.append((kind, name))
589 pats.append((kind, name))
590
590
591 if kind in ('glob', 're', 'relglob', 'relre'):
591 if kind in ('glob', 're', 'relglob', 'relre'):
592 anypats = True
592 anypats = True
593
593
594 if kind == 'glob':
594 if kind == 'glob':
595 root = globprefix(name)
595 root = globprefix(name)
596 roots.append(root)
596 roots.append(root)
597 elif kind in ('relpath', 'path'):
597 elif kind in ('relpath', 'path'):
598 roots.append(name or '.')
598 roots.append(name or '.')
599 elif kind == 'relglob':
599 elif kind == 'relglob':
600 roots.append('.')
600 roots.append('.')
601 return roots, pats, anypats
601 return roots, pats, anypats
602
602
603 roots, pats, anypats = normalizepats(names, dflt_pat)
603 roots, pats, anypats = normalizepats(names, dflt_pat)
604
604
605 patmatch = matchfn(pats, '$') or always
605 patmatch = matchfn(pats, '$') or always
606 incmatch = always
606 incmatch = always
607 if inc:
607 if inc:
608 dummy, inckinds, dummy = normalizepats(inc, 'glob')
608 dummy, inckinds, dummy = normalizepats(inc, 'glob')
609 incmatch = matchfn(inckinds, '(?:/|$)')
609 incmatch = matchfn(inckinds, '(?:/|$)')
610 excmatch = lambda fn: False
610 excmatch = never
611 if exc:
611 if exc:
612 dummy, exckinds, dummy = normalizepats(exc, 'glob')
612 dummy, exckinds, dummy = normalizepats(exc, 'glob')
613 excmatch = matchfn(exckinds, '(?:/|$)')
613 excmatch = matchfn(exckinds, '(?:/|$)')
614
614
615 if not names and inc and not exc:
615 if not names and inc and not exc:
616 # common case: hgignore patterns
616 # common case: hgignore patterns
617 match = incmatch
617 match = incmatch
618 else:
618 else:
619 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
619 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
620
620
621 return (roots, match, (inc or exc or anypats) and True)
621 return (roots, match, (inc or exc or anypats) and True)
622
622
623 _hgexecutable = None
623 _hgexecutable = None
624
624
625 def main_is_frozen():
625 def main_is_frozen():
626 """return True if we are a frozen executable.
626 """return True if we are a frozen executable.
627
627
628 The code supports py2exe (most common, Windows only) and tools/freeze
628 The code supports py2exe (most common, Windows only) and tools/freeze
629 (portable, not much used).
629 (portable, not much used).
630 """
630 """
631 return (hasattr(sys, "frozen") or # new py2exe
631 return (hasattr(sys, "frozen") or # new py2exe
632 hasattr(sys, "importers") or # old py2exe
632 hasattr(sys, "importers") or # old py2exe
633 imp.is_frozen("__main__")) # tools/freeze
633 imp.is_frozen("__main__")) # tools/freeze
634
634
635 def hgexecutable():
635 def hgexecutable():
636 """return location of the 'hg' executable.
636 """return location of the 'hg' executable.
637
637
638 Defaults to $HG or 'hg' in the search path.
638 Defaults to $HG or 'hg' in the search path.
639 """
639 """
640 if _hgexecutable is None:
640 if _hgexecutable is None:
641 hg = os.environ.get('HG')
641 hg = os.environ.get('HG')
642 if hg:
642 if hg:
643 set_hgexecutable(hg)
643 set_hgexecutable(hg)
644 elif main_is_frozen():
644 elif main_is_frozen():
645 set_hgexecutable(sys.executable)
645 set_hgexecutable(sys.executable)
646 else:
646 else:
647 set_hgexecutable(find_exe('hg', 'hg'))
647 set_hgexecutable(find_exe('hg', 'hg'))
648 return _hgexecutable
648 return _hgexecutable
649
649
650 def set_hgexecutable(path):
650 def set_hgexecutable(path):
651 """set location of the 'hg' executable"""
651 """set location of the 'hg' executable"""
652 global _hgexecutable
652 global _hgexecutable
653 _hgexecutable = path
653 _hgexecutable = path
654
654
655 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
655 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
656 '''enhanced shell command execution.
656 '''enhanced shell command execution.
657 run with environment maybe modified, maybe in different dir.
657 run with environment maybe modified, maybe in different dir.
658
658
659 if command fails and onerr is None, return status. if ui object,
659 if command fails and onerr is None, return status. if ui object,
660 print error message and return status, else raise onerr object as
660 print error message and return status, else raise onerr object as
661 exception.'''
661 exception.'''
662 def py2shell(val):
662 def py2shell(val):
663 'convert python object into string that is useful to shell'
663 'convert python object into string that is useful to shell'
664 if val in (None, False):
664 if val in (None, False):
665 return '0'
665 return '0'
666 if val == True:
666 if val == True:
667 return '1'
667 return '1'
668 return str(val)
668 return str(val)
669 oldenv = {}
669 oldenv = {}
670 for k in environ:
670 for k in environ:
671 oldenv[k] = os.environ.get(k)
671 oldenv[k] = os.environ.get(k)
672 if cwd is not None:
672 if cwd is not None:
673 oldcwd = os.getcwd()
673 oldcwd = os.getcwd()
674 origcmd = cmd
674 origcmd = cmd
675 if os.name == 'nt':
675 if os.name == 'nt':
676 cmd = '"%s"' % cmd
676 cmd = '"%s"' % cmd
677 try:
677 try:
678 for k, v in environ.iteritems():
678 for k, v in environ.iteritems():
679 os.environ[k] = py2shell(v)
679 os.environ[k] = py2shell(v)
680 os.environ['HG'] = hgexecutable()
680 os.environ['HG'] = hgexecutable()
681 if cwd is not None and oldcwd != cwd:
681 if cwd is not None and oldcwd != cwd:
682 os.chdir(cwd)
682 os.chdir(cwd)
683 rc = os.system(cmd)
683 rc = os.system(cmd)
684 if sys.platform == 'OpenVMS' and rc & 1:
684 if sys.platform == 'OpenVMS' and rc & 1:
685 rc = 0
685 rc = 0
686 if rc and onerr:
686 if rc and onerr:
687 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
687 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
688 explain_exit(rc)[0])
688 explain_exit(rc)[0])
689 if errprefix:
689 if errprefix:
690 errmsg = '%s: %s' % (errprefix, errmsg)
690 errmsg = '%s: %s' % (errprefix, errmsg)
691 try:
691 try:
692 onerr.warn(errmsg + '\n')
692 onerr.warn(errmsg + '\n')
693 except AttributeError:
693 except AttributeError:
694 raise onerr(errmsg)
694 raise onerr(errmsg)
695 return rc
695 return rc
696 finally:
696 finally:
697 for k, v in oldenv.iteritems():
697 for k, v in oldenv.iteritems():
698 if v is None:
698 if v is None:
699 del os.environ[k]
699 del os.environ[k]
700 else:
700 else:
701 os.environ[k] = v
701 os.environ[k] = v
702 if cwd is not None and oldcwd != cwd:
702 if cwd is not None and oldcwd != cwd:
703 os.chdir(oldcwd)
703 os.chdir(oldcwd)
704
704
705 class SignatureError:
705 class SignatureError:
706 pass
706 pass
707
707
708 def checksignature(func):
708 def checksignature(func):
709 '''wrap a function with code to check for calling errors'''
709 '''wrap a function with code to check for calling errors'''
710 def check(*args, **kwargs):
710 def check(*args, **kwargs):
711 try:
711 try:
712 return func(*args, **kwargs)
712 return func(*args, **kwargs)
713 except TypeError:
713 except TypeError:
714 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
714 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
715 raise SignatureError
715 raise SignatureError
716 raise
716 raise
717
717
718 return check
718 return check
719
719
720 # os.path.lexists is not available on python2.3
720 # os.path.lexists is not available on python2.3
721 def lexists(filename):
721 def lexists(filename):
722 "test whether a file with this name exists. does not follow symlinks"
722 "test whether a file with this name exists. does not follow symlinks"
723 try:
723 try:
724 os.lstat(filename)
724 os.lstat(filename)
725 except:
725 except:
726 return False
726 return False
727 return True
727 return True
728
728
729 def rename(src, dst):
729 def rename(src, dst):
730 """forcibly rename a file"""
730 """forcibly rename a file"""
731 try:
731 try:
732 os.rename(src, dst)
732 os.rename(src, dst)
733 except OSError, err: # FIXME: check err (EEXIST ?)
733 except OSError, err: # FIXME: check err (EEXIST ?)
734 # on windows, rename to existing file is not allowed, so we
734 # on windows, rename to existing file is not allowed, so we
735 # must delete destination first. but if file is open, unlink
735 # must delete destination first. but if file is open, unlink
736 # schedules it for delete but does not delete it. rename
736 # schedules it for delete but does not delete it. rename
737 # happens immediately even for open files, so we create
737 # happens immediately even for open files, so we create
738 # temporary file, delete it, rename destination to that name,
738 # temporary file, delete it, rename destination to that name,
739 # then delete that. then rename is safe to do.
739 # then delete that. then rename is safe to do.
740 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
740 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
741 os.close(fd)
741 os.close(fd)
742 os.unlink(temp)
742 os.unlink(temp)
743 os.rename(dst, temp)
743 os.rename(dst, temp)
744 os.unlink(temp)
744 os.unlink(temp)
745 os.rename(src, dst)
745 os.rename(src, dst)
746
746
747 def unlink(f):
747 def unlink(f):
748 """unlink and remove the directory if it is empty"""
748 """unlink and remove the directory if it is empty"""
749 os.unlink(f)
749 os.unlink(f)
750 # try removing directories that might now be empty
750 # try removing directories that might now be empty
751 try:
751 try:
752 os.removedirs(os.path.dirname(f))
752 os.removedirs(os.path.dirname(f))
753 except OSError:
753 except OSError:
754 pass
754 pass
755
755
756 def copyfile(src, dest):
756 def copyfile(src, dest):
757 "copy a file, preserving mode"
757 "copy a file, preserving mode"
758 if os.path.islink(src):
758 if os.path.islink(src):
759 try:
759 try:
760 os.unlink(dest)
760 os.unlink(dest)
761 except:
761 except:
762 pass
762 pass
763 os.symlink(os.readlink(src), dest)
763 os.symlink(os.readlink(src), dest)
764 else:
764 else:
765 try:
765 try:
766 shutil.copyfile(src, dest)
766 shutil.copyfile(src, dest)
767 shutil.copymode(src, dest)
767 shutil.copymode(src, dest)
768 except shutil.Error, inst:
768 except shutil.Error, inst:
769 raise Abort(str(inst))
769 raise Abort(str(inst))
770
770
771 def copyfiles(src, dst, hardlink=None):
771 def copyfiles(src, dst, hardlink=None):
772 """Copy a directory tree using hardlinks if possible"""
772 """Copy a directory tree using hardlinks if possible"""
773
773
774 if hardlink is None:
774 if hardlink is None:
775 hardlink = (os.stat(src).st_dev ==
775 hardlink = (os.stat(src).st_dev ==
776 os.stat(os.path.dirname(dst)).st_dev)
776 os.stat(os.path.dirname(dst)).st_dev)
777
777
778 if os.path.isdir(src):
778 if os.path.isdir(src):
779 os.mkdir(dst)
779 os.mkdir(dst)
780 for name, kind in osutil.listdir(src):
780 for name, kind in osutil.listdir(src):
781 srcname = os.path.join(src, name)
781 srcname = os.path.join(src, name)
782 dstname = os.path.join(dst, name)
782 dstname = os.path.join(dst, name)
783 copyfiles(srcname, dstname, hardlink)
783 copyfiles(srcname, dstname, hardlink)
784 else:
784 else:
785 if hardlink:
785 if hardlink:
786 try:
786 try:
787 os_link(src, dst)
787 os_link(src, dst)
788 except (IOError, OSError):
788 except (IOError, OSError):
789 hardlink = False
789 hardlink = False
790 shutil.copy(src, dst)
790 shutil.copy(src, dst)
791 else:
791 else:
792 shutil.copy(src, dst)
792 shutil.copy(src, dst)
793
793
794 class path_auditor(object):
794 class path_auditor(object):
795 '''ensure that a filesystem path contains no banned components.
795 '''ensure that a filesystem path contains no banned components.
796 the following properties of a path are checked:
796 the following properties of a path are checked:
797
797
798 - under top-level .hg
798 - under top-level .hg
799 - starts at the root of a windows drive
799 - starts at the root of a windows drive
800 - contains ".."
800 - contains ".."
801 - traverses a symlink (e.g. a/symlink_here/b)
801 - traverses a symlink (e.g. a/symlink_here/b)
802 - inside a nested repository'''
802 - inside a nested repository'''
803
803
804 def __init__(self, root):
804 def __init__(self, root):
805 self.audited = set()
805 self.audited = set()
806 self.auditeddir = set()
806 self.auditeddir = set()
807 self.root = root
807 self.root = root
808
808
809 def __call__(self, path):
809 def __call__(self, path):
810 if path in self.audited:
810 if path in self.audited:
811 return
811 return
812 normpath = os.path.normcase(path)
812 normpath = os.path.normcase(path)
813 parts = splitpath(normpath)
813 parts = splitpath(normpath)
814 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
814 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
815 or os.pardir in parts):
815 or os.pardir in parts):
816 raise Abort(_("path contains illegal component: %s") % path)
816 raise Abort(_("path contains illegal component: %s") % path)
817 def check(prefix):
817 def check(prefix):
818 curpath = os.path.join(self.root, prefix)
818 curpath = os.path.join(self.root, prefix)
819 try:
819 try:
820 st = os.lstat(curpath)
820 st = os.lstat(curpath)
821 except OSError, err:
821 except OSError, err:
822 # EINVAL can be raised as invalid path syntax under win32.
822 # EINVAL can be raised as invalid path syntax under win32.
823 # They must be ignored for patterns can be checked too.
823 # They must be ignored for patterns can be checked too.
824 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
824 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
825 raise
825 raise
826 else:
826 else:
827 if stat.S_ISLNK(st.st_mode):
827 if stat.S_ISLNK(st.st_mode):
828 raise Abort(_('path %r traverses symbolic link %r') %
828 raise Abort(_('path %r traverses symbolic link %r') %
829 (path, prefix))
829 (path, prefix))
830 elif (stat.S_ISDIR(st.st_mode) and
830 elif (stat.S_ISDIR(st.st_mode) and
831 os.path.isdir(os.path.join(curpath, '.hg'))):
831 os.path.isdir(os.path.join(curpath, '.hg'))):
832 raise Abort(_('path %r is inside repo %r') %
832 raise Abort(_('path %r is inside repo %r') %
833 (path, prefix))
833 (path, prefix))
834 parts.pop()
834 parts.pop()
835 prefixes = []
835 prefixes = []
836 for n in range(len(parts)):
836 for n in range(len(parts)):
837 prefix = os.sep.join(parts)
837 prefix = os.sep.join(parts)
838 if prefix in self.auditeddir:
838 if prefix in self.auditeddir:
839 break
839 break
840 check(prefix)
840 check(prefix)
841 prefixes.append(prefix)
841 prefixes.append(prefix)
842 parts.pop()
842 parts.pop()
843
843
844 self.audited.add(path)
844 self.audited.add(path)
845 # only add prefixes to the cache after checking everything: we don't
845 # only add prefixes to the cache after checking everything: we don't
846 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
846 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
847 self.auditeddir.update(prefixes)
847 self.auditeddir.update(prefixes)
848
848
849 def _makelock_file(info, pathname):
849 def _makelock_file(info, pathname):
850 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
850 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
851 os.write(ld, info)
851 os.write(ld, info)
852 os.close(ld)
852 os.close(ld)
853
853
854 def _readlock_file(pathname):
854 def _readlock_file(pathname):
855 return posixfile(pathname).read()
855 return posixfile(pathname).read()
856
856
857 def nlinks(pathname):
857 def nlinks(pathname):
858 """Return number of hardlinks for the given file."""
858 """Return number of hardlinks for the given file."""
859 return os.lstat(pathname).st_nlink
859 return os.lstat(pathname).st_nlink
860
860
861 if hasattr(os, 'link'):
861 if hasattr(os, 'link'):
862 os_link = os.link
862 os_link = os.link
863 else:
863 else:
864 def os_link(src, dst):
864 def os_link(src, dst):
865 raise OSError(0, _("Hardlinks not supported"))
865 raise OSError(0, _("Hardlinks not supported"))
866
866
867 def fstat(fp):
867 def fstat(fp):
868 '''stat file object that may not have fileno method.'''
868 '''stat file object that may not have fileno method.'''
869 try:
869 try:
870 return os.fstat(fp.fileno())
870 return os.fstat(fp.fileno())
871 except AttributeError:
871 except AttributeError:
872 return os.stat(fp.name)
872 return os.stat(fp.name)
873
873
874 posixfile = file
874 posixfile = file
875
875
876 def openhardlinks():
876 def openhardlinks():
877 '''return true if it is safe to hold open file handles to hardlinks'''
877 '''return true if it is safe to hold open file handles to hardlinks'''
878 return True
878 return True
879
879
880 def _statfiles(files):
880 def _statfiles(files):
881 'Stat each file in files and yield stat or None if file does not exist.'
881 'Stat each file in files and yield stat or None if file does not exist.'
882 lstat = os.lstat
882 lstat = os.lstat
883 for nf in files:
883 for nf in files:
884 try:
884 try:
885 st = lstat(nf)
885 st = lstat(nf)
886 except OSError, err:
886 except OSError, err:
887 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
887 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
888 raise
888 raise
889 st = None
889 st = None
890 yield st
890 yield st
891
891
892 def _statfiles_clustered(files):
892 def _statfiles_clustered(files):
893 '''Stat each file in files and yield stat or None if file does not exist.
893 '''Stat each file in files and yield stat or None if file does not exist.
894 Cluster and cache stat per directory to minimize number of OS stat calls.'''
894 Cluster and cache stat per directory to minimize number of OS stat calls.'''
895 lstat = os.lstat
895 lstat = os.lstat
896 ncase = os.path.normcase
896 ncase = os.path.normcase
897 sep = os.sep
897 sep = os.sep
898 dircache = {} # dirname -> filename -> status | None if file does not exist
898 dircache = {} # dirname -> filename -> status | None if file does not exist
899 for nf in files:
899 for nf in files:
900 nf = ncase(nf)
900 nf = ncase(nf)
901 pos = nf.rfind(sep)
901 pos = nf.rfind(sep)
902 if pos == -1:
902 if pos == -1:
903 dir, base = '.', nf
903 dir, base = '.', nf
904 else:
904 else:
905 dir, base = nf[:pos+1], nf[pos+1:]
905 dir, base = nf[:pos+1], nf[pos+1:]
906 cache = dircache.get(dir, None)
906 cache = dircache.get(dir, None)
907 if cache is None:
907 if cache is None:
908 try:
908 try:
909 dmap = dict([(ncase(n), s)
909 dmap = dict([(ncase(n), s)
910 for n, k, s in osutil.listdir(dir, True)])
910 for n, k, s in osutil.listdir(dir, True)])
911 except OSError, err:
911 except OSError, err:
912 # handle directory not found in Python version prior to 2.5
912 # handle directory not found in Python version prior to 2.5
913 # Python <= 2.4 returns native Windows code 3 in errno
913 # Python <= 2.4 returns native Windows code 3 in errno
914 # Python >= 2.5 returns ENOENT and adds winerror field
914 # Python >= 2.5 returns ENOENT and adds winerror field
915 # EINVAL is raised if dir is not a directory.
915 # EINVAL is raised if dir is not a directory.
916 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
916 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
917 errno.ENOTDIR):
917 errno.ENOTDIR):
918 raise
918 raise
919 dmap = {}
919 dmap = {}
920 cache = dircache.setdefault(dir, dmap)
920 cache = dircache.setdefault(dir, dmap)
921 yield cache.get(base, None)
921 yield cache.get(base, None)
922
922
923 if sys.platform == 'win32':
923 if sys.platform == 'win32':
924 statfiles = _statfiles_clustered
924 statfiles = _statfiles_clustered
925 else:
925 else:
926 statfiles = _statfiles
926 statfiles = _statfiles
927
927
928 getuser_fallback = None
928 getuser_fallback = None
929
929
930 def getuser():
930 def getuser():
931 '''return name of current user'''
931 '''return name of current user'''
932 try:
932 try:
933 return getpass.getuser()
933 return getpass.getuser()
934 except ImportError:
934 except ImportError:
935 # import of pwd will fail on windows - try fallback
935 # import of pwd will fail on windows - try fallback
936 if getuser_fallback:
936 if getuser_fallback:
937 return getuser_fallback()
937 return getuser_fallback()
938 # raised if win32api not available
938 # raised if win32api not available
939 raise Abort(_('user name not available - set USERNAME '
939 raise Abort(_('user name not available - set USERNAME '
940 'environment variable'))
940 'environment variable'))
941
941
942 def username(uid=None):
942 def username(uid=None):
943 """Return the name of the user with the given uid.
943 """Return the name of the user with the given uid.
944
944
945 If uid is None, return the name of the current user."""
945 If uid is None, return the name of the current user."""
946 try:
946 try:
947 import pwd
947 import pwd
948 if uid is None:
948 if uid is None:
949 uid = os.getuid()
949 uid = os.getuid()
950 try:
950 try:
951 return pwd.getpwuid(uid)[0]
951 return pwd.getpwuid(uid)[0]
952 except KeyError:
952 except KeyError:
953 return str(uid)
953 return str(uid)
954 except ImportError:
954 except ImportError:
955 return None
955 return None
956
956
957 def groupname(gid=None):
957 def groupname(gid=None):
958 """Return the name of the group with the given gid.
958 """Return the name of the group with the given gid.
959
959
960 If gid is None, return the name of the current group."""
960 If gid is None, return the name of the current group."""
961 try:
961 try:
962 import grp
962 import grp
963 if gid is None:
963 if gid is None:
964 gid = os.getgid()
964 gid = os.getgid()
965 try:
965 try:
966 return grp.getgrgid(gid)[0]
966 return grp.getgrgid(gid)[0]
967 except KeyError:
967 except KeyError:
968 return str(gid)
968 return str(gid)
969 except ImportError:
969 except ImportError:
970 return None
970 return None
971
971
972 # File system features
972 # File system features
973
973
974 def checkcase(path):
974 def checkcase(path):
975 """
975 """
976 Check whether the given path is on a case-sensitive filesystem
976 Check whether the given path is on a case-sensitive filesystem
977
977
978 Requires a path (like /foo/.hg) ending with a foldable final
978 Requires a path (like /foo/.hg) ending with a foldable final
979 directory component.
979 directory component.
980 """
980 """
981 s1 = os.stat(path)
981 s1 = os.stat(path)
982 d, b = os.path.split(path)
982 d, b = os.path.split(path)
983 p2 = os.path.join(d, b.upper())
983 p2 = os.path.join(d, b.upper())
984 if path == p2:
984 if path == p2:
985 p2 = os.path.join(d, b.lower())
985 p2 = os.path.join(d, b.lower())
986 try:
986 try:
987 s2 = os.stat(p2)
987 s2 = os.stat(p2)
988 if s2 == s1:
988 if s2 == s1:
989 return False
989 return False
990 return True
990 return True
991 except:
991 except:
992 return True
992 return True
993
993
994 _fspathcache = {}
994 _fspathcache = {}
995 def fspath(name, root):
995 def fspath(name, root):
996 '''Get name in the case stored in the filesystem
996 '''Get name in the case stored in the filesystem
997
997
998 The name is either relative to root, or it is an absolute path starting
998 The name is either relative to root, or it is an absolute path starting
999 with root. Note that this function is unnecessary, and should not be
999 with root. Note that this function is unnecessary, and should not be
1000 called, for case-sensitive filesystems (simply because it's expensive).
1000 called, for case-sensitive filesystems (simply because it's expensive).
1001 '''
1001 '''
1002 # If name is absolute, make it relative
1002 # If name is absolute, make it relative
1003 if name.lower().startswith(root.lower()):
1003 if name.lower().startswith(root.lower()):
1004 l = len(root)
1004 l = len(root)
1005 if name[l] == os.sep or name[l] == os.altsep:
1005 if name[l] == os.sep or name[l] == os.altsep:
1006 l = l + 1
1006 l = l + 1
1007 name = name[l:]
1007 name = name[l:]
1008
1008
1009 if not os.path.exists(os.path.join(root, name)):
1009 if not os.path.exists(os.path.join(root, name)):
1010 return None
1010 return None
1011
1011
1012 seps = os.sep
1012 seps = os.sep
1013 if os.altsep:
1013 if os.altsep:
1014 seps = seps + os.altsep
1014 seps = seps + os.altsep
1015 # Protect backslashes. This gets silly very quickly.
1015 # Protect backslashes. This gets silly very quickly.
1016 seps.replace('\\','\\\\')
1016 seps.replace('\\','\\\\')
1017 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1017 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1018 dir = os.path.normcase(os.path.normpath(root))
1018 dir = os.path.normcase(os.path.normpath(root))
1019 result = []
1019 result = []
1020 for part, sep in pattern.findall(name):
1020 for part, sep in pattern.findall(name):
1021 if sep:
1021 if sep:
1022 result.append(sep)
1022 result.append(sep)
1023 continue
1023 continue
1024
1024
1025 if dir not in _fspathcache:
1025 if dir not in _fspathcache:
1026 _fspathcache[dir] = os.listdir(dir)
1026 _fspathcache[dir] = os.listdir(dir)
1027 contents = _fspathcache[dir]
1027 contents = _fspathcache[dir]
1028
1028
1029 lpart = part.lower()
1029 lpart = part.lower()
1030 for n in contents:
1030 for n in contents:
1031 if n.lower() == lpart:
1031 if n.lower() == lpart:
1032 result.append(n)
1032 result.append(n)
1033 break
1033 break
1034 else:
1034 else:
1035 # Cannot happen, as the file exists!
1035 # Cannot happen, as the file exists!
1036 result.append(part)
1036 result.append(part)
1037 dir = os.path.join(dir, lpart)
1037 dir = os.path.join(dir, lpart)
1038
1038
1039 return ''.join(result)
1039 return ''.join(result)
1040
1040
1041 def checkexec(path):
1041 def checkexec(path):
1042 """
1042 """
1043 Check whether the given path is on a filesystem with UNIX-like exec flags
1043 Check whether the given path is on a filesystem with UNIX-like exec flags
1044
1044
1045 Requires a directory (like /foo/.hg)
1045 Requires a directory (like /foo/.hg)
1046 """
1046 """
1047
1047
1048 # VFAT on some Linux versions can flip mode but it doesn't persist
1048 # VFAT on some Linux versions can flip mode but it doesn't persist
1049 # a FS remount. Frequently we can detect it if files are created
1049 # a FS remount. Frequently we can detect it if files are created
1050 # with exec bit on.
1050 # with exec bit on.
1051
1051
1052 try:
1052 try:
1053 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1053 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1054 fh, fn = tempfile.mkstemp("", "", path)
1054 fh, fn = tempfile.mkstemp("", "", path)
1055 try:
1055 try:
1056 os.close(fh)
1056 os.close(fh)
1057 m = os.stat(fn).st_mode & 0777
1057 m = os.stat(fn).st_mode & 0777
1058 new_file_has_exec = m & EXECFLAGS
1058 new_file_has_exec = m & EXECFLAGS
1059 os.chmod(fn, m ^ EXECFLAGS)
1059 os.chmod(fn, m ^ EXECFLAGS)
1060 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1060 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1061 finally:
1061 finally:
1062 os.unlink(fn)
1062 os.unlink(fn)
1063 except (IOError, OSError):
1063 except (IOError, OSError):
1064 # we don't care, the user probably won't be able to commit anyway
1064 # we don't care, the user probably won't be able to commit anyway
1065 return False
1065 return False
1066 return not (new_file_has_exec or exec_flags_cannot_flip)
1066 return not (new_file_has_exec or exec_flags_cannot_flip)
1067
1067
1068 def checklink(path):
1068 def checklink(path):
1069 """check whether the given path is on a symlink-capable filesystem"""
1069 """check whether the given path is on a symlink-capable filesystem"""
1070 # mktemp is not racy because symlink creation will fail if the
1070 # mktemp is not racy because symlink creation will fail if the
1071 # file already exists
1071 # file already exists
1072 name = tempfile.mktemp(dir=path)
1072 name = tempfile.mktemp(dir=path)
1073 try:
1073 try:
1074 os.symlink(".", name)
1074 os.symlink(".", name)
1075 os.unlink(name)
1075 os.unlink(name)
1076 return True
1076 return True
1077 except (OSError, AttributeError):
1077 except (OSError, AttributeError):
1078 return False
1078 return False
1079
1079
1080 _umask = os.umask(0)
1080 _umask = os.umask(0)
1081 os.umask(_umask)
1081 os.umask(_umask)
1082
1082
1083 def needbinarypatch():
1083 def needbinarypatch():
1084 """return True if patches should be applied in binary mode by default."""
1084 """return True if patches should be applied in binary mode by default."""
1085 return os.name == 'nt'
1085 return os.name == 'nt'
1086
1086
1087 def endswithsep(path):
1087 def endswithsep(path):
1088 '''Check path ends with os.sep or os.altsep.'''
1088 '''Check path ends with os.sep or os.altsep.'''
1089 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1089 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1090
1090
1091 def splitpath(path):
1091 def splitpath(path):
1092 '''Split path by os.sep.
1092 '''Split path by os.sep.
1093 Note that this function does not use os.altsep because this is
1093 Note that this function does not use os.altsep because this is
1094 an alternative of simple "xxx.split(os.sep)".
1094 an alternative of simple "xxx.split(os.sep)".
1095 It is recommended to use os.path.normpath() before using this
1095 It is recommended to use os.path.normpath() before using this
1096 function if need.'''
1096 function if need.'''
1097 return path.split(os.sep)
1097 return path.split(os.sep)
1098
1098
1099 def gui():
1099 def gui():
1100 '''Are we running in a GUI?'''
1100 '''Are we running in a GUI?'''
1101 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1101 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1102
1102
1103 def lookup_reg(key, name=None, scope=None):
1103 def lookup_reg(key, name=None, scope=None):
1104 return None
1104 return None
1105
1105
1106 # Platform specific variants
1106 # Platform specific variants
1107 if os.name == 'nt':
1107 if os.name == 'nt':
1108 import msvcrt
1108 import msvcrt
1109 nulldev = 'NUL:'
1109 nulldev = 'NUL:'
1110
1110
1111 class winstdout:
1111 class winstdout:
1112 '''stdout on windows misbehaves if sent through a pipe'''
1112 '''stdout on windows misbehaves if sent through a pipe'''
1113
1113
1114 def __init__(self, fp):
1114 def __init__(self, fp):
1115 self.fp = fp
1115 self.fp = fp
1116
1116
1117 def __getattr__(self, key):
1117 def __getattr__(self, key):
1118 return getattr(self.fp, key)
1118 return getattr(self.fp, key)
1119
1119
1120 def close(self):
1120 def close(self):
1121 try:
1121 try:
1122 self.fp.close()
1122 self.fp.close()
1123 except: pass
1123 except: pass
1124
1124
1125 def write(self, s):
1125 def write(self, s):
1126 try:
1126 try:
1127 # This is workaround for "Not enough space" error on
1127 # This is workaround for "Not enough space" error on
1128 # writing large size of data to console.
1128 # writing large size of data to console.
1129 limit = 16000
1129 limit = 16000
1130 l = len(s)
1130 l = len(s)
1131 start = 0
1131 start = 0
1132 while start < l:
1132 while start < l:
1133 end = start + limit
1133 end = start + limit
1134 self.fp.write(s[start:end])
1134 self.fp.write(s[start:end])
1135 start = end
1135 start = end
1136 except IOError, inst:
1136 except IOError, inst:
1137 if inst.errno != 0: raise
1137 if inst.errno != 0: raise
1138 self.close()
1138 self.close()
1139 raise IOError(errno.EPIPE, 'Broken pipe')
1139 raise IOError(errno.EPIPE, 'Broken pipe')
1140
1140
1141 def flush(self):
1141 def flush(self):
1142 try:
1142 try:
1143 return self.fp.flush()
1143 return self.fp.flush()
1144 except IOError, inst:
1144 except IOError, inst:
1145 if inst.errno != errno.EINVAL: raise
1145 if inst.errno != errno.EINVAL: raise
1146 self.close()
1146 self.close()
1147 raise IOError(errno.EPIPE, 'Broken pipe')
1147 raise IOError(errno.EPIPE, 'Broken pipe')
1148
1148
1149 sys.stdout = winstdout(sys.stdout)
1149 sys.stdout = winstdout(sys.stdout)
1150
1150
1151 def _is_win_9x():
1151 def _is_win_9x():
1152 '''return true if run on windows 95, 98 or me.'''
1152 '''return true if run on windows 95, 98 or me.'''
1153 try:
1153 try:
1154 return sys.getwindowsversion()[3] == 1
1154 return sys.getwindowsversion()[3] == 1
1155 except AttributeError:
1155 except AttributeError:
1156 return 'command' in os.environ.get('comspec', '')
1156 return 'command' in os.environ.get('comspec', '')
1157
1157
1158 def openhardlinks():
1158 def openhardlinks():
1159 return not _is_win_9x and "win32api" in locals()
1159 return not _is_win_9x and "win32api" in locals()
1160
1160
1161 def system_rcpath():
1161 def system_rcpath():
1162 try:
1162 try:
1163 return system_rcpath_win32()
1163 return system_rcpath_win32()
1164 except:
1164 except:
1165 return [r'c:\mercurial\mercurial.ini']
1165 return [r'c:\mercurial\mercurial.ini']
1166
1166
1167 def user_rcpath():
1167 def user_rcpath():
1168 '''return os-specific hgrc search path to the user dir'''
1168 '''return os-specific hgrc search path to the user dir'''
1169 try:
1169 try:
1170 path = user_rcpath_win32()
1170 path = user_rcpath_win32()
1171 except:
1171 except:
1172 home = os.path.expanduser('~')
1172 home = os.path.expanduser('~')
1173 path = [os.path.join(home, 'mercurial.ini'),
1173 path = [os.path.join(home, 'mercurial.ini'),
1174 os.path.join(home, '.hgrc')]
1174 os.path.join(home, '.hgrc')]
1175 userprofile = os.environ.get('USERPROFILE')
1175 userprofile = os.environ.get('USERPROFILE')
1176 if userprofile:
1176 if userprofile:
1177 path.append(os.path.join(userprofile, 'mercurial.ini'))
1177 path.append(os.path.join(userprofile, 'mercurial.ini'))
1178 path.append(os.path.join(userprofile, '.hgrc'))
1178 path.append(os.path.join(userprofile, '.hgrc'))
1179 return path
1179 return path
1180
1180
1181 def parse_patch_output(output_line):
1181 def parse_patch_output(output_line):
1182 """parses the output produced by patch and returns the file name"""
1182 """parses the output produced by patch and returns the file name"""
1183 pf = output_line[14:]
1183 pf = output_line[14:]
1184 if pf[0] == '`':
1184 if pf[0] == '`':
1185 pf = pf[1:-1] # Remove the quotes
1185 pf = pf[1:-1] # Remove the quotes
1186 return pf
1186 return pf
1187
1187
1188 def sshargs(sshcmd, host, user, port):
1188 def sshargs(sshcmd, host, user, port):
1189 '''Build argument list for ssh or Plink'''
1189 '''Build argument list for ssh or Plink'''
1190 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1190 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1191 args = user and ("%s@%s" % (user, host)) or host
1191 args = user and ("%s@%s" % (user, host)) or host
1192 return port and ("%s %s %s" % (args, pflag, port)) or args
1192 return port and ("%s %s %s" % (args, pflag, port)) or args
1193
1193
1194 def testpid(pid):
1194 def testpid(pid):
1195 '''return False if pid dead, True if running or not known'''
1195 '''return False if pid dead, True if running or not known'''
1196 return True
1196 return True
1197
1197
1198 def set_flags(f, l, x):
1198 def set_flags(f, l, x):
1199 pass
1199 pass
1200
1200
1201 def set_binary(fd):
1201 def set_binary(fd):
1202 # When run without console, pipes may expose invalid
1202 # When run without console, pipes may expose invalid
1203 # fileno(), usually set to -1.
1203 # fileno(), usually set to -1.
1204 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1204 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1205 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1205 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1206
1206
1207 def pconvert(path):
1207 def pconvert(path):
1208 return '/'.join(splitpath(path))
1208 return '/'.join(splitpath(path))
1209
1209
1210 def localpath(path):
1210 def localpath(path):
1211 return path.replace('/', '\\')
1211 return path.replace('/', '\\')
1212
1212
1213 def normpath(path):
1213 def normpath(path):
1214 return pconvert(os.path.normpath(path))
1214 return pconvert(os.path.normpath(path))
1215
1215
1216 makelock = _makelock_file
1216 makelock = _makelock_file
1217 readlock = _readlock_file
1217 readlock = _readlock_file
1218
1218
1219 def samestat(s1, s2):
1219 def samestat(s1, s2):
1220 return False
1220 return False
1221
1221
1222 # A sequence of backslashes is special iff it precedes a double quote:
1222 # A sequence of backslashes is special iff it precedes a double quote:
1223 # - if there's an even number of backslashes, the double quote is not
1223 # - if there's an even number of backslashes, the double quote is not
1224 # quoted (i.e. it ends the quoted region)
1224 # quoted (i.e. it ends the quoted region)
1225 # - if there's an odd number of backslashes, the double quote is quoted
1225 # - if there's an odd number of backslashes, the double quote is quoted
1226 # - in both cases, every pair of backslashes is unquoted into a single
1226 # - in both cases, every pair of backslashes is unquoted into a single
1227 # backslash
1227 # backslash
1228 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1228 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1229 # So, to quote a string, we must surround it in double quotes, double
1229 # So, to quote a string, we must surround it in double quotes, double
1230 # the number of backslashes that preceed double quotes and add another
1230 # the number of backslashes that preceed double quotes and add another
1231 # backslash before every double quote (being careful with the double
1231 # backslash before every double quote (being careful with the double
1232 # quote we've appended to the end)
1232 # quote we've appended to the end)
1233 _quotere = None
1233 _quotere = None
1234 def shellquote(s):
1234 def shellquote(s):
1235 global _quotere
1235 global _quotere
1236 if _quotere is None:
1236 if _quotere is None:
1237 _quotere = re.compile(r'(\\*)("|\\$)')
1237 _quotere = re.compile(r'(\\*)("|\\$)')
1238 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1238 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1239
1239
1240 def quotecommand(cmd):
1240 def quotecommand(cmd):
1241 """Build a command string suitable for os.popen* calls."""
1241 """Build a command string suitable for os.popen* calls."""
1242 # The extra quotes are needed because popen* runs the command
1242 # The extra quotes are needed because popen* runs the command
1243 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1243 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1244 return '"' + cmd + '"'
1244 return '"' + cmd + '"'
1245
1245
1246 def popen(command, mode='r'):
1246 def popen(command, mode='r'):
1247 # Work around "popen spawned process may not write to stdout
1247 # Work around "popen spawned process may not write to stdout
1248 # under windows"
1248 # under windows"
1249 # http://bugs.python.org/issue1366
1249 # http://bugs.python.org/issue1366
1250 command += " 2> %s" % nulldev
1250 command += " 2> %s" % nulldev
1251 return os.popen(quotecommand(command), mode)
1251 return os.popen(quotecommand(command), mode)
1252
1252
1253 def explain_exit(code):
1253 def explain_exit(code):
1254 return _("exited with status %d") % code, code
1254 return _("exited with status %d") % code, code
1255
1255
1256 # if you change this stub into a real check, please try to implement the
1256 # if you change this stub into a real check, please try to implement the
1257 # username and groupname functions above, too.
1257 # username and groupname functions above, too.
1258 def isowner(fp, st=None):
1258 def isowner(fp, st=None):
1259 return True
1259 return True
1260
1260
1261 def find_in_path(name, path, default=None):
1261 def find_in_path(name, path, default=None):
1262 '''find name in search path. path can be string (will be split
1262 '''find name in search path. path can be string (will be split
1263 with os.pathsep), or iterable thing that returns strings. if name
1263 with os.pathsep), or iterable thing that returns strings. if name
1264 found, return path to name. else return default. name is looked up
1264 found, return path to name. else return default. name is looked up
1265 using cmd.exe rules, using PATHEXT.'''
1265 using cmd.exe rules, using PATHEXT.'''
1266 if isinstance(path, str):
1266 if isinstance(path, str):
1267 path = path.split(os.pathsep)
1267 path = path.split(os.pathsep)
1268
1268
1269 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1269 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1270 pathext = pathext.lower().split(os.pathsep)
1270 pathext = pathext.lower().split(os.pathsep)
1271 isexec = os.path.splitext(name)[1].lower() in pathext
1271 isexec = os.path.splitext(name)[1].lower() in pathext
1272
1272
1273 for p in path:
1273 for p in path:
1274 p_name = os.path.join(p, name)
1274 p_name = os.path.join(p, name)
1275
1275
1276 if isexec and os.path.exists(p_name):
1276 if isexec and os.path.exists(p_name):
1277 return p_name
1277 return p_name
1278
1278
1279 for ext in pathext:
1279 for ext in pathext:
1280 p_name_ext = p_name + ext
1280 p_name_ext = p_name + ext
1281 if os.path.exists(p_name_ext):
1281 if os.path.exists(p_name_ext):
1282 return p_name_ext
1282 return p_name_ext
1283 return default
1283 return default
1284
1284
1285 def set_signal_handler():
1285 def set_signal_handler():
1286 try:
1286 try:
1287 set_signal_handler_win32()
1287 set_signal_handler_win32()
1288 except NameError:
1288 except NameError:
1289 pass
1289 pass
1290
1290
1291 try:
1291 try:
1292 # override functions with win32 versions if possible
1292 # override functions with win32 versions if possible
1293 from util_win32 import *
1293 from util_win32 import *
1294 if not _is_win_9x():
1294 if not _is_win_9x():
1295 posixfile = posixfile_nt
1295 posixfile = posixfile_nt
1296 except ImportError:
1296 except ImportError:
1297 pass
1297 pass
1298
1298
1299 else:
1299 else:
1300 nulldev = '/dev/null'
1300 nulldev = '/dev/null'
1301
1301
1302 def rcfiles(path):
1302 def rcfiles(path):
1303 rcs = [os.path.join(path, 'hgrc')]
1303 rcs = [os.path.join(path, 'hgrc')]
1304 rcdir = os.path.join(path, 'hgrc.d')
1304 rcdir = os.path.join(path, 'hgrc.d')
1305 try:
1305 try:
1306 rcs.extend([os.path.join(rcdir, f)
1306 rcs.extend([os.path.join(rcdir, f)
1307 for f, kind in osutil.listdir(rcdir)
1307 for f, kind in osutil.listdir(rcdir)
1308 if f.endswith(".rc")])
1308 if f.endswith(".rc")])
1309 except OSError:
1309 except OSError:
1310 pass
1310 pass
1311 return rcs
1311 return rcs
1312
1312
1313 def system_rcpath():
1313 def system_rcpath():
1314 path = []
1314 path = []
1315 # old mod_python does not set sys.argv
1315 # old mod_python does not set sys.argv
1316 if len(getattr(sys, 'argv', [])) > 0:
1316 if len(getattr(sys, 'argv', [])) > 0:
1317 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1317 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1318 '/../etc/mercurial'))
1318 '/../etc/mercurial'))
1319 path.extend(rcfiles('/etc/mercurial'))
1319 path.extend(rcfiles('/etc/mercurial'))
1320 return path
1320 return path
1321
1321
1322 def user_rcpath():
1322 def user_rcpath():
1323 return [os.path.expanduser('~/.hgrc')]
1323 return [os.path.expanduser('~/.hgrc')]
1324
1324
1325 def parse_patch_output(output_line):
1325 def parse_patch_output(output_line):
1326 """parses the output produced by patch and returns the file name"""
1326 """parses the output produced by patch and returns the file name"""
1327 pf = output_line[14:]
1327 pf = output_line[14:]
1328 if os.sys.platform == 'OpenVMS':
1328 if os.sys.platform == 'OpenVMS':
1329 if pf[0] == '`':
1329 if pf[0] == '`':
1330 pf = pf[1:-1] # Remove the quotes
1330 pf = pf[1:-1] # Remove the quotes
1331 else:
1331 else:
1332 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1332 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1333 pf = pf[1:-1] # Remove the quotes
1333 pf = pf[1:-1] # Remove the quotes
1334 return pf
1334 return pf
1335
1335
1336 def sshargs(sshcmd, host, user, port):
1336 def sshargs(sshcmd, host, user, port):
1337 '''Build argument list for ssh'''
1337 '''Build argument list for ssh'''
1338 args = user and ("%s@%s" % (user, host)) or host
1338 args = user and ("%s@%s" % (user, host)) or host
1339 return port and ("%s -p %s" % (args, port)) or args
1339 return port and ("%s -p %s" % (args, port)) or args
1340
1340
1341 def is_exec(f):
1341 def is_exec(f):
1342 """check whether a file is executable"""
1342 """check whether a file is executable"""
1343 return (os.lstat(f).st_mode & 0100 != 0)
1343 return (os.lstat(f).st_mode & 0100 != 0)
1344
1344
1345 def set_flags(f, l, x):
1345 def set_flags(f, l, x):
1346 s = os.lstat(f).st_mode
1346 s = os.lstat(f).st_mode
1347 if l:
1347 if l:
1348 if not stat.S_ISLNK(s):
1348 if not stat.S_ISLNK(s):
1349 # switch file to link
1349 # switch file to link
1350 data = file(f).read()
1350 data = file(f).read()
1351 os.unlink(f)
1351 os.unlink(f)
1352 try:
1352 try:
1353 os.symlink(data, f)
1353 os.symlink(data, f)
1354 except:
1354 except:
1355 # failed to make a link, rewrite file
1355 # failed to make a link, rewrite file
1356 file(f, "w").write(data)
1356 file(f, "w").write(data)
1357 # no chmod needed at this point
1357 # no chmod needed at this point
1358 return
1358 return
1359 if stat.S_ISLNK(s):
1359 if stat.S_ISLNK(s):
1360 # switch link to file
1360 # switch link to file
1361 data = os.readlink(f)
1361 data = os.readlink(f)
1362 os.unlink(f)
1362 os.unlink(f)
1363 file(f, "w").write(data)
1363 file(f, "w").write(data)
1364 s = 0666 & ~_umask # avoid restatting for chmod
1364 s = 0666 & ~_umask # avoid restatting for chmod
1365
1365
1366 sx = s & 0100
1366 sx = s & 0100
1367 if x and not sx:
1367 if x and not sx:
1368 # Turn on +x for every +r bit when making a file executable
1368 # Turn on +x for every +r bit when making a file executable
1369 # and obey umask.
1369 # and obey umask.
1370 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1370 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1371 elif not x and sx:
1371 elif not x and sx:
1372 # Turn off all +x bits
1372 # Turn off all +x bits
1373 os.chmod(f, s & 0666)
1373 os.chmod(f, s & 0666)
1374
1374
1375 def set_binary(fd):
1375 def set_binary(fd):
1376 pass
1376 pass
1377
1377
1378 def pconvert(path):
1378 def pconvert(path):
1379 return path
1379 return path
1380
1380
1381 def localpath(path):
1381 def localpath(path):
1382 return path
1382 return path
1383
1383
1384 normpath = os.path.normpath
1384 normpath = os.path.normpath
1385 samestat = os.path.samestat
1385 samestat = os.path.samestat
1386
1386
1387 def makelock(info, pathname):
1387 def makelock(info, pathname):
1388 try:
1388 try:
1389 os.symlink(info, pathname)
1389 os.symlink(info, pathname)
1390 except OSError, why:
1390 except OSError, why:
1391 if why.errno == errno.EEXIST:
1391 if why.errno == errno.EEXIST:
1392 raise
1392 raise
1393 else:
1393 else:
1394 _makelock_file(info, pathname)
1394 _makelock_file(info, pathname)
1395
1395
1396 def readlock(pathname):
1396 def readlock(pathname):
1397 try:
1397 try:
1398 return os.readlink(pathname)
1398 return os.readlink(pathname)
1399 except OSError, why:
1399 except OSError, why:
1400 if why.errno in (errno.EINVAL, errno.ENOSYS):
1400 if why.errno in (errno.EINVAL, errno.ENOSYS):
1401 return _readlock_file(pathname)
1401 return _readlock_file(pathname)
1402 else:
1402 else:
1403 raise
1403 raise
1404
1404
1405 def shellquote(s):
1405 def shellquote(s):
1406 if os.sys.platform == 'OpenVMS':
1406 if os.sys.platform == 'OpenVMS':
1407 return '"%s"' % s
1407 return '"%s"' % s
1408 else:
1408 else:
1409 return "'%s'" % s.replace("'", "'\\''")
1409 return "'%s'" % s.replace("'", "'\\''")
1410
1410
1411 def quotecommand(cmd):
1411 def quotecommand(cmd):
1412 return cmd
1412 return cmd
1413
1413
1414 def popen(command, mode='r'):
1414 def popen(command, mode='r'):
1415 return os.popen(command, mode)
1415 return os.popen(command, mode)
1416
1416
1417 def testpid(pid):
1417 def testpid(pid):
1418 '''return False if pid dead, True if running or not sure'''
1418 '''return False if pid dead, True if running or not sure'''
1419 if os.sys.platform == 'OpenVMS':
1419 if os.sys.platform == 'OpenVMS':
1420 return True
1420 return True
1421 try:
1421 try:
1422 os.kill(pid, 0)
1422 os.kill(pid, 0)
1423 return True
1423 return True
1424 except OSError, inst:
1424 except OSError, inst:
1425 return inst.errno != errno.ESRCH
1425 return inst.errno != errno.ESRCH
1426
1426
1427 def explain_exit(code):
1427 def explain_exit(code):
1428 """return a 2-tuple (desc, code) describing a process's status"""
1428 """return a 2-tuple (desc, code) describing a process's status"""
1429 if os.WIFEXITED(code):
1429 if os.WIFEXITED(code):
1430 val = os.WEXITSTATUS(code)
1430 val = os.WEXITSTATUS(code)
1431 return _("exited with status %d") % val, val
1431 return _("exited with status %d") % val, val
1432 elif os.WIFSIGNALED(code):
1432 elif os.WIFSIGNALED(code):
1433 val = os.WTERMSIG(code)
1433 val = os.WTERMSIG(code)
1434 return _("killed by signal %d") % val, val
1434 return _("killed by signal %d") % val, val
1435 elif os.WIFSTOPPED(code):
1435 elif os.WIFSTOPPED(code):
1436 val = os.WSTOPSIG(code)
1436 val = os.WSTOPSIG(code)
1437 return _("stopped by signal %d") % val, val
1437 return _("stopped by signal %d") % val, val
1438 raise ValueError(_("invalid exit code"))
1438 raise ValueError(_("invalid exit code"))
1439
1439
1440 def isowner(fp, st=None):
1440 def isowner(fp, st=None):
1441 """Return True if the file object f belongs to the current user.
1441 """Return True if the file object f belongs to the current user.
1442
1442
1443 The return value of a util.fstat(f) may be passed as the st argument.
1443 The return value of a util.fstat(f) may be passed as the st argument.
1444 """
1444 """
1445 if st is None:
1445 if st is None:
1446 st = fstat(fp)
1446 st = fstat(fp)
1447 return st.st_uid == os.getuid()
1447 return st.st_uid == os.getuid()
1448
1448
1449 def find_in_path(name, path, default=None):
1449 def find_in_path(name, path, default=None):
1450 '''find name in search path. path can be string (will be split
1450 '''find name in search path. path can be string (will be split
1451 with os.pathsep), or iterable thing that returns strings. if name
1451 with os.pathsep), or iterable thing that returns strings. if name
1452 found, return path to name. else return default.'''
1452 found, return path to name. else return default.'''
1453 if isinstance(path, str):
1453 if isinstance(path, str):
1454 path = path.split(os.pathsep)
1454 path = path.split(os.pathsep)
1455 for p in path:
1455 for p in path:
1456 p_name = os.path.join(p, name)
1456 p_name = os.path.join(p, name)
1457 if os.path.exists(p_name):
1457 if os.path.exists(p_name):
1458 return p_name
1458 return p_name
1459 return default
1459 return default
1460
1460
1461 def set_signal_handler():
1461 def set_signal_handler():
1462 pass
1462 pass
1463
1463
1464 def find_exe(name, default=None):
1464 def find_exe(name, default=None):
1465 '''find path of an executable.
1465 '''find path of an executable.
1466 if name contains a path component, return it as is. otherwise,
1466 if name contains a path component, return it as is. otherwise,
1467 use normal executable search path.'''
1467 use normal executable search path.'''
1468
1468
1469 if os.sep in name or sys.platform == 'OpenVMS':
1469 if os.sep in name or sys.platform == 'OpenVMS':
1470 # don't check the executable bit. if the file isn't
1470 # don't check the executable bit. if the file isn't
1471 # executable, whoever tries to actually run it will give a
1471 # executable, whoever tries to actually run it will give a
1472 # much more useful error message.
1472 # much more useful error message.
1473 return name
1473 return name
1474 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1474 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1475
1475
1476 def mktempcopy(name, emptyok=False, createmode=None):
1476 def mktempcopy(name, emptyok=False, createmode=None):
1477 """Create a temporary file with the same contents from name
1477 """Create a temporary file with the same contents from name
1478
1478
1479 The permission bits are copied from the original file.
1479 The permission bits are copied from the original file.
1480
1480
1481 If the temporary file is going to be truncated immediately, you
1481 If the temporary file is going to be truncated immediately, you
1482 can use emptyok=True as an optimization.
1482 can use emptyok=True as an optimization.
1483
1483
1484 Returns the name of the temporary file.
1484 Returns the name of the temporary file.
1485 """
1485 """
1486 d, fn = os.path.split(name)
1486 d, fn = os.path.split(name)
1487 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1487 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1488 os.close(fd)
1488 os.close(fd)
1489 # Temporary files are created with mode 0600, which is usually not
1489 # Temporary files are created with mode 0600, which is usually not
1490 # what we want. If the original file already exists, just copy
1490 # what we want. If the original file already exists, just copy
1491 # its mode. Otherwise, manually obey umask.
1491 # its mode. Otherwise, manually obey umask.
1492 try:
1492 try:
1493 st_mode = os.lstat(name).st_mode & 0777
1493 st_mode = os.lstat(name).st_mode & 0777
1494 except OSError, inst:
1494 except OSError, inst:
1495 if inst.errno != errno.ENOENT:
1495 if inst.errno != errno.ENOENT:
1496 raise
1496 raise
1497 st_mode = createmode
1497 st_mode = createmode
1498 if st_mode is None:
1498 if st_mode is None:
1499 st_mode = ~_umask
1499 st_mode = ~_umask
1500 st_mode &= 0666
1500 st_mode &= 0666
1501 os.chmod(temp, st_mode)
1501 os.chmod(temp, st_mode)
1502 if emptyok:
1502 if emptyok:
1503 return temp
1503 return temp
1504 try:
1504 try:
1505 try:
1505 try:
1506 ifp = posixfile(name, "rb")
1506 ifp = posixfile(name, "rb")
1507 except IOError, inst:
1507 except IOError, inst:
1508 if inst.errno == errno.ENOENT:
1508 if inst.errno == errno.ENOENT:
1509 return temp
1509 return temp
1510 if not getattr(inst, 'filename', None):
1510 if not getattr(inst, 'filename', None):
1511 inst.filename = name
1511 inst.filename = name
1512 raise
1512 raise
1513 ofp = posixfile(temp, "wb")
1513 ofp = posixfile(temp, "wb")
1514 for chunk in filechunkiter(ifp):
1514 for chunk in filechunkiter(ifp):
1515 ofp.write(chunk)
1515 ofp.write(chunk)
1516 ifp.close()
1516 ifp.close()
1517 ofp.close()
1517 ofp.close()
1518 except:
1518 except:
1519 try: os.unlink(temp)
1519 try: os.unlink(temp)
1520 except: pass
1520 except: pass
1521 raise
1521 raise
1522 return temp
1522 return temp
1523
1523
1524 class atomictempfile(posixfile):
1524 class atomictempfile(posixfile):
1525 """file-like object that atomically updates a file
1525 """file-like object that atomically updates a file
1526
1526
1527 All writes will be redirected to a temporary copy of the original
1527 All writes will be redirected to a temporary copy of the original
1528 file. When rename is called, the copy is renamed to the original
1528 file. When rename is called, the copy is renamed to the original
1529 name, making the changes visible.
1529 name, making the changes visible.
1530 """
1530 """
1531 def __init__(self, name, mode, createmode):
1531 def __init__(self, name, mode, createmode):
1532 self.__name = name
1532 self.__name = name
1533 self.temp = mktempcopy(name, emptyok=('w' in mode),
1533 self.temp = mktempcopy(name, emptyok=('w' in mode),
1534 createmode=createmode)
1534 createmode=createmode)
1535 posixfile.__init__(self, self.temp, mode)
1535 posixfile.__init__(self, self.temp, mode)
1536
1536
1537 def rename(self):
1537 def rename(self):
1538 if not self.closed:
1538 if not self.closed:
1539 posixfile.close(self)
1539 posixfile.close(self)
1540 rename(self.temp, localpath(self.__name))
1540 rename(self.temp, localpath(self.__name))
1541
1541
1542 def __del__(self):
1542 def __del__(self):
1543 if not self.closed:
1543 if not self.closed:
1544 try:
1544 try:
1545 os.unlink(self.temp)
1545 os.unlink(self.temp)
1546 except: pass
1546 except: pass
1547 posixfile.close(self)
1547 posixfile.close(self)
1548
1548
1549 def makedirs(name, mode=None):
1549 def makedirs(name, mode=None):
1550 """recursive directory creation with parent mode inheritance"""
1550 """recursive directory creation with parent mode inheritance"""
1551 try:
1551 try:
1552 os.mkdir(name)
1552 os.mkdir(name)
1553 if mode is not None:
1553 if mode is not None:
1554 os.chmod(name, mode)
1554 os.chmod(name, mode)
1555 return
1555 return
1556 except OSError, err:
1556 except OSError, err:
1557 if err.errno == errno.EEXIST:
1557 if err.errno == errno.EEXIST:
1558 return
1558 return
1559 if err.errno != errno.ENOENT:
1559 if err.errno != errno.ENOENT:
1560 raise
1560 raise
1561 parent = os.path.abspath(os.path.dirname(name))
1561 parent = os.path.abspath(os.path.dirname(name))
1562 makedirs(parent, mode)
1562 makedirs(parent, mode)
1563 makedirs(name, mode)
1563 makedirs(name, mode)
1564
1564
1565 class opener(object):
1565 class opener(object):
1566 """Open files relative to a base directory
1566 """Open files relative to a base directory
1567
1567
1568 This class is used to hide the details of COW semantics and
1568 This class is used to hide the details of COW semantics and
1569 remote file access from higher level code.
1569 remote file access from higher level code.
1570 """
1570 """
1571 def __init__(self, base, audit=True):
1571 def __init__(self, base, audit=True):
1572 self.base = base
1572 self.base = base
1573 if audit:
1573 if audit:
1574 self.audit_path = path_auditor(base)
1574 self.audit_path = path_auditor(base)
1575 else:
1575 else:
1576 self.audit_path = always
1576 self.audit_path = always
1577 self.createmode = None
1577 self.createmode = None
1578
1578
1579 def __getattr__(self, name):
1579 def __getattr__(self, name):
1580 if name == '_can_symlink':
1580 if name == '_can_symlink':
1581 self._can_symlink = checklink(self.base)
1581 self._can_symlink = checklink(self.base)
1582 return self._can_symlink
1582 return self._can_symlink
1583 raise AttributeError(name)
1583 raise AttributeError(name)
1584
1584
1585 def _fixfilemode(self, name):
1585 def _fixfilemode(self, name):
1586 if self.createmode is None:
1586 if self.createmode is None:
1587 return
1587 return
1588 os.chmod(name, self.createmode & 0666)
1588 os.chmod(name, self.createmode & 0666)
1589
1589
1590 def __call__(self, path, mode="r", text=False, atomictemp=False):
1590 def __call__(self, path, mode="r", text=False, atomictemp=False):
1591 self.audit_path(path)
1591 self.audit_path(path)
1592 f = os.path.join(self.base, path)
1592 f = os.path.join(self.base, path)
1593
1593
1594 if not text and "b" not in mode:
1594 if not text and "b" not in mode:
1595 mode += "b" # for that other OS
1595 mode += "b" # for that other OS
1596
1596
1597 nlink = -1
1597 nlink = -1
1598 if mode not in ("r", "rb"):
1598 if mode not in ("r", "rb"):
1599 try:
1599 try:
1600 nlink = nlinks(f)
1600 nlink = nlinks(f)
1601 except OSError:
1601 except OSError:
1602 nlink = 0
1602 nlink = 0
1603 d = os.path.dirname(f)
1603 d = os.path.dirname(f)
1604 if not os.path.isdir(d):
1604 if not os.path.isdir(d):
1605 makedirs(d, self.createmode)
1605 makedirs(d, self.createmode)
1606 if atomictemp:
1606 if atomictemp:
1607 return atomictempfile(f, mode, self.createmode)
1607 return atomictempfile(f, mode, self.createmode)
1608 if nlink > 1:
1608 if nlink > 1:
1609 rename(mktempcopy(f), f)
1609 rename(mktempcopy(f), f)
1610 fp = posixfile(f, mode)
1610 fp = posixfile(f, mode)
1611 if nlink == 0:
1611 if nlink == 0:
1612 self._fixfilemode(f)
1612 self._fixfilemode(f)
1613 return fp
1613 return fp
1614
1614
1615 def symlink(self, src, dst):
1615 def symlink(self, src, dst):
1616 self.audit_path(dst)
1616 self.audit_path(dst)
1617 linkname = os.path.join(self.base, dst)
1617 linkname = os.path.join(self.base, dst)
1618 try:
1618 try:
1619 os.unlink(linkname)
1619 os.unlink(linkname)
1620 except OSError:
1620 except OSError:
1621 pass
1621 pass
1622
1622
1623 dirname = os.path.dirname(linkname)
1623 dirname = os.path.dirname(linkname)
1624 if not os.path.exists(dirname):
1624 if not os.path.exists(dirname):
1625 makedirs(dirname, self.createmode)
1625 makedirs(dirname, self.createmode)
1626
1626
1627 if self._can_symlink:
1627 if self._can_symlink:
1628 try:
1628 try:
1629 os.symlink(src, linkname)
1629 os.symlink(src, linkname)
1630 except OSError, err:
1630 except OSError, err:
1631 raise OSError(err.errno, _('could not symlink to %r: %s') %
1631 raise OSError(err.errno, _('could not symlink to %r: %s') %
1632 (src, err.strerror), linkname)
1632 (src, err.strerror), linkname)
1633 else:
1633 else:
1634 f = self(dst, "w")
1634 f = self(dst, "w")
1635 f.write(src)
1635 f.write(src)
1636 f.close()
1636 f.close()
1637 self._fixfilemode(dst)
1637 self._fixfilemode(dst)
1638
1638
1639 class chunkbuffer(object):
1639 class chunkbuffer(object):
1640 """Allow arbitrary sized chunks of data to be efficiently read from an
1640 """Allow arbitrary sized chunks of data to be efficiently read from an
1641 iterator over chunks of arbitrary size."""
1641 iterator over chunks of arbitrary size."""
1642
1642
1643 def __init__(self, in_iter):
1643 def __init__(self, in_iter):
1644 """in_iter is the iterator that's iterating over the input chunks.
1644 """in_iter is the iterator that's iterating over the input chunks.
1645 targetsize is how big a buffer to try to maintain."""
1645 targetsize is how big a buffer to try to maintain."""
1646 self.iter = iter(in_iter)
1646 self.iter = iter(in_iter)
1647 self.buf = ''
1647 self.buf = ''
1648 self.targetsize = 2**16
1648 self.targetsize = 2**16
1649
1649
1650 def read(self, l):
1650 def read(self, l):
1651 """Read L bytes of data from the iterator of chunks of data.
1651 """Read L bytes of data from the iterator of chunks of data.
1652 Returns less than L bytes if the iterator runs dry."""
1652 Returns less than L bytes if the iterator runs dry."""
1653 if l > len(self.buf) and self.iter:
1653 if l > len(self.buf) and self.iter:
1654 # Clamp to a multiple of self.targetsize
1654 # Clamp to a multiple of self.targetsize
1655 targetsize = max(l, self.targetsize)
1655 targetsize = max(l, self.targetsize)
1656 collector = cStringIO.StringIO()
1656 collector = cStringIO.StringIO()
1657 collector.write(self.buf)
1657 collector.write(self.buf)
1658 collected = len(self.buf)
1658 collected = len(self.buf)
1659 for chunk in self.iter:
1659 for chunk in self.iter:
1660 collector.write(chunk)
1660 collector.write(chunk)
1661 collected += len(chunk)
1661 collected += len(chunk)
1662 if collected >= targetsize:
1662 if collected >= targetsize:
1663 break
1663 break
1664 if collected < targetsize:
1664 if collected < targetsize:
1665 self.iter = False
1665 self.iter = False
1666 self.buf = collector.getvalue()
1666 self.buf = collector.getvalue()
1667 if len(self.buf) == l:
1667 if len(self.buf) == l:
1668 s, self.buf = str(self.buf), ''
1668 s, self.buf = str(self.buf), ''
1669 else:
1669 else:
1670 s, self.buf = self.buf[:l], buffer(self.buf, l)
1670 s, self.buf = self.buf[:l], buffer(self.buf, l)
1671 return s
1671 return s
1672
1672
1673 def filechunkiter(f, size=65536, limit=None):
1673 def filechunkiter(f, size=65536, limit=None):
1674 """Create a generator that produces the data in the file size
1674 """Create a generator that produces the data in the file size
1675 (default 65536) bytes at a time, up to optional limit (default is
1675 (default 65536) bytes at a time, up to optional limit (default is
1676 to read all data). Chunks may be less than size bytes if the
1676 to read all data). Chunks may be less than size bytes if the
1677 chunk is the last chunk in the file, or the file is a socket or
1677 chunk is the last chunk in the file, or the file is a socket or
1678 some other type of file that sometimes reads less data than is
1678 some other type of file that sometimes reads less data than is
1679 requested."""
1679 requested."""
1680 assert size >= 0
1680 assert size >= 0
1681 assert limit is None or limit >= 0
1681 assert limit is None or limit >= 0
1682 while True:
1682 while True:
1683 if limit is None: nbytes = size
1683 if limit is None: nbytes = size
1684 else: nbytes = min(limit, size)
1684 else: nbytes = min(limit, size)
1685 s = nbytes and f.read(nbytes)
1685 s = nbytes and f.read(nbytes)
1686 if not s: break
1686 if not s: break
1687 if limit: limit -= len(s)
1687 if limit: limit -= len(s)
1688 yield s
1688 yield s
1689
1689
1690 def makedate():
1690 def makedate():
1691 lt = time.localtime()
1691 lt = time.localtime()
1692 if lt[8] == 1 and time.daylight:
1692 if lt[8] == 1 and time.daylight:
1693 tz = time.altzone
1693 tz = time.altzone
1694 else:
1694 else:
1695 tz = time.timezone
1695 tz = time.timezone
1696 return time.mktime(lt), tz
1696 return time.mktime(lt), tz
1697
1697
1698 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1698 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1699 """represent a (unixtime, offset) tuple as a localized time.
1699 """represent a (unixtime, offset) tuple as a localized time.
1700 unixtime is seconds since the epoch, and offset is the time zone's
1700 unixtime is seconds since the epoch, and offset is the time zone's
1701 number of seconds away from UTC. if timezone is false, do not
1701 number of seconds away from UTC. if timezone is false, do not
1702 append time zone to string."""
1702 append time zone to string."""
1703 t, tz = date or makedate()
1703 t, tz = date or makedate()
1704 if "%1" in format or "%2" in format:
1704 if "%1" in format or "%2" in format:
1705 sign = (tz > 0) and "-" or "+"
1705 sign = (tz > 0) and "-" or "+"
1706 minutes = abs(tz) / 60
1706 minutes = abs(tz) / 60
1707 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1707 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1708 format = format.replace("%2", "%02d" % (minutes % 60))
1708 format = format.replace("%2", "%02d" % (minutes % 60))
1709 s = time.strftime(format, time.gmtime(float(t) - tz))
1709 s = time.strftime(format, time.gmtime(float(t) - tz))
1710 return s
1710 return s
1711
1711
1712 def shortdate(date=None):
1712 def shortdate(date=None):
1713 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1713 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1714 return datestr(date, format='%Y-%m-%d')
1714 return datestr(date, format='%Y-%m-%d')
1715
1715
1716 def strdate(string, format, defaults=[]):
1716 def strdate(string, format, defaults=[]):
1717 """parse a localized time string and return a (unixtime, offset) tuple.
1717 """parse a localized time string and return a (unixtime, offset) tuple.
1718 if the string cannot be parsed, ValueError is raised."""
1718 if the string cannot be parsed, ValueError is raised."""
1719 def timezone(string):
1719 def timezone(string):
1720 tz = string.split()[-1]
1720 tz = string.split()[-1]
1721 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1721 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1722 sign = (tz[0] == "+") and 1 or -1
1722 sign = (tz[0] == "+") and 1 or -1
1723 hours = int(tz[1:3])
1723 hours = int(tz[1:3])
1724 minutes = int(tz[3:5])
1724 minutes = int(tz[3:5])
1725 return -sign * (hours * 60 + minutes) * 60
1725 return -sign * (hours * 60 + minutes) * 60
1726 if tz == "GMT" or tz == "UTC":
1726 if tz == "GMT" or tz == "UTC":
1727 return 0
1727 return 0
1728 return None
1728 return None
1729
1729
1730 # NOTE: unixtime = localunixtime + offset
1730 # NOTE: unixtime = localunixtime + offset
1731 offset, date = timezone(string), string
1731 offset, date = timezone(string), string
1732 if offset != None:
1732 if offset != None:
1733 date = " ".join(string.split()[:-1])
1733 date = " ".join(string.split()[:-1])
1734
1734
1735 # add missing elements from defaults
1735 # add missing elements from defaults
1736 for part in defaults:
1736 for part in defaults:
1737 found = [True for p in part if ("%"+p) in format]
1737 found = [True for p in part if ("%"+p) in format]
1738 if not found:
1738 if not found:
1739 date += "@" + defaults[part]
1739 date += "@" + defaults[part]
1740 format += "@%" + part[0]
1740 format += "@%" + part[0]
1741
1741
1742 timetuple = time.strptime(date, format)
1742 timetuple = time.strptime(date, format)
1743 localunixtime = int(calendar.timegm(timetuple))
1743 localunixtime = int(calendar.timegm(timetuple))
1744 if offset is None:
1744 if offset is None:
1745 # local timezone
1745 # local timezone
1746 unixtime = int(time.mktime(timetuple))
1746 unixtime = int(time.mktime(timetuple))
1747 offset = unixtime - localunixtime
1747 offset = unixtime - localunixtime
1748 else:
1748 else:
1749 unixtime = localunixtime + offset
1749 unixtime = localunixtime + offset
1750 return unixtime, offset
1750 return unixtime, offset
1751
1751
1752 def parsedate(date, formats=None, defaults=None):
1752 def parsedate(date, formats=None, defaults=None):
1753 """parse a localized date/time string and return a (unixtime, offset) tuple.
1753 """parse a localized date/time string and return a (unixtime, offset) tuple.
1754
1754
1755 The date may be a "unixtime offset" string or in one of the specified
1755 The date may be a "unixtime offset" string or in one of the specified
1756 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1756 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1757 """
1757 """
1758 if not date:
1758 if not date:
1759 return 0, 0
1759 return 0, 0
1760 if isinstance(date, tuple) and len(date) == 2:
1760 if isinstance(date, tuple) and len(date) == 2:
1761 return date
1761 return date
1762 if not formats:
1762 if not formats:
1763 formats = defaultdateformats
1763 formats = defaultdateformats
1764 date = date.strip()
1764 date = date.strip()
1765 try:
1765 try:
1766 when, offset = map(int, date.split(' '))
1766 when, offset = map(int, date.split(' '))
1767 except ValueError:
1767 except ValueError:
1768 # fill out defaults
1768 # fill out defaults
1769 if not defaults:
1769 if not defaults:
1770 defaults = {}
1770 defaults = {}
1771 now = makedate()
1771 now = makedate()
1772 for part in "d mb yY HI M S".split():
1772 for part in "d mb yY HI M S".split():
1773 if part not in defaults:
1773 if part not in defaults:
1774 if part[0] in "HMS":
1774 if part[0] in "HMS":
1775 defaults[part] = "00"
1775 defaults[part] = "00"
1776 else:
1776 else:
1777 defaults[part] = datestr(now, "%" + part[0])
1777 defaults[part] = datestr(now, "%" + part[0])
1778
1778
1779 for format in formats:
1779 for format in formats:
1780 try:
1780 try:
1781 when, offset = strdate(date, format, defaults)
1781 when, offset = strdate(date, format, defaults)
1782 except (ValueError, OverflowError):
1782 except (ValueError, OverflowError):
1783 pass
1783 pass
1784 else:
1784 else:
1785 break
1785 break
1786 else:
1786 else:
1787 raise Abort(_('invalid date: %r ') % date)
1787 raise Abort(_('invalid date: %r ') % date)
1788 # validate explicit (probably user-specified) date and
1788 # validate explicit (probably user-specified) date and
1789 # time zone offset. values must fit in signed 32 bits for
1789 # time zone offset. values must fit in signed 32 bits for
1790 # current 32-bit linux runtimes. timezones go from UTC-12
1790 # current 32-bit linux runtimes. timezones go from UTC-12
1791 # to UTC+14
1791 # to UTC+14
1792 if abs(when) > 0x7fffffff:
1792 if abs(when) > 0x7fffffff:
1793 raise Abort(_('date exceeds 32 bits: %d') % when)
1793 raise Abort(_('date exceeds 32 bits: %d') % when)
1794 if offset < -50400 or offset > 43200:
1794 if offset < -50400 or offset > 43200:
1795 raise Abort(_('impossible time zone offset: %d') % offset)
1795 raise Abort(_('impossible time zone offset: %d') % offset)
1796 return when, offset
1796 return when, offset
1797
1797
1798 def matchdate(date):
1798 def matchdate(date):
1799 """Return a function that matches a given date match specifier
1799 """Return a function that matches a given date match specifier
1800
1800
1801 Formats include:
1801 Formats include:
1802
1802
1803 '{date}' match a given date to the accuracy provided
1803 '{date}' match a given date to the accuracy provided
1804
1804
1805 '<{date}' on or before a given date
1805 '<{date}' on or before a given date
1806
1806
1807 '>{date}' on or after a given date
1807 '>{date}' on or after a given date
1808
1808
1809 """
1809 """
1810
1810
1811 def lower(date):
1811 def lower(date):
1812 d = dict(mb="1", d="1")
1812 d = dict(mb="1", d="1")
1813 return parsedate(date, extendeddateformats, d)[0]
1813 return parsedate(date, extendeddateformats, d)[0]
1814
1814
1815 def upper(date):
1815 def upper(date):
1816 d = dict(mb="12", HI="23", M="59", S="59")
1816 d = dict(mb="12", HI="23", M="59", S="59")
1817 for days in "31 30 29".split():
1817 for days in "31 30 29".split():
1818 try:
1818 try:
1819 d["d"] = days
1819 d["d"] = days
1820 return parsedate(date, extendeddateformats, d)[0]
1820 return parsedate(date, extendeddateformats, d)[0]
1821 except:
1821 except:
1822 pass
1822 pass
1823 d["d"] = "28"
1823 d["d"] = "28"
1824 return parsedate(date, extendeddateformats, d)[0]
1824 return parsedate(date, extendeddateformats, d)[0]
1825
1825
1826 if date[0] == "<":
1826 if date[0] == "<":
1827 when = upper(date[1:])
1827 when = upper(date[1:])
1828 return lambda x: x <= when
1828 return lambda x: x <= when
1829 elif date[0] == ">":
1829 elif date[0] == ">":
1830 when = lower(date[1:])
1830 when = lower(date[1:])
1831 return lambda x: x >= when
1831 return lambda x: x >= when
1832 elif date[0] == "-":
1832 elif date[0] == "-":
1833 try:
1833 try:
1834 days = int(date[1:])
1834 days = int(date[1:])
1835 except ValueError:
1835 except ValueError:
1836 raise Abort(_("invalid day spec: %s") % date[1:])
1836 raise Abort(_("invalid day spec: %s") % date[1:])
1837 when = makedate()[0] - days * 3600 * 24
1837 when = makedate()[0] - days * 3600 * 24
1838 return lambda x: x >= when
1838 return lambda x: x >= when
1839 elif " to " in date:
1839 elif " to " in date:
1840 a, b = date.split(" to ")
1840 a, b = date.split(" to ")
1841 start, stop = lower(a), upper(b)
1841 start, stop = lower(a), upper(b)
1842 return lambda x: x >= start and x <= stop
1842 return lambda x: x >= start and x <= stop
1843 else:
1843 else:
1844 start, stop = lower(date), upper(date)
1844 start, stop = lower(date), upper(date)
1845 return lambda x: x >= start and x <= stop
1845 return lambda x: x >= start and x <= stop
1846
1846
1847 def shortuser(user):
1847 def shortuser(user):
1848 """Return a short representation of a user name or email address."""
1848 """Return a short representation of a user name or email address."""
1849 f = user.find('@')
1849 f = user.find('@')
1850 if f >= 0:
1850 if f >= 0:
1851 user = user[:f]
1851 user = user[:f]
1852 f = user.find('<')
1852 f = user.find('<')
1853 if f >= 0:
1853 if f >= 0:
1854 user = user[f+1:]
1854 user = user[f+1:]
1855 f = user.find(' ')
1855 f = user.find(' ')
1856 if f >= 0:
1856 if f >= 0:
1857 user = user[:f]
1857 user = user[:f]
1858 f = user.find('.')
1858 f = user.find('.')
1859 if f >= 0:
1859 if f >= 0:
1860 user = user[:f]
1860 user = user[:f]
1861 return user
1861 return user
1862
1862
1863 def email(author):
1863 def email(author):
1864 '''get email of author.'''
1864 '''get email of author.'''
1865 r = author.find('>')
1865 r = author.find('>')
1866 if r == -1: r = None
1866 if r == -1: r = None
1867 return author[author.find('<')+1:r]
1867 return author[author.find('<')+1:r]
1868
1868
1869 def ellipsis(text, maxlength=400):
1869 def ellipsis(text, maxlength=400):
1870 """Trim string to at most maxlength (default: 400) characters."""
1870 """Trim string to at most maxlength (default: 400) characters."""
1871 if len(text) <= maxlength:
1871 if len(text) <= maxlength:
1872 return text
1872 return text
1873 else:
1873 else:
1874 return "%s..." % (text[:maxlength-3])
1874 return "%s..." % (text[:maxlength-3])
1875
1875
1876 def walkrepos(path, followsym=False, seen_dirs=None):
1876 def walkrepos(path, followsym=False, seen_dirs=None):
1877 '''yield every hg repository under path, recursively.'''
1877 '''yield every hg repository under path, recursively.'''
1878 def errhandler(err):
1878 def errhandler(err):
1879 if err.filename == path:
1879 if err.filename == path:
1880 raise err
1880 raise err
1881 if followsym and hasattr(os.path, 'samestat'):
1881 if followsym and hasattr(os.path, 'samestat'):
1882 def _add_dir_if_not_there(dirlst, dirname):
1882 def _add_dir_if_not_there(dirlst, dirname):
1883 match = False
1883 match = False
1884 samestat = os.path.samestat
1884 samestat = os.path.samestat
1885 dirstat = os.stat(dirname)
1885 dirstat = os.stat(dirname)
1886 for lstdirstat in dirlst:
1886 for lstdirstat in dirlst:
1887 if samestat(dirstat, lstdirstat):
1887 if samestat(dirstat, lstdirstat):
1888 match = True
1888 match = True
1889 break
1889 break
1890 if not match:
1890 if not match:
1891 dirlst.append(dirstat)
1891 dirlst.append(dirstat)
1892 return not match
1892 return not match
1893 else:
1893 else:
1894 followsym = False
1894 followsym = False
1895
1895
1896 if (seen_dirs is None) and followsym:
1896 if (seen_dirs is None) and followsym:
1897 seen_dirs = []
1897 seen_dirs = []
1898 _add_dir_if_not_there(seen_dirs, path)
1898 _add_dir_if_not_there(seen_dirs, path)
1899 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1899 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1900 if '.hg' in dirs:
1900 if '.hg' in dirs:
1901 dirs.remove('.hg') # don't recurse inside the .hg directory
1901 dirs.remove('.hg') # don't recurse inside the .hg directory
1902 yield root # found a repository
1902 yield root # found a repository
1903 qroot = os.path.join(root, '.hg', 'patches')
1903 qroot = os.path.join(root, '.hg', 'patches')
1904 if os.path.isdir(os.path.join(qroot, '.hg')):
1904 if os.path.isdir(os.path.join(qroot, '.hg')):
1905 yield qroot # we have a patch queue repo here
1905 yield qroot # we have a patch queue repo here
1906 elif followsym:
1906 elif followsym:
1907 newdirs = []
1907 newdirs = []
1908 for d in dirs:
1908 for d in dirs:
1909 fname = os.path.join(root, d)
1909 fname = os.path.join(root, d)
1910 if _add_dir_if_not_there(seen_dirs, fname):
1910 if _add_dir_if_not_there(seen_dirs, fname):
1911 if os.path.islink(fname):
1911 if os.path.islink(fname):
1912 for hgname in walkrepos(fname, True, seen_dirs):
1912 for hgname in walkrepos(fname, True, seen_dirs):
1913 yield hgname
1913 yield hgname
1914 else:
1914 else:
1915 newdirs.append(d)
1915 newdirs.append(d)
1916 dirs[:] = newdirs
1916 dirs[:] = newdirs
1917
1917
1918 _rcpath = None
1918 _rcpath = None
1919
1919
1920 def os_rcpath():
1920 def os_rcpath():
1921 '''return default os-specific hgrc search path'''
1921 '''return default os-specific hgrc search path'''
1922 path = system_rcpath()
1922 path = system_rcpath()
1923 path.extend(user_rcpath())
1923 path.extend(user_rcpath())
1924 path = [os.path.normpath(f) for f in path]
1924 path = [os.path.normpath(f) for f in path]
1925 return path
1925 return path
1926
1926
1927 def rcpath():
1927 def rcpath():
1928 '''return hgrc search path. if env var HGRCPATH is set, use it.
1928 '''return hgrc search path. if env var HGRCPATH is set, use it.
1929 for each item in path, if directory, use files ending in .rc,
1929 for each item in path, if directory, use files ending in .rc,
1930 else use item.
1930 else use item.
1931 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1931 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1932 if no HGRCPATH, use default os-specific path.'''
1932 if no HGRCPATH, use default os-specific path.'''
1933 global _rcpath
1933 global _rcpath
1934 if _rcpath is None:
1934 if _rcpath is None:
1935 if 'HGRCPATH' in os.environ:
1935 if 'HGRCPATH' in os.environ:
1936 _rcpath = []
1936 _rcpath = []
1937 for p in os.environ['HGRCPATH'].split(os.pathsep):
1937 for p in os.environ['HGRCPATH'].split(os.pathsep):
1938 if not p: continue
1938 if not p: continue
1939 if os.path.isdir(p):
1939 if os.path.isdir(p):
1940 for f, kind in osutil.listdir(p):
1940 for f, kind in osutil.listdir(p):
1941 if f.endswith('.rc'):
1941 if f.endswith('.rc'):
1942 _rcpath.append(os.path.join(p, f))
1942 _rcpath.append(os.path.join(p, f))
1943 else:
1943 else:
1944 _rcpath.append(p)
1944 _rcpath.append(p)
1945 else:
1945 else:
1946 _rcpath = os_rcpath()
1946 _rcpath = os_rcpath()
1947 return _rcpath
1947 return _rcpath
1948
1948
1949 def bytecount(nbytes):
1949 def bytecount(nbytes):
1950 '''return byte count formatted as readable string, with units'''
1950 '''return byte count formatted as readable string, with units'''
1951
1951
1952 units = (
1952 units = (
1953 (100, 1<<30, _('%.0f GB')),
1953 (100, 1<<30, _('%.0f GB')),
1954 (10, 1<<30, _('%.1f GB')),
1954 (10, 1<<30, _('%.1f GB')),
1955 (1, 1<<30, _('%.2f GB')),
1955 (1, 1<<30, _('%.2f GB')),
1956 (100, 1<<20, _('%.0f MB')),
1956 (100, 1<<20, _('%.0f MB')),
1957 (10, 1<<20, _('%.1f MB')),
1957 (10, 1<<20, _('%.1f MB')),
1958 (1, 1<<20, _('%.2f MB')),
1958 (1, 1<<20, _('%.2f MB')),
1959 (100, 1<<10, _('%.0f KB')),
1959 (100, 1<<10, _('%.0f KB')),
1960 (10, 1<<10, _('%.1f KB')),
1960 (10, 1<<10, _('%.1f KB')),
1961 (1, 1<<10, _('%.2f KB')),
1961 (1, 1<<10, _('%.2f KB')),
1962 (1, 1, _('%.0f bytes')),
1962 (1, 1, _('%.0f bytes')),
1963 )
1963 )
1964
1964
1965 for multiplier, divisor, format in units:
1965 for multiplier, divisor, format in units:
1966 if nbytes >= divisor * multiplier:
1966 if nbytes >= divisor * multiplier:
1967 return format % (nbytes / float(divisor))
1967 return format % (nbytes / float(divisor))
1968 return units[-1][2] % nbytes
1968 return units[-1][2] % nbytes
1969
1969
1970 def drop_scheme(scheme, path):
1970 def drop_scheme(scheme, path):
1971 sc = scheme + ':'
1971 sc = scheme + ':'
1972 if path.startswith(sc):
1972 if path.startswith(sc):
1973 path = path[len(sc):]
1973 path = path[len(sc):]
1974 if path.startswith('//'):
1974 if path.startswith('//'):
1975 path = path[2:]
1975 path = path[2:]
1976 return path
1976 return path
1977
1977
1978 def uirepr(s):
1978 def uirepr(s):
1979 # Avoid double backslash in Windows path repr()
1979 # Avoid double backslash in Windows path repr()
1980 return repr(s).replace('\\\\', '\\')
1980 return repr(s).replace('\\\\', '\\')
@@ -1,373 +1,373 b''
1 # util_win32.py - utility functions that use win32 API
1 # util_win32.py - utility functions that use win32 API
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of
6 # This software may be used and distributed according to the terms of
7 # the GNU General Public License, incorporated herein by reference.
7 # the GNU General Public License, incorporated herein by reference.
8
8
9 # Mark Hammond's win32all package allows better functionality on
9 # Mark Hammond's win32all package allows better functionality on
10 # Windows. this module overrides definitions in util.py. if not
10 # Windows. this module overrides definitions in util.py. if not
11 # available, import of this module will fail, and generic code will be
11 # available, import of this module will fail, and generic code will be
12 # used.
12 # used.
13
13
14 import win32api
14 import win32api
15
15
16 import errno, os, sys, pywintypes, win32con, win32file, win32process
16 import errno, os, sys, pywintypes, win32con, win32file, win32process
17 import cStringIO, winerror
17 import cStringIO, winerror
18 import osutil
18 import osutil
19 import util
19 import util
20 from win32com.shell import shell,shellcon
20 from win32com.shell import shell,shellcon
21
21
22 class WinError:
22 class WinError:
23 winerror_map = {
23 winerror_map = {
24 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
24 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
25 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
25 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
26 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
26 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
27 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
27 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
28 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
28 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
29 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
29 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
30 winerror.ERROR_BAD_COMMAND: errno.EIO,
30 winerror.ERROR_BAD_COMMAND: errno.EIO,
31 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
31 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
32 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
32 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
33 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
33 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
35 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
35 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
36 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
36 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
37 winerror.ERROR_BAD_PIPE: errno.EPIPE,
37 winerror.ERROR_BAD_PIPE: errno.EPIPE,
38 winerror.ERROR_BAD_UNIT: errno.ENODEV,
38 winerror.ERROR_BAD_UNIT: errno.ENODEV,
39 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
39 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
40 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
40 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
41 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
41 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
42 winerror.ERROR_BUSY: errno.EBUSY,
42 winerror.ERROR_BUSY: errno.EBUSY,
43 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
43 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
44 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
44 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
45 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
45 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
46 winerror.ERROR_CANTOPEN: errno.EIO,
46 winerror.ERROR_CANTOPEN: errno.EIO,
47 winerror.ERROR_CANTREAD: errno.EIO,
47 winerror.ERROR_CANTREAD: errno.EIO,
48 winerror.ERROR_CANTWRITE: errno.EIO,
48 winerror.ERROR_CANTWRITE: errno.EIO,
49 winerror.ERROR_CRC: errno.EIO,
49 winerror.ERROR_CRC: errno.EIO,
50 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
50 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
51 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
51 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
52 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
52 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
53 winerror.ERROR_DIRECTORY: errno.EINVAL,
53 winerror.ERROR_DIRECTORY: errno.EINVAL,
54 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
54 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
55 winerror.ERROR_DISK_CHANGE: errno.EIO,
55 winerror.ERROR_DISK_CHANGE: errno.EIO,
56 winerror.ERROR_DISK_FULL: errno.ENOSPC,
56 winerror.ERROR_DISK_FULL: errno.ENOSPC,
57 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
57 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
58 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
58 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
59 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
59 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
60 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
60 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
61 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
61 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
62 winerror.ERROR_FILE_INVALID: errno.ENODEV,
62 winerror.ERROR_FILE_INVALID: errno.ENODEV,
63 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
63 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
64 winerror.ERROR_GEN_FAILURE: errno.EIO,
64 winerror.ERROR_GEN_FAILURE: errno.EIO,
65 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
65 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
66 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
66 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
67 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
67 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
68 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
68 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
69 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
69 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
70 winerror.ERROR_INVALID_DATA: errno.EINVAL,
70 winerror.ERROR_INVALID_DATA: errno.EINVAL,
71 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
71 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
72 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
72 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
73 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
73 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
74 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
74 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
75 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
75 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
76 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
76 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
77 winerror.ERROR_INVALID_NAME: errno.EINVAL,
77 winerror.ERROR_INVALID_NAME: errno.EINVAL,
78 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
78 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
79 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
79 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
80 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
80 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
81 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
81 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
82 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
82 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
83 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
83 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
84 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
84 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
85 winerror.ERROR_IO_DEVICE: errno.EIO,
85 winerror.ERROR_IO_DEVICE: errno.EIO,
86 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
86 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
87 winerror.ERROR_LOCKED: errno.EBUSY,
87 winerror.ERROR_LOCKED: errno.EBUSY,
88 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
88 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
89 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
89 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
90 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
90 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
91 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
91 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
92 winerror.ERROR_MORE_DATA: errno.EPIPE,
92 winerror.ERROR_MORE_DATA: errno.EPIPE,
93 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
93 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
94 winerror.ERROR_NOACCESS: errno.EFAULT,
94 winerror.ERROR_NOACCESS: errno.EFAULT,
95 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
95 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
96 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
96 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
97 winerror.ERROR_NOT_READY: errno.EAGAIN,
97 winerror.ERROR_NOT_READY: errno.EAGAIN,
98 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
98 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
99 winerror.ERROR_NO_DATA: errno.EPIPE,
99 winerror.ERROR_NO_DATA: errno.EPIPE,
100 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
100 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
101 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
101 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
102 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
102 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
103 winerror.ERROR_OPEN_FAILED: errno.EIO,
103 winerror.ERROR_OPEN_FAILED: errno.EIO,
104 winerror.ERROR_OPEN_FILES: errno.EBUSY,
104 winerror.ERROR_OPEN_FILES: errno.EBUSY,
105 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
105 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
106 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
106 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
107 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
107 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
108 winerror.ERROR_PATH_BUSY: errno.EBUSY,
108 winerror.ERROR_PATH_BUSY: errno.EBUSY,
109 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
109 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
110 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
110 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
111 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
111 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
112 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
112 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
113 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
113 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
114 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
114 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
115 winerror.ERROR_READ_FAULT: errno.EIO,
115 winerror.ERROR_READ_FAULT: errno.EIO,
116 winerror.ERROR_SEEK: errno.EIO,
116 winerror.ERROR_SEEK: errno.EIO,
117 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
117 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
118 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
118 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
119 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
119 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
120 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
120 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
121 winerror.ERROR_SWAPERROR: errno.ENOENT,
121 winerror.ERROR_SWAPERROR: errno.ENOENT,
122 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
122 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
123 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
123 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
124 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
124 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
125 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
125 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
126 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
126 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
127 winerror.ERROR_WRITE_FAULT: errno.EIO,
127 winerror.ERROR_WRITE_FAULT: errno.EIO,
128 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
128 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
129 }
129 }
130
130
131 def __init__(self, err):
131 def __init__(self, err):
132 self.win_errno, self.win_function, self.win_strerror = err
132 self.win_errno, self.win_function, self.win_strerror = err
133 if self.win_strerror.endswith('.'):
133 if self.win_strerror.endswith('.'):
134 self.win_strerror = self.win_strerror[:-1]
134 self.win_strerror = self.win_strerror[:-1]
135
135
136 class WinIOError(WinError, IOError):
136 class WinIOError(WinError, IOError):
137 def __init__(self, err, filename=None):
137 def __init__(self, err, filename=None):
138 WinError.__init__(self, err)
138 WinError.__init__(self, err)
139 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
139 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
140 self.win_strerror)
140 self.win_strerror)
141 self.filename = filename
141 self.filename = filename
142
142
143 class WinOSError(WinError, OSError):
143 class WinOSError(WinError, OSError):
144 def __init__(self, err):
144 def __init__(self, err):
145 WinError.__init__(self, err)
145 WinError.__init__(self, err)
146 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
146 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
147 self.win_strerror)
147 self.win_strerror)
148
148
149 def os_link(src, dst):
149 def os_link(src, dst):
150 try:
150 try:
151 win32file.CreateHardLink(dst, src)
151 win32file.CreateHardLink(dst, src)
152 # CreateHardLink sometimes succeeds on mapped drives but
152 # CreateHardLink sometimes succeeds on mapped drives but
153 # following nlinks() returns 1. Check it now and bail out.
153 # following nlinks() returns 1. Check it now and bail out.
154 if nlinks(src) < 2:
154 if nlinks(src) < 2:
155 try:
155 try:
156 win32file.DeleteFile(dst)
156 win32file.DeleteFile(dst)
157 except:
157 except:
158 pass
158 pass
159 # Fake hardlinking error
159 # Fake hardlinking error
160 raise WinOSError((18, 'CreateHardLink', 'The system cannot '
160 raise WinOSError((18, 'CreateHardLink', 'The system cannot '
161 'move the file to a different disk drive'))
161 'move the file to a different disk drive'))
162 except pywintypes.error, details:
162 except pywintypes.error, details:
163 raise WinOSError(details)
163 raise WinOSError(details)
164
164
165 def nlinks(pathname):
165 def nlinks(pathname):
166 """Return number of hardlinks for the given file."""
166 """Return number of hardlinks for the given file."""
167 try:
167 try:
168 fh = win32file.CreateFile(pathname,
168 fh = win32file.CreateFile(pathname,
169 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
169 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
170 None, win32file.OPEN_EXISTING, 0, None)
170 None, win32file.OPEN_EXISTING, 0, None)
171 res = win32file.GetFileInformationByHandle(fh)
171 res = win32file.GetFileInformationByHandle(fh)
172 fh.Close()
172 fh.Close()
173 return res[7]
173 return res[7]
174 except pywintypes.error:
174 except pywintypes.error:
175 return os.lstat(pathname).st_nlink
175 return os.lstat(pathname).st_nlink
176
176
177 def testpid(pid):
177 def testpid(pid):
178 '''return True if pid is still running or unable to
178 '''return True if pid is still running or unable to
179 determine, False otherwise'''
179 determine, False otherwise'''
180 try:
180 try:
181 handle = win32api.OpenProcess(
181 handle = win32api.OpenProcess(
182 win32con.PROCESS_QUERY_INFORMATION, False, pid)
182 win32con.PROCESS_QUERY_INFORMATION, False, pid)
183 if handle:
183 if handle:
184 status = win32process.GetExitCodeProcess(handle)
184 status = win32process.GetExitCodeProcess(handle)
185 return status == win32con.STILL_ACTIVE
185 return status == win32con.STILL_ACTIVE
186 except pywintypes.error, details:
186 except pywintypes.error, details:
187 return details[0] != winerror.ERROR_INVALID_PARAMETER
187 return details[0] != winerror.ERROR_INVALID_PARAMETER
188 return True
188 return True
189
189
190 def lookup_reg(key, valname=None, scope=None):
190 def lookup_reg(key, valname=None, scope=None):
191 ''' Look up a key/value name in the Windows registry.
191 ''' Look up a key/value name in the Windows registry.
192
192
193 valname: value name. If unspecified, the default value for the key
193 valname: value name. If unspecified, the default value for the key
194 is used.
194 is used.
195 scope: optionally specify scope for registry lookup, this can be
195 scope: optionally specify scope for registry lookup, this can be
196 a sequence of scopes to look up in order. Default (CURRENT_USER,
196 a sequence of scopes to look up in order. Default (CURRENT_USER,
197 LOCAL_MACHINE).
197 LOCAL_MACHINE).
198 '''
198 '''
199 try:
199 try:
200 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
200 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
201 QueryValueEx, OpenKey
201 QueryValueEx, OpenKey
202 except ImportError:
202 except ImportError:
203 return None
203 return None
204
204
205 if scope is None:
205 if scope is None:
206 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
206 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
207 elif not isinstance(scope, (list, tuple)):
207 elif not isinstance(scope, (list, tuple)):
208 scope = (scope,)
208 scope = (scope,)
209 for s in scope:
209 for s in scope:
210 try:
210 try:
211 val = QueryValueEx(OpenKey(s, key), valname)[0]
211 val = QueryValueEx(OpenKey(s, key), valname)[0]
212 # never let a Unicode string escape into the wild
212 # never let a Unicode string escape into the wild
213 return util.tolocal(val.encode('UTF-8'))
213 return util.tolocal(val.encode('UTF-8'))
214 except EnvironmentError:
214 except EnvironmentError:
215 pass
215 pass
216
216
217 def system_rcpath_win32():
217 def system_rcpath_win32():
218 '''return default os-specific hgrc search path'''
218 '''return default os-specific hgrc search path'''
219 proc = win32api.GetCurrentProcess()
219 proc = win32api.GetCurrentProcess()
220 try:
220 try:
221 # This will fail on windows < NT
221 # This will fail on windows < NT
222 filename = win32process.GetModuleFileNameEx(proc, 0)
222 filename = win32process.GetModuleFileNameEx(proc, 0)
223 except:
223 except:
224 filename = win32api.GetModuleFileName(0)
224 filename = win32api.GetModuleFileName(0)
225 # Use mercurial.ini found in directory with hg.exe
225 # Use mercurial.ini found in directory with hg.exe
226 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
226 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
227 if os.path.isfile(progrc):
227 if os.path.isfile(progrc):
228 return [progrc]
228 return [progrc]
229 # else look for a system rcpath in the registry
229 # else look for a system rcpath in the registry
230 try:
230 try:
231 value = win32api.RegQueryValue(
231 value = win32api.RegQueryValue(
232 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
232 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
233 rcpath = []
233 rcpath = []
234 for p in value.split(os.pathsep):
234 for p in value.split(os.pathsep):
235 if p.lower().endswith('mercurial.ini'):
235 if p.lower().endswith('mercurial.ini'):
236 rcpath.append(p)
236 rcpath.append(p)
237 elif os.path.isdir(p):
237 elif os.path.isdir(p):
238 for f, kind in osutil.listdir(p):
238 for f, kind in osutil.listdir(p):
239 if f.endswith('.rc'):
239 if f.endswith('.rc'):
240 rcpath.append(os.path.join(p, f))
240 rcpath.append(os.path.join(p, f))
241 return rcpath
241 return rcpath
242 except pywintypes.error:
242 except pywintypes.error:
243 return []
243 return []
244
244
245 def user_rcpath_win32():
245 def user_rcpath_win32():
246 '''return os-specific hgrc search path to the user dir'''
246 '''return os-specific hgrc search path to the user dir'''
247 userdir = os.path.expanduser('~')
247 userdir = os.path.expanduser('~')
248 if sys.getwindowsversion() != 2 and userdir == '~':
248 if sys.getwindowsversion()[3] != 2 and userdir == '~':
249 # We are on win < nt: fetch the APPDATA directory location and use
249 # We are on win < nt: fetch the APPDATA directory location and use
250 # the parent directory as the user home dir.
250 # the parent directory as the user home dir.
251 appdir = shell.SHGetPathFromIDList(
251 appdir = shell.SHGetPathFromIDList(
252 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
252 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
253 userdir = os.path.dirname(appdir)
253 userdir = os.path.dirname(appdir)
254 return [os.path.join(userdir, 'mercurial.ini'),
254 return [os.path.join(userdir, 'mercurial.ini'),
255 os.path.join(userdir, '.hgrc')]
255 os.path.join(userdir, '.hgrc')]
256
256
257 class posixfile_nt(object):
257 class posixfile_nt(object):
258 '''file object with posix-like semantics. on windows, normal
258 '''file object with posix-like semantics. on windows, normal
259 files can not be deleted or renamed if they are open. must open
259 files can not be deleted or renamed if they are open. must open
260 with win32file.FILE_SHARE_DELETE. this flag does not exist on
260 with win32file.FILE_SHARE_DELETE. this flag does not exist on
261 windows < nt, so do not use this class there.'''
261 windows < nt, so do not use this class there.'''
262
262
263 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
263 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
264 # but does not work at all. wrap win32 file api instead.
264 # but does not work at all. wrap win32 file api instead.
265
265
266 def __init__(self, name, mode='rb'):
266 def __init__(self, name, mode='rb'):
267 self.closed = False
267 self.closed = False
268 self.name = name
268 self.name = name
269 self.mode = mode
269 self.mode = mode
270 access = 0
270 access = 0
271 if 'r' in mode or '+' in mode:
271 if 'r' in mode or '+' in mode:
272 access |= win32file.GENERIC_READ
272 access |= win32file.GENERIC_READ
273 if 'w' in mode or 'a' in mode or '+' in mode:
273 if 'w' in mode or 'a' in mode or '+' in mode:
274 access |= win32file.GENERIC_WRITE
274 access |= win32file.GENERIC_WRITE
275 if 'r' in mode:
275 if 'r' in mode:
276 creation = win32file.OPEN_EXISTING
276 creation = win32file.OPEN_EXISTING
277 elif 'a' in mode:
277 elif 'a' in mode:
278 creation = win32file.OPEN_ALWAYS
278 creation = win32file.OPEN_ALWAYS
279 else:
279 else:
280 creation = win32file.CREATE_ALWAYS
280 creation = win32file.CREATE_ALWAYS
281 try:
281 try:
282 self.handle = win32file.CreateFile(name,
282 self.handle = win32file.CreateFile(name,
283 access,
283 access,
284 win32file.FILE_SHARE_READ |
284 win32file.FILE_SHARE_READ |
285 win32file.FILE_SHARE_WRITE |
285 win32file.FILE_SHARE_WRITE |
286 win32file.FILE_SHARE_DELETE,
286 win32file.FILE_SHARE_DELETE,
287 None,
287 None,
288 creation,
288 creation,
289 win32file.FILE_ATTRIBUTE_NORMAL,
289 win32file.FILE_ATTRIBUTE_NORMAL,
290 0)
290 0)
291 except pywintypes.error, err:
291 except pywintypes.error, err:
292 raise WinIOError(err, name)
292 raise WinIOError(err, name)
293
293
294 def __iter__(self):
294 def __iter__(self):
295 for line in self.readlines():
295 for line in self.readlines():
296 yield line
296 yield line
297
297
298 def read(self, count=-1):
298 def read(self, count=-1):
299 try:
299 try:
300 cs = cStringIO.StringIO()
300 cs = cStringIO.StringIO()
301 while count:
301 while count:
302 wincount = int(count)
302 wincount = int(count)
303 if wincount == -1:
303 if wincount == -1:
304 wincount = 1048576
304 wincount = 1048576
305 val, data = win32file.ReadFile(self.handle, wincount)
305 val, data = win32file.ReadFile(self.handle, wincount)
306 if not data: break
306 if not data: break
307 cs.write(data)
307 cs.write(data)
308 if count != -1:
308 if count != -1:
309 count -= len(data)
309 count -= len(data)
310 return cs.getvalue()
310 return cs.getvalue()
311 except pywintypes.error, err:
311 except pywintypes.error, err:
312 raise WinIOError(err)
312 raise WinIOError(err)
313
313
314 def readlines(self, sizehint=None):
314 def readlines(self, sizehint=None):
315 # splitlines() splits on single '\r' while readlines()
315 # splitlines() splits on single '\r' while readlines()
316 # does not. cStringIO has a well behaving readlines() and is fast.
316 # does not. cStringIO has a well behaving readlines() and is fast.
317 return cStringIO.StringIO(self.read()).readlines()
317 return cStringIO.StringIO(self.read()).readlines()
318
318
319 def write(self, data):
319 def write(self, data):
320 try:
320 try:
321 if 'a' in self.mode:
321 if 'a' in self.mode:
322 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
322 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
323 nwrit = 0
323 nwrit = 0
324 while nwrit < len(data):
324 while nwrit < len(data):
325 val, nwrit = win32file.WriteFile(self.handle, data)
325 val, nwrit = win32file.WriteFile(self.handle, data)
326 data = data[nwrit:]
326 data = data[nwrit:]
327 except pywintypes.error, err:
327 except pywintypes.error, err:
328 raise WinIOError(err)
328 raise WinIOError(err)
329
329
330 def writelines(self, sequence):
330 def writelines(self, sequence):
331 for s in sequence:
331 for s in sequence:
332 self.write(s)
332 self.write(s)
333
333
334 def seek(self, pos, whence=0):
334 def seek(self, pos, whence=0):
335 try:
335 try:
336 win32file.SetFilePointer(self.handle, int(pos), whence)
336 win32file.SetFilePointer(self.handle, int(pos), whence)
337 except pywintypes.error, err:
337 except pywintypes.error, err:
338 raise WinIOError(err)
338 raise WinIOError(err)
339
339
340 def tell(self):
340 def tell(self):
341 try:
341 try:
342 return win32file.SetFilePointer(self.handle, 0,
342 return win32file.SetFilePointer(self.handle, 0,
343 win32file.FILE_CURRENT)
343 win32file.FILE_CURRENT)
344 except pywintypes.error, err:
344 except pywintypes.error, err:
345 raise WinIOError(err)
345 raise WinIOError(err)
346
346
347 def close(self):
347 def close(self):
348 if not self.closed:
348 if not self.closed:
349 self.handle = None
349 self.handle = None
350 self.closed = True
350 self.closed = True
351
351
352 def flush(self):
352 def flush(self):
353 # we have no application-level buffering
353 # we have no application-level buffering
354 pass
354 pass
355
355
356 def truncate(self, pos=0):
356 def truncate(self, pos=0):
357 try:
357 try:
358 win32file.SetFilePointer(self.handle, int(pos),
358 win32file.SetFilePointer(self.handle, int(pos),
359 win32file.FILE_BEGIN)
359 win32file.FILE_BEGIN)
360 win32file.SetEndOfFile(self.handle)
360 win32file.SetEndOfFile(self.handle)
361 except pywintypes.error, err:
361 except pywintypes.error, err:
362 raise WinIOError(err)
362 raise WinIOError(err)
363
363
364 getuser_fallback = win32api.GetUserName
364 getuser_fallback = win32api.GetUserName
365
365
366 def set_signal_handler_win32():
366 def set_signal_handler_win32():
367 """Register a termination handler for console events including
367 """Register a termination handler for console events including
368 CTRL+C. python signal handlers do not work well with socket
368 CTRL+C. python signal handlers do not work well with socket
369 operations.
369 operations.
370 """
370 """
371 def handler(event):
371 def handler(event):
372 win32process.ExitProcess(1)
372 win32process.ExitProcess(1)
373 win32api.SetConsoleCtrlHandler(handler)
373 win32api.SetConsoleCtrlHandler(handler)
@@ -1,225 +1,229 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Test the running system for features availability. Exit with zero
2 """Test the running system for features availability. Exit with zero
3 if all features are there, non-zero otherwise. If a feature name is
3 if all features are there, non-zero otherwise. If a feature name is
4 prefixed with "no-", the absence of feature is tested.
4 prefixed with "no-", the absence of feature is tested.
5 """
5 """
6 import optparse
6 import optparse
7 import os
7 import os
8 import re
8 import re
9 import sys
9 import sys
10 import tempfile
10 import tempfile
11
11
12 tempprefix = 'hg-hghave-'
12 tempprefix = 'hg-hghave-'
13
13
14 def matchoutput(cmd, regexp, ignorestatus=False):
14 def matchoutput(cmd, regexp, ignorestatus=False):
15 """Return True if cmd executes successfully and its output
15 """Return True if cmd executes successfully and its output
16 is matched by the supplied regular expression.
16 is matched by the supplied regular expression.
17 """
17 """
18 r = re.compile(regexp)
18 r = re.compile(regexp)
19 fh = os.popen(cmd)
19 fh = os.popen(cmd)
20 s = fh.read()
20 s = fh.read()
21 ret = fh.close()
21 ret = fh.close()
22 return (ignorestatus or ret is None) and r.search(s)
22 return (ignorestatus or ret is None) and r.search(s)
23
23
24 def has_baz():
24 def has_baz():
25 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
25 return matchoutput('baz --version 2>&1', r'baz Bazaar version')
26
26
27 def has_bzr():
27 def has_bzr():
28 try:
28 try:
29 import bzrlib
29 import bzrlib
30 return True
30 return True
31 except ImportError:
31 except ImportError:
32 return False
32 return False
33
33
34 def has_cvs():
34 def has_cvs():
35 re = r'Concurrent Versions System.*?server'
35 re = r'Concurrent Versions System.*?server'
36 return matchoutput('cvs --version 2>&1', re)
36 return matchoutput('cvs --version 2>&1', re)
37
37
38 def has_cvsps():
38 def has_cvsps():
39 return matchoutput('cvsps -h -q 2>&1', r'cvsps version', True)
39 return matchoutput('cvsps -h -q 2>&1', r'cvsps version', True)
40
40
41 def has_darcs():
41 def has_darcs():
42 return matchoutput('darcs', r'darcs version', True)
42 return matchoutput('darcs', r'darcs version', True)
43
43
44 def has_mtn():
44 def has_mtn():
45 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
45 return matchoutput('mtn --version', r'monotone', True) and not matchoutput(
46 'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
46 'mtn --version', r'monotone 0\.(\d|[12]\d|3[01])[^\d]', True)
47
47
48 def has_eol_in_paths():
48 def has_eol_in_paths():
49 try:
49 try:
50 fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r')
50 fd, path = tempfile.mkstemp(prefix=tempprefix, suffix='\n\r')
51 os.close(fd)
51 os.close(fd)
52 os.remove(path)
52 os.remove(path)
53 return True
53 return True
54 except:
54 except:
55 return False
55 return False
56
56
57 def has_executablebit():
57 def has_executablebit():
58 fd, path = tempfile.mkstemp(prefix=tempprefix)
58 fd, path = tempfile.mkstemp(prefix=tempprefix)
59 os.close(fd)
59 os.close(fd)
60 try:
60 try:
61 s = os.lstat(path).st_mode
61 s = os.lstat(path).st_mode
62 os.chmod(path, s | 0100)
62 os.chmod(path, s | 0100)
63 return (os.lstat(path).st_mode & 0100 != 0)
63 return (os.lstat(path).st_mode & 0100 != 0)
64 finally:
64 finally:
65 os.remove(path)
65 os.remove(path)
66
66
67 def has_icasefs():
67 def has_icasefs():
68 # Stolen from mercurial.util
68 # Stolen from mercurial.util
69 fd, path = tempfile.mkstemp(prefix=tempprefix)
69 fd, path = tempfile.mkstemp(prefix=tempprefix)
70 os.close(fd)
70 os.close(fd)
71 try:
71 try:
72 s1 = os.stat(path)
72 s1 = os.stat(path)
73 d, b = os.path.split(path)
73 d, b = os.path.split(path)
74 p2 = os.path.join(d, b.upper())
74 p2 = os.path.join(d, b.upper())
75 if path == p2:
75 if path == p2:
76 p2 = os.path.join(d, b.lower())
76 p2 = os.path.join(d, b.lower())
77 try:
77 try:
78 s2 = os.stat(p2)
78 s2 = os.stat(p2)
79 return s2 == s1
79 return s2 == s1
80 except:
80 except:
81 return False
81 return False
82 finally:
82 finally:
83 os.remove(path)
83 os.remove(path)
84
84
85 def has_inotify():
85 def has_inotify():
86 try:
86 try:
87 import hgext.inotify.linux.watcher
87 import hgext.inotify.linux.watcher
88 return True
88 return True
89 except ImportError:
89 except ImportError:
90 return False
90 return False
91
91
92 def has_fifo():
92 def has_fifo():
93 return hasattr(os, "mkfifo")
93 return hasattr(os, "mkfifo")
94
94
95 def has_hotshot():
95 def has_hotshot():
96 try:
96 try:
97 # hotshot.stats tests hotshot and many problematic dependencies
97 # hotshot.stats tests hotshot and many problematic dependencies
98 # like profile.
98 # like profile.
99 import hotshot.stats
99 import hotshot.stats
100 return True
100 return True
101 except ImportError:
101 except ImportError:
102 return False
102 return False
103
103
104 def has_lsprof():
104 def has_lsprof():
105 try:
105 try:
106 import _lsprof
106 import _lsprof
107 return True
107 return True
108 except ImportError:
108 except ImportError:
109 return False
109 return False
110
110
111 def has_git():
111 def has_git():
112 return matchoutput('git --version 2>&1', r'^git version')
112 return matchoutput('git --version 2>&1', r'^git version')
113
113
114 def has_svn():
114 def has_svn():
115 return matchoutput('svn --version 2>&1', r'^svn, version') and \
115 return matchoutput('svn --version 2>&1', r'^svn, version') and \
116 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
116 matchoutput('svnadmin --version 2>&1', r'^svnadmin, version')
117
117
118 def has_svn_bindings():
118 def has_svn_bindings():
119 try:
119 try:
120 import svn.core
120 import svn.core
121 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
121 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
122 if version < (1, 4):
122 if version < (1, 4):
123 return False
123 return False
124 return True
124 return True
125 except ImportError:
125 except ImportError:
126 return False
126 return False
127
127
128 def has_symlink():
128 def has_symlink():
129 return hasattr(os, "symlink")
129 return hasattr(os, "symlink")
130
130
131 def has_tla():
131 def has_tla():
132 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
132 return matchoutput('tla --version 2>&1', r'The GNU Arch Revision')
133
133
134 def has_unix_permissions():
134 def has_unix_permissions():
135 d = tempfile.mkdtemp(prefix=tempprefix, dir=".")
135 d = tempfile.mkdtemp(prefix=tempprefix, dir=".")
136 try:
136 try:
137 fname = os.path.join(d, 'foo')
137 fname = os.path.join(d, 'foo')
138 for umask in (077, 007, 022):
138 for umask in (077, 007, 022):
139 os.umask(umask)
139 os.umask(umask)
140 f = open(fname, 'w')
140 f = open(fname, 'w')
141 f.close()
141 f.close()
142 mode = os.stat(fname).st_mode
142 mode = os.stat(fname).st_mode
143 os.unlink(fname)
143 os.unlink(fname)
144 if mode & 0777 != ~umask & 0666:
144 if mode & 0777 != ~umask & 0666:
145 return False
145 return False
146 return True
146 return True
147 finally:
147 finally:
148 os.rmdir(d)
148 os.rmdir(d)
149
149
150 def has_pygments():
150 def has_pygments():
151 try:
151 try:
152 import pygments
152 import pygments
153 return True
153 return True
154 except ImportError:
154 except ImportError:
155 return False
155 return False
156
156
157 def has_outer_repo():
158 return matchoutput('hg root 2>&1', r'')
159
157 checks = {
160 checks = {
158 "baz": (has_baz, "GNU Arch baz client"),
161 "baz": (has_baz, "GNU Arch baz client"),
159 "bzr": (has_bzr, "Canonical's Bazaar client"),
162 "bzr": (has_bzr, "Canonical's Bazaar client"),
160 "cvs": (has_cvs, "cvs client/server"),
163 "cvs": (has_cvs, "cvs client/server"),
161 "cvsps": (has_cvsps, "cvsps utility"),
164 "cvsps": (has_cvsps, "cvsps utility"),
162 "darcs": (has_darcs, "darcs client"),
165 "darcs": (has_darcs, "darcs client"),
163 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
166 "eol-in-paths": (has_eol_in_paths, "end-of-lines in paths"),
164 "execbit": (has_executablebit, "executable bit"),
167 "execbit": (has_executablebit, "executable bit"),
165 "fifo": (has_fifo, "named pipes"),
168 "fifo": (has_fifo, "named pipes"),
166 "git": (has_git, "git command line client"),
169 "git": (has_git, "git command line client"),
167 "hotshot": (has_hotshot, "python hotshot module"),
170 "hotshot": (has_hotshot, "python hotshot module"),
168 "icasefs": (has_icasefs, "case insensitive file system"),
171 "icasefs": (has_icasefs, "case insensitive file system"),
169 "inotify": (has_inotify, "inotify extension support"),
172 "inotify": (has_inotify, "inotify extension support"),
170 "lsprof": (has_lsprof, "python lsprof module"),
173 "lsprof": (has_lsprof, "python lsprof module"),
171 "mtn": (has_mtn, "monotone client (> 0.31)"),
174 "mtn": (has_mtn, "monotone client (> 0.31)"),
175 "outer-repo": (has_outer_repo, "outer repo"),
172 "pygments": (has_pygments, "Pygments source highlighting library"),
176 "pygments": (has_pygments, "Pygments source highlighting library"),
173 "svn": (has_svn, "subversion client and admin tools"),
177 "svn": (has_svn, "subversion client and admin tools"),
174 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
178 "svn-bindings": (has_svn_bindings, "subversion python bindings"),
175 "symlink": (has_symlink, "symbolic links"),
179 "symlink": (has_symlink, "symbolic links"),
176 "tla": (has_tla, "GNU Arch tla client"),
180 "tla": (has_tla, "GNU Arch tla client"),
177 "unix-permissions": (has_unix_permissions, "unix-style permissions"),
181 "unix-permissions": (has_unix_permissions, "unix-style permissions"),
178 }
182 }
179
183
180 def list_features():
184 def list_features():
181 for name, feature in checks.iteritems():
185 for name, feature in checks.iteritems():
182 desc = feature[1]
186 desc = feature[1]
183 print name + ':', desc
187 print name + ':', desc
184
188
185 parser = optparse.OptionParser("%prog [options] [features]")
189 parser = optparse.OptionParser("%prog [options] [features]")
186 parser.add_option("--list-features", action="store_true",
190 parser.add_option("--list-features", action="store_true",
187 help="list available features")
191 help="list available features")
188 parser.add_option("-q", "--quiet", action="store_true",
192 parser.add_option("-q", "--quiet", action="store_true",
189 help="check features silently")
193 help="check features silently")
190
194
191 if __name__ == '__main__':
195 if __name__ == '__main__':
192 options, args = parser.parse_args()
196 options, args = parser.parse_args()
193 if options.list_features:
197 if options.list_features:
194 list_features()
198 list_features()
195 sys.exit(0)
199 sys.exit(0)
196
200
197 quiet = options.quiet
201 quiet = options.quiet
198
202
199 failures = 0
203 failures = 0
200
204
201 def error(msg):
205 def error(msg):
202 global failures
206 global failures
203 if not quiet:
207 if not quiet:
204 sys.stderr.write(msg + '\n')
208 sys.stderr.write(msg + '\n')
205 failures += 1
209 failures += 1
206
210
207 for feature in args:
211 for feature in args:
208 negate = feature.startswith('no-')
212 negate = feature.startswith('no-')
209 if negate:
213 if negate:
210 feature = feature[3:]
214 feature = feature[3:]
211
215
212 if feature not in checks:
216 if feature not in checks:
213 error('skipped: unknown feature: ' + feature)
217 error('skipped: unknown feature: ' + feature)
214 continue
218 continue
215
219
216 check, desc = checks[feature]
220 check, desc = checks[feature]
217 if not negate and not check():
221 if not negate and not check():
218 error('skipped: missing feature: ' + desc)
222 error('skipped: missing feature: ' + desc)
219 elif negate and check():
223 elif negate and check():
220 error('skipped: system supports %s' % desc)
224 error('skipped: system supports %s' % desc)
221
225
222 if failures != 0:
226 if failures != 0:
223 sys.exit(1)
227 sys.exit(1)
224
228
225
229
@@ -1,136 +1,136 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 "$TESTDIR/hghave" svn svn-bindings || exit 80
3 "$TESTDIR/hghave" svn svn-bindings no-outer-repo || exit 80
4
4
5 fixpath()
5 fixpath()
6 {
6 {
7 tr '\\' /
7 tr '\\' /
8 }
8 }
9
9
10 svnupanddisplay()
10 svnupanddisplay()
11 {
11 {
12 (
12 (
13 cd $1;
13 cd $1;
14 svn up;
14 svn up;
15 svn st -v | fixpath
15 svn st -v | fixpath
16 limit=''
16 limit=''
17 if [ $2 -gt 0 ]; then
17 if [ $2 -gt 0 ]; then
18 limit="--limit=$2"
18 limit="--limit=$2"
19 fi
19 fi
20 svn log --xml -v $limit | fixpath | sed 's,<date>.*,<date/>,'
20 svn log --xml -v $limit | fixpath | sed 's,<date>.*,<date/>,'
21 )
21 )
22 }
22 }
23
23
24 echo "[extensions]" >> $HGRCPATH
24 echo "[extensions]" >> $HGRCPATH
25 echo "convert = " >> $HGRCPATH
25 echo "convert = " >> $HGRCPATH
26
26
27 hg init a
27 hg init a
28
28
29 echo a > a/a
29 echo a > a/a
30 mkdir -p a/d1/d2
30 mkdir -p a/d1/d2
31 echo b > a/d1/d2/b
31 echo b > a/d1/d2/b
32 echo % add
32 echo % add
33 hg --cwd a ci -d '0 0' -A -m 'add a file'
33 hg --cwd a ci -d '0 0' -A -m 'add a file'
34
34
35 "$TESTDIR/svn-safe-append.py" a a/a
35 "$TESTDIR/svn-safe-append.py" a a/a
36 echo % modify
36 echo % modify
37 hg --cwd a ci -d '1 0' -m 'modify a file'
37 hg --cwd a ci -d '1 0' -m 'modify a file'
38 hg --cwd a tip -q
38 hg --cwd a tip -q
39
39
40 hg convert -d svn a
40 hg convert -d svn a
41 svnupanddisplay a-hg-wc 2
41 svnupanddisplay a-hg-wc 2
42 ls a a-hg-wc
42 ls a a-hg-wc
43 cmp a/a a-hg-wc/a && echo same || echo different
43 cmp a/a a-hg-wc/a && echo same || echo different
44
44
45 hg --cwd a mv a b
45 hg --cwd a mv a b
46 echo % rename
46 echo % rename
47 hg --cwd a ci -d '2 0' -m 'rename a file'
47 hg --cwd a ci -d '2 0' -m 'rename a file'
48 hg --cwd a tip -q
48 hg --cwd a tip -q
49
49
50 hg convert -d svn a
50 hg convert -d svn a
51 svnupanddisplay a-hg-wc 1
51 svnupanddisplay a-hg-wc 1
52 ls a a-hg-wc
52 ls a a-hg-wc
53
53
54 hg --cwd a cp b c
54 hg --cwd a cp b c
55 echo % copy
55 echo % copy
56 hg --cwd a ci -d '3 0' -m 'copy a file'
56 hg --cwd a ci -d '3 0' -m 'copy a file'
57 hg --cwd a tip -q
57 hg --cwd a tip -q
58
58
59 hg convert -d svn a
59 hg convert -d svn a
60 svnupanddisplay a-hg-wc 1
60 svnupanddisplay a-hg-wc 1
61 ls a a-hg-wc
61 ls a a-hg-wc
62
62
63 hg --cwd a rm b
63 hg --cwd a rm b
64 echo % remove
64 echo % remove
65 hg --cwd a ci -d '4 0' -m 'remove a file'
65 hg --cwd a ci -d '4 0' -m 'remove a file'
66 hg --cwd a tip -q
66 hg --cwd a tip -q
67
67
68 hg convert -d svn a
68 hg convert -d svn a
69 svnupanddisplay a-hg-wc 1
69 svnupanddisplay a-hg-wc 1
70 ls a a-hg-wc
70 ls a a-hg-wc
71
71
72 chmod +x a/c
72 chmod +x a/c
73 echo % executable
73 echo % executable
74 hg --cwd a ci -d '5 0' -m 'make a file executable'
74 hg --cwd a ci -d '5 0' -m 'make a file executable'
75 hg --cwd a tip -q
75 hg --cwd a tip -q
76
76
77 hg convert -d svn a
77 hg convert -d svn a
78 svnupanddisplay a-hg-wc 1
78 svnupanddisplay a-hg-wc 1
79 test -x a-hg-wc/c && echo executable || echo not executable
79 test -x a-hg-wc/c && echo executable || echo not executable
80
80
81 echo % executable in new directory
81 echo % executable in new directory
82
82
83 rm -rf a a-hg a-hg-wc
83 rm -rf a a-hg a-hg-wc
84 hg init a
84 hg init a
85
85
86 mkdir a/d1
86 mkdir a/d1
87 echo a > a/d1/a
87 echo a > a/d1/a
88 chmod +x a/d1/a
88 chmod +x a/d1/a
89 hg --cwd a ci -d '0 0' -A -m 'add executable file in new directory'
89 hg --cwd a ci -d '0 0' -A -m 'add executable file in new directory'
90
90
91 hg convert -d svn a
91 hg convert -d svn a
92 svnupanddisplay a-hg-wc 1
92 svnupanddisplay a-hg-wc 1
93 test -x a-hg-wc/d1/a && echo executable || echo not executable
93 test -x a-hg-wc/d1/a && echo executable || echo not executable
94
94
95 echo % copy to new directory
95 echo % copy to new directory
96
96
97 mkdir a/d2
97 mkdir a/d2
98 hg --cwd a cp d1/a d2/a
98 hg --cwd a cp d1/a d2/a
99 hg --cwd a ci -d '1 0' -A -m 'copy file to new directory'
99 hg --cwd a ci -d '1 0' -A -m 'copy file to new directory'
100
100
101 hg convert -d svn a
101 hg convert -d svn a
102 svnupanddisplay a-hg-wc 1
102 svnupanddisplay a-hg-wc 1
103
103
104 echo % branchy history
104 echo % branchy history
105
105
106 hg init b
106 hg init b
107 echo base > b/b
107 echo base > b/b
108 hg --cwd b ci -d '0 0' -Ambase
108 hg --cwd b ci -d '0 0' -Ambase
109
109
110 "$TESTDIR/svn-safe-append.py" left-1 b/b
110 "$TESTDIR/svn-safe-append.py" left-1 b/b
111 echo left-1 > b/left-1
111 echo left-1 > b/left-1
112 hg --cwd b ci -d '1 0' -Amleft-1
112 hg --cwd b ci -d '1 0' -Amleft-1
113
113
114 "$TESTDIR/svn-safe-append.py" left-2 b/b
114 "$TESTDIR/svn-safe-append.py" left-2 b/b
115 echo left-2 > b/left-2
115 echo left-2 > b/left-2
116 hg --cwd b ci -d '2 0' -Amleft-2
116 hg --cwd b ci -d '2 0' -Amleft-2
117
117
118 hg --cwd b up 0
118 hg --cwd b up 0
119
119
120 "$TESTDIR/svn-safe-append.py" right-1 b/b
120 "$TESTDIR/svn-safe-append.py" right-1 b/b
121 echo right-1 > b/right-1
121 echo right-1 > b/right-1
122 hg --cwd b ci -d '3 0' -Amright-1
122 hg --cwd b ci -d '3 0' -Amright-1
123
123
124 "$TESTDIR/svn-safe-append.py" right-2 b/b
124 "$TESTDIR/svn-safe-append.py" right-2 b/b
125 echo right-2 > b/right-2
125 echo right-2 > b/right-2
126 hg --cwd b ci -d '4 0' -Amright-2
126 hg --cwd b ci -d '4 0' -Amright-2
127
127
128 hg --cwd b up -C 2
128 hg --cwd b up -C 2
129 hg --cwd b merge
129 hg --cwd b merge
130 hg --cwd b revert -r 2 b
130 hg --cwd b revert -r 2 b
131 hg resolve -m b
131 hg resolve -m b
132 hg --cwd b ci -d '5 0' -m 'merge'
132 hg --cwd b ci -d '5 0' -m 'merge'
133
133
134 hg convert -d svn b
134 hg convert -d svn b
135 echo % expect 4 changes
135 echo % expect 4 changes
136 svnupanddisplay b-hg-wc 0
136 svnupanddisplay b-hg-wc 0
@@ -1,25 +1,27 b''
1 #!/bin/sh
1 #!/bin/sh
2 # test command parsing and dispatch
2 # test command parsing and dispatch
3
3
4 "$TESTDIR/hghave" no-outer-repo || exit 80
5
4 hg init a
6 hg init a
5 cd a
7 cd a
6 echo a > a
8 echo a > a
7 hg ci -Ama -d '0 0'
9 hg ci -Ama -d '0 0'
8
10
9 echo "# missing arg"
11 echo "# missing arg"
10 hg cat
12 hg cat
11
13
12 echo '% [defaults]'
14 echo '% [defaults]'
13 hg cat a
15 hg cat a
14 cat >> $HGRCPATH <<EOF
16 cat >> $HGRCPATH <<EOF
15 [defaults]
17 [defaults]
16 cat = -v
18 cat = -v
17 EOF
19 EOF
18 hg cat a
20 hg cat a
19
21
20 echo '% no repo'
22 echo '% no repo'
21 cd ..
23 cd ..
22 hg cat
24 hg cat
23
25
24 exit 0
26 exit 0
25
27
@@ -1,104 +1,106 b''
1 #!/bin/sh
1 #!/bin/sh
2 # Test basic extension support
2 # Test basic extension support
3
3
4 "$TESTDIR/hghave" no-outer-repo || exit 80
5
4 cat > foobar.py <<EOF
6 cat > foobar.py <<EOF
5 import os
7 import os
6 from mercurial import commands
8 from mercurial import commands
7
9
8 def uisetup(ui):
10 def uisetup(ui):
9 ui.write("uisetup called\\n")
11 ui.write("uisetup called\\n")
10 ui.write("ui.parentui is%s None\\n" % (ui.parentui is not None
12 ui.write("ui.parentui is%s None\\n" % (ui.parentui is not None
11 and "not" or ""))
13 and "not" or ""))
12
14
13 def reposetup(ui, repo):
15 def reposetup(ui, repo):
14 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
16 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
15 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
17 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
16
18
17 def foo(ui, *args, **kwargs):
19 def foo(ui, *args, **kwargs):
18 ui.write("Foo\\n")
20 ui.write("Foo\\n")
19
21
20 def bar(ui, *args, **kwargs):
22 def bar(ui, *args, **kwargs):
21 ui.write("Bar\\n")
23 ui.write("Bar\\n")
22
24
23 cmdtable = {
25 cmdtable = {
24 "foo": (foo, [], "hg foo"),
26 "foo": (foo, [], "hg foo"),
25 "bar": (bar, [], "hg bar"),
27 "bar": (bar, [], "hg bar"),
26 }
28 }
27
29
28 commands.norepo += ' bar'
30 commands.norepo += ' bar'
29 EOF
31 EOF
30 abspath=`pwd`/foobar.py
32 abspath=`pwd`/foobar.py
31
33
32 mkdir barfoo
34 mkdir barfoo
33 cp foobar.py barfoo/__init__.py
35 cp foobar.py barfoo/__init__.py
34 barfoopath=`pwd`/barfoo
36 barfoopath=`pwd`/barfoo
35
37
36 hg init a
38 hg init a
37 cd a
39 cd a
38 echo foo > file
40 echo foo > file
39 hg add file
41 hg add file
40 hg commit -m 'add file'
42 hg commit -m 'add file'
41
43
42 echo '[extensions]' >> $HGRCPATH
44 echo '[extensions]' >> $HGRCPATH
43 echo "foobar = $abspath" >> $HGRCPATH
45 echo "foobar = $abspath" >> $HGRCPATH
44 hg foo
46 hg foo
45
47
46 cd ..
48 cd ..
47 hg clone a b
49 hg clone a b
48
50
49 hg bar
51 hg bar
50 echo 'foobar = !' >> $HGRCPATH
52 echo 'foobar = !' >> $HGRCPATH
51
53
52 echo '% module/__init__.py-style'
54 echo '% module/__init__.py-style'
53 echo "barfoo = $barfoopath" >> $HGRCPATH
55 echo "barfoo = $barfoopath" >> $HGRCPATH
54 cd a
56 cd a
55 hg foo
57 hg foo
56 echo 'barfoo = !' >> $HGRCPATH
58 echo 'barfoo = !' >> $HGRCPATH
57
59
58 cd ..
60 cd ..
59 cat > empty.py <<EOF
61 cat > empty.py <<EOF
60 '''empty cmdtable
62 '''empty cmdtable
61 '''
63 '''
62 cmdtable = {}
64 cmdtable = {}
63 EOF
65 EOF
64 emptypath=`pwd`/empty.py
66 emptypath=`pwd`/empty.py
65 echo "empty = $emptypath" >> $HGRCPATH
67 echo "empty = $emptypath" >> $HGRCPATH
66 hg help empty
68 hg help empty
67 echo 'empty = !' >> $HGRCPATH
69 echo 'empty = !' >> $HGRCPATH
68
70
69 cat > debugextension.py <<EOF
71 cat > debugextension.py <<EOF
70 '''only debugcommands
72 '''only debugcommands
71 '''
73 '''
72 def debugfoobar(ui, repo, *args, **opts):
74 def debugfoobar(ui, repo, *args, **opts):
73 "yet another debug command"
75 "yet another debug command"
74 pass
76 pass
75
77
76 cmdtable = {"debugfoobar": (debugfoobar, (), "hg debugfoobar")}
78 cmdtable = {"debugfoobar": (debugfoobar, (), "hg debugfoobar")}
77 EOF
79 EOF
78 debugpath=`pwd`/debugextension.py
80 debugpath=`pwd`/debugextension.py
79 echo "debugextension = $debugpath" >> $HGRCPATH
81 echo "debugextension = $debugpath" >> $HGRCPATH
80 hg help debugextension
82 hg help debugextension
81 hg --debug help debugextension
83 hg --debug help debugextension
82 echo 'debugextension = !' >> $HGRCPATH
84 echo 'debugextension = !' >> $HGRCPATH
83
85
84 echo % issue811
86 echo % issue811
85 debugpath=`pwd`/debugissue811.py
87 debugpath=`pwd`/debugissue811.py
86 cat > debugissue811.py <<EOF
88 cat > debugissue811.py <<EOF
87 '''show all loaded extensions
89 '''show all loaded extensions
88 '''
90 '''
89 from mercurial import extensions, commands
91 from mercurial import extensions, commands
90
92
91 def debugextensions(ui):
93 def debugextensions(ui):
92 "yet another debug command"
94 "yet another debug command"
93 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
95 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
94
96
95 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
97 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
96 commands.norepo += " debugextensions"
98 commands.norepo += " debugextensions"
97 EOF
99 EOF
98 echo "debugissue811 = $debugpath" >> $HGRCPATH
100 echo "debugissue811 = $debugpath" >> $HGRCPATH
99 echo "mq=" >> $HGRCPATH
101 echo "mq=" >> $HGRCPATH
100 echo "hgext.mq=" >> $HGRCPATH
102 echo "hgext.mq=" >> $HGRCPATH
101 echo "hgext/mq=" >> $HGRCPATH
103 echo "hgext/mq=" >> $HGRCPATH
102
104
103 echo % show extensions
105 echo % show extensions
104 hg debugextensions
106 hg debugextensions
@@ -1,94 +1,96 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 "$TESTDIR/hghave" no-outer-repo || exit 80
4
3 hg init a
5 hg init a
4 cd a
6 cd a
5 echo a > a
7 echo a > a
6 hg ci -A -d'1 0' -m a
8 hg ci -A -d'1 0' -m a
7
9
8 cd ..
10 cd ..
9
11
10 hg init b
12 hg init b
11 cd b
13 cd b
12 echo b > b
14 echo b > b
13 hg ci -A -d'1 0' -m b
15 hg ci -A -d'1 0' -m b
14
16
15 cd ..
17 cd ..
16
18
17 hg clone a c
19 hg clone a c
18 cd c
20 cd c
19 hg pull -f ../b
21 hg pull -f ../b
20 hg merge
22 hg merge
21
23
22 cd ..
24 cd ..
23
25
24 echo %% -R/--repository
26 echo %% -R/--repository
25 hg -R a tip
27 hg -R a tip
26 hg --repository b tip
28 hg --repository b tip
27
29
28 echo %% implicit -R
30 echo %% implicit -R
29 hg ann a/a
31 hg ann a/a
30 hg ann a/a a/a
32 hg ann a/a a/a
31 hg ann a/a b/b
33 hg ann a/a b/b
32 hg -R b ann a/a
34 hg -R b ann a/a
33 hg log
35 hg log
34
36
35 echo %% abbrev of long option
37 echo %% abbrev of long option
36 hg --repo c tip
38 hg --repo c tip
37
39
38 echo "%% earlygetopt with duplicate options (36d23de02da1)"
40 echo "%% earlygetopt with duplicate options (36d23de02da1)"
39 hg --cwd a --cwd b --cwd c tip
41 hg --cwd a --cwd b --cwd c tip
40 hg --repo c --repository b -R a tip
42 hg --repo c --repository b -R a tip
41
43
42 echo "%% earlygetopt short option without following space"
44 echo "%% earlygetopt short option without following space"
43 hg -q -Rb tip
45 hg -q -Rb tip
44
46
45 echo "%% earlygetopt with illegal abbreviations"
47 echo "%% earlygetopt with illegal abbreviations"
46 hg --confi "foo.bar=baz"
48 hg --confi "foo.bar=baz"
47 hg --cw a tip
49 hg --cw a tip
48 hg --rep a tip
50 hg --rep a tip
49 hg --repositor a tip
51 hg --repositor a tip
50 hg -qR a tip
52 hg -qR a tip
51 hg -qRa tip
53 hg -qRa tip
52
54
53 echo %% --cwd
55 echo %% --cwd
54 hg --cwd a parents
56 hg --cwd a parents
55
57
56 echo %% -y/--noninteractive - just be sure it is parsed
58 echo %% -y/--noninteractive - just be sure it is parsed
57 hg --cwd a tip -q --noninteractive
59 hg --cwd a tip -q --noninteractive
58 hg --cwd a tip -q -y
60 hg --cwd a tip -q -y
59
61
60 echo %% -q/--quiet
62 echo %% -q/--quiet
61 hg -R a -q tip
63 hg -R a -q tip
62 hg -R b -q tip
64 hg -R b -q tip
63 hg -R c --quiet parents
65 hg -R c --quiet parents
64
66
65 echo %% -v/--verbose
67 echo %% -v/--verbose
66 hg --cwd c head -v
68 hg --cwd c head -v
67 hg --cwd b tip --verbose
69 hg --cwd b tip --verbose
68
70
69 echo %% --config
71 echo %% --config
70 hg --cwd c --config paths.quuxfoo=bar paths | grep quuxfoo > /dev/null && echo quuxfoo
72 hg --cwd c --config paths.quuxfoo=bar paths | grep quuxfoo > /dev/null && echo quuxfoo
71 hg --cwd c --config '' tip -q
73 hg --cwd c --config '' tip -q
72 hg --cwd c --config a.b tip -q
74 hg --cwd c --config a.b tip -q
73 hg --cwd c --config a tip -q
75 hg --cwd c --config a tip -q
74 hg --cwd c --config a.= tip -q
76 hg --cwd c --config a.= tip -q
75 hg --cwd c --config .b= tip -q
77 hg --cwd c --config .b= tip -q
76
78
77 echo %% --debug
79 echo %% --debug
78 hg --cwd c log --debug
80 hg --cwd c log --debug
79
81
80 echo %% --traceback
82 echo %% --traceback
81 hg --cwd c --config x --traceback tip 2>&1 | grep -i 'traceback'
83 hg --cwd c --config x --traceback tip 2>&1 | grep -i 'traceback'
82
84
83 echo %% --time
85 echo %% --time
84 hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g'
86 hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g'
85
87
86 echo %% --version
88 echo %% --version
87 hg --version -q | sed 's/version [^)]*/version xxx/'
89 hg --version -q | sed 's/version [^)]*/version xxx/'
88
90
89 echo %% -h/--help
91 echo %% -h/--help
90 hg -h
92 hg -h
91 hg --help
93 hg --help
92
94
93 echo %% not tested: --debugger
95 echo %% not tested: --debugger
94
96
@@ -1,166 +1,168 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # @ (34) head
3 # @ (34) head
4 # |
4 # |
5 # | o (33) head
5 # | o (33) head
6 # | |
6 # | |
7 # o | (32) expand
7 # o | (32) expand
8 # |\ \
8 # |\ \
9 # | o \ (31) expand
9 # | o \ (31) expand
10 # | |\ \
10 # | |\ \
11 # | | o \ (30) expand
11 # | | o \ (30) expand
12 # | | |\ \
12 # | | |\ \
13 # | | | o | (29) regular commit
13 # | | | o | (29) regular commit
14 # | | | | |
14 # | | | | |
15 # | | o | | (28) merge zero known
15 # | | o | | (28) merge zero known
16 # | | |\ \ \
16 # | | |\ \ \
17 # o | | | | | (27) collapse
17 # o | | | | | (27) collapse
18 # |/ / / / /
18 # |/ / / / /
19 # | | o---+ (26) merge one known; far right
19 # | | o---+ (26) merge one known; far right
20 # | | | | |
20 # | | | | |
21 # +---o | | (25) merge one known; far left
21 # +---o | | (25) merge one known; far left
22 # | | | | |
22 # | | | | |
23 # | | o | | (24) merge one known; immediate right
23 # | | o | | (24) merge one known; immediate right
24 # | | |\| |
24 # | | |\| |
25 # | | o | | (23) merge one known; immediate left
25 # | | o | | (23) merge one known; immediate left
26 # | |/| | |
26 # | |/| | |
27 # +---o---+ (22) merge two known; one far left, one far right
27 # +---o---+ (22) merge two known; one far left, one far right
28 # | | / /
28 # | | / /
29 # o | | | (21) expand
29 # o | | | (21) expand
30 # |\ \ \ \
30 # |\ \ \ \
31 # | o---+-+ (20) merge two known; two far right
31 # | o---+-+ (20) merge two known; two far right
32 # | / / /
32 # | / / /
33 # o | | | (19) expand
33 # o | | | (19) expand
34 # |\ \ \ \
34 # |\ \ \ \
35 # +---+---o (18) merge two known; two far left
35 # +---+---o (18) merge two known; two far left
36 # | | | |
36 # | | | |
37 # | o | | (17) expand
37 # | o | | (17) expand
38 # | |\ \ \
38 # | |\ \ \
39 # | | o---+ (16) merge two known; one immediate right, one near right
39 # | | o---+ (16) merge two known; one immediate right, one near right
40 # | | |/ /
40 # | | |/ /
41 # o | | | (15) expand
41 # o | | | (15) expand
42 # |\ \ \ \
42 # |\ \ \ \
43 # | o-----+ (14) merge two known; one immediate right, one far right
43 # | o-----+ (14) merge two known; one immediate right, one far right
44 # | |/ / /
44 # | |/ / /
45 # o | | | (13) expand
45 # o | | | (13) expand
46 # |\ \ \ \
46 # |\ \ \ \
47 # +---o | | (12) merge two known; one immediate right, one far left
47 # +---o | | (12) merge two known; one immediate right, one far left
48 # | | |/ /
48 # | | |/ /
49 # | o | | (11) expand
49 # | o | | (11) expand
50 # | |\ \ \
50 # | |\ \ \
51 # | | o---+ (10) merge two known; one immediate left, one near right
51 # | | o---+ (10) merge two known; one immediate left, one near right
52 # | |/ / /
52 # | |/ / /
53 # o | | | (9) expand
53 # o | | | (9) expand
54 # |\ \ \ \
54 # |\ \ \ \
55 # | o-----+ (8) merge two known; one immediate left, one far right
55 # | o-----+ (8) merge two known; one immediate left, one far right
56 # |/ / / /
56 # |/ / / /
57 # o | | | (7) expand
57 # o | | | (7) expand
58 # |\ \ \ \
58 # |\ \ \ \
59 # +---o | | (6) merge two known; one immediate left, one far left
59 # +---o | | (6) merge two known; one immediate left, one far left
60 # | |/ / /
60 # | |/ / /
61 # | o | | (5) expand
61 # | o | | (5) expand
62 # | |\ \ \
62 # | |\ \ \
63 # | | o | | (4) merge two known; one immediate left, one immediate right
63 # | | o | | (4) merge two known; one immediate left, one immediate right
64 # | |/|/ /
64 # | |/|/ /
65 # | o / / (3) collapse
65 # | o / / (3) collapse
66 # |/ / /
66 # |/ / /
67 # o / / (2) collapse
67 # o / / (2) collapse
68 # |/ /
68 # |/ /
69 # o / (1) collapse
69 # o / (1) collapse
70 # |/
70 # |/
71 # o (0) root
71 # o (0) root
72
72
73 "$TESTDIR/hghave" no-outer-repo || exit 80
74
73 set -e
75 set -e
74
76
75 commit()
77 commit()
76 {
78 {
77 rev=$1
79 rev=$1
78 msg=$2
80 msg=$2
79 shift 2
81 shift 2
80 if [ "$#" -gt 0 ]; then
82 if [ "$#" -gt 0 ]; then
81 hg debugsetparents "$@"
83 hg debugsetparents "$@"
82 fi
84 fi
83 echo $rev > $rev
85 echo $rev > $rev
84 hg add $rev
86 hg add $rev
85 hg rawcommit -q -d "$rev 0" -m "($rev) $msg" $rev
87 hg rawcommit -q -d "$rev 0" -m "($rev) $msg" $rev
86 }
88 }
87
89
88 echo "[extensions]" >> $HGRCPATH
90 echo "[extensions]" >> $HGRCPATH
89 echo "graphlog=" >> $HGRCPATH
91 echo "graphlog=" >> $HGRCPATH
90
92
91 echo % init
93 echo % init
92 hg init repo
94 hg init repo
93
95
94 cd repo
96 cd repo
95
97
96 echo % empty repo
98 echo % empty repo
97 hg glog
99 hg glog
98
100
99 echo % building tree
101 echo % building tree
100 commit 0 "root"
102 commit 0 "root"
101 commit 1 "collapse" 0
103 commit 1 "collapse" 0
102 commit 2 "collapse" 1
104 commit 2 "collapse" 1
103 commit 3 "collapse" 2
105 commit 3 "collapse" 2
104 commit 4 "merge two known; one immediate left, one immediate right" 1 3
106 commit 4 "merge two known; one immediate left, one immediate right" 1 3
105 commit 5 "expand" 3 4
107 commit 5 "expand" 3 4
106 commit 6 "merge two known; one immediate left, one far left" 2 5
108 commit 6 "merge two known; one immediate left, one far left" 2 5
107 commit 7 "expand" 2 5
109 commit 7 "expand" 2 5
108 commit 8 "merge two known; one immediate left, one far right" 0 7
110 commit 8 "merge two known; one immediate left, one far right" 0 7
109 commit 9 "expand" 7 8
111 commit 9 "expand" 7 8
110 commit 10 "merge two known; one immediate left, one near right" 0 6
112 commit 10 "merge two known; one immediate left, one near right" 0 6
111 commit 11 "expand" 6 10
113 commit 11 "expand" 6 10
112 commit 12 "merge two known; one immediate right, one far left" 1 9
114 commit 12 "merge two known; one immediate right, one far left" 1 9
113 commit 13 "expand" 9 11
115 commit 13 "expand" 9 11
114 commit 14 "merge two known; one immediate right, one far right" 0 12
116 commit 14 "merge two known; one immediate right, one far right" 0 12
115 commit 15 "expand" 13 14
117 commit 15 "expand" 13 14
116 commit 16 "merge two known; one immediate right, one near right" 0 1
118 commit 16 "merge two known; one immediate right, one near right" 0 1
117 commit 17 "expand" 12 16
119 commit 17 "expand" 12 16
118 commit 18 "merge two known; two far left" 1 15
120 commit 18 "merge two known; two far left" 1 15
119 commit 19 "expand" 15 17
121 commit 19 "expand" 15 17
120 commit 20 "merge two known; two far right" 0 18
122 commit 20 "merge two known; two far right" 0 18
121 commit 21 "expand" 19 20
123 commit 21 "expand" 19 20
122 commit 22 "merge two known; one far left, one far right" 18 21
124 commit 22 "merge two known; one far left, one far right" 18 21
123 commit 23 "merge one known; immediate left" 1 22
125 commit 23 "merge one known; immediate left" 1 22
124 commit 24 "merge one known; immediate right" 0 23
126 commit 24 "merge one known; immediate right" 0 23
125 commit 25 "merge one known; far left" 21 24
127 commit 25 "merge one known; far left" 21 24
126 commit 26 "merge one known; far right" 18 25
128 commit 26 "merge one known; far right" 18 25
127 commit 27 "collapse" 21
129 commit 27 "collapse" 21
128 commit 28 "merge zero known" 1 26
130 commit 28 "merge zero known" 1 26
129 commit 29 "regular commit" 0
131 commit 29 "regular commit" 0
130 commit 30 "expand" 28 29
132 commit 30 "expand" 28 29
131 commit 31 "expand" 21 30
133 commit 31 "expand" 21 30
132 commit 32 "expand" 27 31
134 commit 32 "expand" 27 31
133 commit 33 "head" 18
135 commit 33 "head" 18
134 commit 34 "head" 32
136 commit 34 "head" 32
135
137
136 echo % glog -q
138 echo % glog -q
137 hg glog -q
139 hg glog -q
138
140
139 echo % glog
141 echo % glog
140 hg glog
142 hg glog
141
143
142 echo % file glog
144 echo % file glog
143 hg glog 5
145 hg glog 5
144
146
145 echo % unused arguments
147 echo % unused arguments
146 hg glog -q foo bar || echo failed
148 hg glog -q foo bar || echo failed
147
149
148 echo % from outer space
150 echo % from outer space
149 cd ..
151 cd ..
150 hg glog -l1 repo
152 hg glog -l1 repo
151 hg glog -l1 repo/0
153 hg glog -l1 repo/0
152 hg glog -l1 repo/missing
154 hg glog -l1 repo/missing
153
155
154 echo % file log with revs != cset revs
156 echo % file log with revs != cset revs
155 hg init flog
157 hg init flog
156 cd flog
158 cd flog
157 echo one >one
159 echo one >one
158 hg add one
160 hg add one
159 hg commit -mone
161 hg commit -mone
160 echo two >two
162 echo two >two
161 hg add two
163 hg add two
162 hg commit -mtwo
164 hg commit -mtwo
163 echo more >two
165 echo more >two
164 hg commit -mmore
166 hg commit -mmore
165 hg glog two
167 hg glog two
166
168
@@ -1,44 +1,44 b''
1 #!/bin/sh
1 #!/bin/sh
2 # Some tests for hgweb. Tests static files, plain files and different 404's.
2 # Some tests for hgweb. Tests static files, plain files and different 404's.
3
3
4 hg init test
4 hg init test
5 cd test
5 cd test
6 mkdir da
6 mkdir da
7 echo foo > da/foo
7 echo foo > da/foo
8 echo foo > foo
8 echo foo > foo
9 hg ci -Ambase -d '0 0'
9 hg ci -Ambase -d '0 0'
10 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
10 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
11 cat hg.pid >> $DAEMON_PIDS
11 cat hg.pid >> $DAEMON_PIDS
12 echo % manifest
12 echo % manifest
13 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw')
13 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/?style=raw')
14 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/da?style=raw')
14 ("$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/da?style=raw')
15
15
16 echo % plain file
16 echo % plain file
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?style=raw'
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?style=raw'
18
18
19 echo % should give a 404 - static file that does not exist
19 echo % should give a 404 - static file that does not exist
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/bogus'
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/bogus'
21
21
22 echo % should give a 404 - bad revision
22 echo % should give a 404 - bad revision
23 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/spam/foo?style=raw'
23 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/spam/foo?style=raw'
24
24
25 echo % should give a 400 - bad command
25 echo % should give a 400 - bad command
26 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?cmd=spam&style=raw' | sed 's/400.*/400/'
26 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/foo?cmd=spam&style=raw' | sed 's/400.*/400/'
27
27
28 echo % should give a 404 - file does not exist
28 echo % should give a 404 - file does not exist
29 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw'
29 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork?style=raw'
30 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork'
30 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/file/tip/bork'
31 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/bork?style=raw'
31 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/bork?style=raw'
32
32
33 echo % stop and restart
33 echo % stop and restart
34 kill `cat hg.pid`
34 "$TESTDIR/killdaemons.py"
35 hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
35 hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log
36 cat hg.pid >> $DAEMON_PIDS
36 cat hg.pid >> $DAEMON_PIDS
37 # Test the access/error files are opened in append mode
37 # Test the access/error files are opened in append mode
38 python -c "print len(file('access.log').readlines()), 'log lines written'"
38 python -c "print len(file('access.log').readlines()), 'log lines written'"
39
39
40 echo % static file
40 echo % static file
41 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css'
41 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/static/style-gitweb.css'
42
42
43 echo % errors
43 echo % errors
44 cat errors.log
44 cat errors.log
@@ -1,34 +1,34 b''
1 echo % setting up repo
1 echo % setting up repo
2 hg init test
2 hg init test
3 cd test
3 cd test
4 echo a > a
4 echo a > a
5 echo b > b
5 echo b > b
6 hg ci -Ama
6 hg ci -Ama
7
7
8 echo % change permissions for git diffs
8 echo % change permissions for git diffs
9 chmod 755 a
9 chmod 755 a
10 hg ci -Amb
10 hg ci -Amb
11
11
12 echo % set up hgweb
12 echo % set up hgweb
13 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
13 hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
14 cat hg.pid >> $DAEMON_PIDS
14 cat hg.pid >> $DAEMON_PIDS
15
15
16 echo % revision
16 echo % revision
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0'
17 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0'
18
18
19 echo % diff removed file
19 echo % diff removed file
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
20 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
21
21
22 echo % set up hgweb with git diffs
22 echo % set up hgweb with git diffs
23 kill `cat hg.pid`
23 "$TESTDIR/killdaemons.py"
24 hg serve --config 'diff.git=1' -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
24 hg serve --config 'diff.git=1' -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
25 cat hg.pid >> $DAEMON_PIDS
25 cat hg.pid >> $DAEMON_PIDS
26
26
27 echo % revision
27 echo % revision
28 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0'
28 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/rev/0'
29
29
30 echo % diff removed file
30 echo % diff removed file
31 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
31 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/diff/tip/a'
32
32
33 echo % errors
33 echo % errors
34 cat errors.log
34 cat errors.log
General Comments 0
You need to be logged in to leave comments. Login now