##// END OF EJS Templates
merge with crew
Vadim Gelfer -
r1584:b3e94785 merge default
parent child Browse files
Show More
@@ -0,0 +1,45 b''
1 #!/usr/bin/env python
2 #
3 # Copyright 2005 by Intevation GmbH <intevation@intevation.de>
4 # Author(s):
5 # Thomas Arendsen Hein <thomas@intevation.de>
6 #
7 # This software may be used and distributed according to the terms
8 # of the GNU General Public License, incorporated herein by reference.
9
10 """
11 hg-ssh - a wrapper for ssh access to a limited set of mercurial repos
12
13 To be used in ~/.ssh/authorized_keys with the "command" option, see sshd(8):
14 command="hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4" ssh-dss ...
15 (probably together with these other useful options:
16 no-port-forwarding,no-X11-forwarding,no-agent-forwarding)
17
18 This allows pull/push over ssh to to the repositories given as arguments.
19
20 If all your repositories are subdirectories of a common directory, you can
21 allow shorter paths with:
22 command="cd path/to/my/repositories && hg-ssh repo1 subdir/repo2"
23 """
24
25 from mercurial import commands
26
27 import sys, os
28
29 cwd = os.getcwd()
30 allowed_paths = [os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
31 for path in sys.argv[1:]]
32 orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
33
34 if orig_cmd.startswith('hg -R ') and orig_cmd.endswith(' serve --stdio'):
35 path = orig_cmd[6:-14]
36 repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
37 if repo in allowed_paths:
38 commands.dispatch(['-R', repo, 'serve', '--stdio'])
39 else:
40 sys.stderr.write("Illegal repository %r\n" % repo)
41 sys.exit(-1)
42 else:
43 sys.stderr.write("Illegal command %r\n" % orig_cmd)
44 sys.exit(-1)
45
@@ -0,0 +1,15 b''
1 #header#
2 <title>Mercurial Error</title>
3 </head>
4 <body>
5
6 <h2>Mercurial Error</h2>
7
8 <p>
9 An error occured while processing your request:
10 </p>
11 <p>
12 #error|escape#
13 </p>
14
15 #footer#
@@ -0,0 +1,18 b''
1 #!/bin/sh
2 #
3 mkdir t
4 cd t
5 hg init
6 echo 0 > a
7 echo 0 > b
8 hg ci -A -m m -d "0 0"
9 hg rm a
10 hg cat a
11 sleep 1 # make sure mtime is changed
12 echo 1 > b
13 hg ci -m m -d "0 0"
14 echo 2 > b
15 hg cat -r 0 a
16 hg cat -r 0 b
17 hg cat -r 1 a
18 hg cat -r 1 b
@@ -0,0 +1,7 b''
1 adding a
2 adding b
3 0
4 0
5 0
6 a: No such file in rev 551e7cb14b32
7 1
@@ -0,0 +1,136 b''
1 #!/bin/sh
2
3 hg init
4 mkdir d1 d1/d11 d2
5 echo d1/a > d1/a
6 echo d1/ba > d1/ba
7 echo d1/a1 > d1/d11/a1
8 echo d1/b > d1/b
9 echo d2/b > d2/b
10 hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
11 hg commit -m "1" -d "0 0"
12
13 echo "# rename a single file"
14 hg rename d1/d11/a1 d2/c
15 hg status
16 hg update -C
17
18 echo "# rename --after a single file"
19 mv d1/d11/a1 d2/c
20 hg rename --after d1/d11/a1 d2/c
21 hg status
22 hg update -C
23
24 echo "# move a single file to an existing directory"
25 hg rename d1/d11/a1 d2
26 hg status
27 hg update -C
28
29 echo "# move --after a single file to an existing directory"
30 mv d1/d11/a1 d2
31 hg rename --after d1/d11/a1 d2
32 hg status
33 hg update -C
34
35 echo "# rename a file using a relative path"
36 (cd d1/d11; hg rename ../../d2/b e)
37 hg status
38 hg update -C
39
40 echo "# rename --after a file using a relative path"
41 (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
42 hg status
43 hg update -C
44
45 echo "# rename directory d1 as d3"
46 hg rename d1/ d3
47 hg status
48 hg update -C
49
50 echo "# rename --after directory d1 as d3"
51 mv d1 d3
52 hg rename --after d1 d3
53 hg status
54 hg update -C
55
56 echo "# move a directory using a relative path"
57 (cd d2; mkdir d3; hg rename ../d1/d11 d3)
58 hg status
59 hg update -C
60
61 echo "# move --after a directory using a relative path"
62 (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
63 hg status
64 hg update -C
65
66 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
67 hg rename d1/d11/ d2
68 hg status
69 hg update -C
70
71 echo "# move directories d1 and d2 to a new directory d3"
72 mkdir d3
73 hg rename d1 d2 d3
74 hg status
75 hg update -C
76
77 echo "# move --after directories d1 and d2 to a new directory d3"
78 mkdir d3
79 mv d1 d2 d3
80 hg rename --after d1 d2 d3
81 hg status
82 hg update -C
83
84 echo "# move everything under directory d1 to existing directory d2, do not"
85 echo "# overwrite existing files (d2/b)"
86 hg rename d1/* d2
87 hg status
88 diff d1/b d2/b
89 hg update -C
90
91 echo "# attempt to move potentially more than one file into a non-existent"
92 echo "# directory"
93 hg rename 'glob:d1/**' dx
94
95 echo "# move every file under d1 to d2/d21 (glob)"
96 mkdir d2/d21
97 hg rename 'glob:d1/**' d2/d21
98 hg status
99 hg update -C
100
101 echo "# move --after some files under d1 to d2/d21 (glob)"
102 mkdir d2/d21
103 mv d1/a d1/d11/a1 d2/d21
104 hg rename --after 'glob:d1/**' d2/d21
105 hg status
106 hg update -C
107
108 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
109 mkdir d2/d21
110 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
111 hg status
112 hg update -C
113
114 echo "# attempt to overwrite an existing file"
115 echo "ca" > d1/ca
116 hg rename d1/ba d1/ca
117 hg status
118 hg update -C
119
120 echo "# forced overwrite of an existing file"
121 echo "ca" > d1/ca
122 hg rename --force d1/ba d1/ca
123 hg status
124 hg update -C
125
126 echo "# replace a symlink with a file"
127 ln -s ba d1/ca
128 hg rename --force d1/ba d1/ca
129 hg status
130 hg update -C
131
132 echo "# do not copy more than one source file to the same destination file"
133 mkdir d3
134 hg rename d1/* d2/* d3
135 hg status
136 hg update -C
@@ -0,0 +1,183 b''
1 # rename a single file
2 A d2/c
3 R d1/d11/a1
4 # rename --after a single file
5 A d2/c
6 R d1/d11/a1
7 # move a single file to an existing directory
8 A d2/a1
9 R d1/d11/a1
10 # move --after a single file to an existing directory
11 A d2/a1
12 R d1/d11/a1
13 # rename a file using a relative path
14 A d1/d11/e
15 R d2/b
16 # rename --after a file using a relative path
17 A d1/d11/e
18 R d2/b
19 # rename directory d1 as d3
20 copying d1/a to d3/a
21 copying d1/b to d3/b
22 copying d1/ba to d3/ba
23 copying d1/d11/a1 to d3/d11/a1
24 removing d1/a
25 removing d1/b
26 removing d1/ba
27 removing d1/d11/a1
28 A d3/a
29 A d3/b
30 A d3/ba
31 A d3/d11/a1
32 R d1/a
33 R d1/b
34 R d1/ba
35 R d1/d11/a1
36 # rename --after directory d1 as d3
37 copying d1/a to d3/a
38 copying d1/b to d3/b
39 copying d1/ba to d3/ba
40 copying d1/d11/a1 to d3/d11/a1
41 removing d1/a
42 removing d1/b
43 removing d1/ba
44 removing d1/d11/a1
45 A d3/a
46 A d3/b
47 A d3/ba
48 A d3/d11/a1
49 R d1/a
50 R d1/b
51 R d1/ba
52 R d1/d11/a1
53 # move a directory using a relative path
54 copying ../d1/d11/a1 to d3/d11/a1
55 removing ../d1/d11/a1
56 A d2/d3/d11/a1
57 R d1/d11/a1
58 # move --after a directory using a relative path
59 copying ../d1/d11/a1 to d3/d11/a1
60 removing ../d1/d11/a1
61 A d2/d3/d11/a1
62 R d1/d11/a1
63 # move directory d1/d11 to an existing directory d2 (removes empty d1)
64 copying d1/d11/a1 to d2/d11/a1
65 removing d1/d11/a1
66 A d2/d11/a1
67 R d1/d11/a1
68 # move directories d1 and d2 to a new directory d3
69 copying d1/a to d3/d1/a
70 copying d1/b to d3/d1/b
71 copying d1/ba to d3/d1/ba
72 copying d1/d11/a1 to d3/d1/d11/a1
73 copying d2/b to d3/d2/b
74 removing d1/a
75 removing d1/b
76 removing d1/ba
77 removing d1/d11/a1
78 removing d2/b
79 A d3/d1/a
80 A d3/d1/b
81 A d3/d1/ba
82 A d3/d1/d11/a1
83 A d3/d2/b
84 R d1/a
85 R d1/b
86 R d1/ba
87 R d1/d11/a1
88 R d2/b
89 # move --after directories d1 and d2 to a new directory d3
90 copying d1/a to d3/d1/a
91 copying d1/b to d3/d1/b
92 copying d1/ba to d3/d1/ba
93 copying d1/d11/a1 to d3/d1/d11/a1
94 copying d2/b to d3/d2/b
95 removing d1/a
96 removing d1/b
97 removing d1/ba
98 removing d1/d11/a1
99 removing d2/b
100 A d3/d1/a
101 A d3/d1/b
102 A d3/d1/ba
103 A d3/d1/d11/a1
104 A d3/d2/b
105 R d1/a
106 R d1/b
107 R d1/ba
108 R d1/d11/a1
109 R d2/b
110 # move everything under directory d1 to existing directory d2, do not
111 # overwrite existing files (d2/b)
112 d2/b: not overwriting - file exists
113 copying d1/d11/a1 to d2/d11/a1
114 removing d1/d11/a1
115 A d2/a
116 A d2/ba
117 A d2/d11/a1
118 R d1/a
119 R d1/ba
120 R d1/d11/a1
121 1c1
122 < d1/b
123 ---
124 > d2/b
125 # attempt to move potentially more than one file into a non-existent
126 # directory
127 abort: with multiple sources, destination must be an existing directory
128 # move every file under d1 to d2/d21 (glob)
129 copying d1/a to d2/d21/a
130 copying d1/b to d2/d21/b
131 copying d1/ba to d2/d21/ba
132 copying d1/d11/a1 to d2/d21/a1
133 removing d1/a
134 removing d1/b
135 removing d1/ba
136 removing d1/d11/a1
137 A d2/d21/a
138 A d2/d21/a1
139 A d2/d21/b
140 A d2/d21/ba
141 R d1/a
142 R d1/b
143 R d1/ba
144 R d1/d11/a1
145 # move --after some files under d1 to d2/d21 (glob)
146 copying d1/a to d2/d21/a
147 copying d1/d11/a1 to d2/d21/a1
148 removing d1/a
149 removing d1/d11/a1
150 A d2/d21/a
151 A d2/d21/a1
152 R d1/a
153 R d1/d11/a1
154 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
155 copying d1/a to d2/d21/a
156 copying d1/d11/a1 to d2/d21/a1
157 removing d1/a
158 removing d1/d11/a1
159 A d2/d21/a
160 A d2/d21/a1
161 R d1/a
162 R d1/d11/a1
163 # attempt to overwrite an existing file
164 d1/ca: not overwriting - file exists
165 ? d1/ca
166 # forced overwrite of an existing file
167 A d1/ca
168 R d1/ba
169 # replace a symlink with a file
170 A d1/ca
171 R d1/ba
172 # do not copy more than one source file to the same destination file
173 copying d1/d11/a1 to d3/d11/a1
174 d3/b: not overwriting - d2/b collides with d1/b
175 removing d1/d11/a1
176 A d3/a
177 A d3/b
178 A d3/ba
179 A d3/d11/a1
180 R d1/a
181 R d1/b
182 R d1/ba
183 R d1/d11/a1
@@ -2,18 +2,25 b' shopt -s extglob'
2
2
3 _hg_commands()
3 _hg_commands()
4 {
4 {
5 local commands="$(hg -v help | sed -e '1,/^list of commands:/d' \
5 local all commands result
6 -e '/^global options:/,$d' \
6
7 -e '/^ [^ ]/!d; s/[,:]//g;')"
7 all=($(hg --debug help | sed -e '1,/^list of commands:/d' \
8 -e '/^global options:/,$d' \
9 -e '/^ [^ ]/!d; s/^ //; s/[,:]//g;'))
10
11 commands="${all[*]##debug*}"
12 result=$(compgen -W "${commands[*]}" -- "$cur")
8
13
9 # hide debug commands from users, but complete them if
14 # hide debug commands from users, but complete them if
10 # specifically asked for
15 # there is no other possible command
11 if [[ "$cur" == de* ]]; then
16 if [ "$result" = "" ]; then
12 commands="$commands debugcheckstate debugstate debugindex"
17 local debug
13 commands="$commands debugindexdot debugwalk debugdata"
18 debug=(${all[*]##!(debug*)})
14 commands="$commands debugancestor debugconfig debugrename"
19 debug="${debug[*]/g/debug}"
20 result=$(compgen -W "$debug" -- "$cur")
15 fi
21 fi
16 COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -W "$commands" -- "$cur") )
22
23 COMPREPLY=(${COMPREPLY[@]:-} $result)
17 }
24 }
18
25
19 _hg_paths()
26 _hg_paths()
@@ -161,7 +168,7 b' shopt -s extglob'
161 fi
168 fi
162 ;;
169 ;;
163 *)
170 *)
164 COMPREPLY=(${COMPREPLY[@]:-} $( compgen -f -- "$cur" ))
171 COMPREPLY=(${COMPREPLY[@]:-} $( compgen -f -- "$cur" ))
165 ;;
172 ;;
166 esac
173 esac
167
174
@@ -26,7 +26,7 b' def check_clean(ui, repo):'
26 ui.warn("Repository is not clean, please commit or revert\n")
26 ui.warn("Repository is not clean, please commit or revert\n")
27 sys.exit(1)
27 sys.exit(1)
28
28
29 class bisect:
29 class bisect(object):
30 """dichotomic search in the DAG of changesets"""
30 """dichotomic search in the DAG of changesets"""
31 def __init__(self, ui, repo):
31 def __init__(self, ui, repo):
32 self.repo = repo
32 self.repo = repo
@@ -116,7 +116,7 b' case $service in'
116 '*:file:_files'
116 '*:file:_files'
117 ;;
117 ;;
118
118
119 (status)
119 (status|st)
120 _arguments $includeExclude \
120 _arguments $includeExclude \
121 '(--no-status)-n[hide status prefix]' \
121 '(--no-status)-n[hide status prefix]' \
122 '(-n)--no-status[hide status prefix]' \
122 '(-n)--no-status[hide status prefix]' \
@@ -87,7 +87,7 b' addremove [options] [files ...]::'
87 New files are ignored if they match any of the patterns in .hgignore. As
87 New files are ignored if they match any of the patterns in .hgignore. As
88 with add, these changes take effect at the next commit.
88 with add, these changes take effect at the next commit.
89
89
90 annotate [-r <rev> -u -n -c] [files ...]::
90 annotate [-r <rev> -u -n -c -d] [files ...]::
91 List changes in files, showing the revision id responsible for each line
91 List changes in files, showing the revision id responsible for each line
92
92
93 This command is useful to discover who did a change or when a change took
93 This command is useful to discover who did a change or when a change took
@@ -103,6 +103,7 b' annotate [-r <rev> -u -n -c] [files ...]'
103 -X, --exclude <pat> exclude names matching the given patterns
103 -X, --exclude <pat> exclude names matching the given patterns
104 -r, --revision <rev> annotate the specified revision
104 -r, --revision <rev> annotate the specified revision
105 -u, --user list the author
105 -u, --user list the author
106 -d, --date list the commit date
106 -c, --changeset list the changeset
107 -c, --changeset list the changeset
107 -n, --number list the revision number (default)
108 -n, --number list the revision number (default)
108
109
@@ -147,7 +147,7 b' static int equatelines(struct line *a, i'
147 break;
147 break;
148
148
149 a[i].e = j; /* use equivalence class for quick compare */
149 a[i].e = j; /* use equivalence class for quick compare */
150 if(h[j].len <= t)
150 if (h[j].len <= t)
151 a[i].n = h[j].pos; /* point to head of match list */
151 a[i].n = h[j].pos; /* point to head of match list */
152 else
152 else
153 a[i].n = -1; /* too popular */
153 a[i].n = -1; /* too popular */
@@ -270,7 +270,7 b' static PyObject *blocks(PyObject *self, '
270 if (!l.head || !rl)
270 if (!l.head || !rl)
271 goto nomem;
271 goto nomem;
272
272
273 for(h = l.base; h != l.head; h++) {
273 for (h = l.base; h != l.head; h++) {
274 m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
274 m = Py_BuildValue("iiii", h->a1, h->a2, h->b1, h->b2);
275 PyList_SetItem(rl, pos, m);
275 PyList_SetItem(rl, pos, m);
276 pos++;
276 pos++;
@@ -305,7 +305,7 b' static PyObject *bdiff(PyObject *self, P'
305 goto nomem;
305 goto nomem;
306
306
307 /* calculate length of output */
307 /* calculate length of output */
308 for(h = l.base; h != l.head; h++) {
308 for (h = l.base; h != l.head; h++) {
309 if (h->a1 != la || h->b1 != lb)
309 if (h->a1 != la || h->b1 != lb)
310 len += 12 + bl[h->b1].l - bl[lb].l;
310 len += 12 + bl[h->b1].l - bl[lb].l;
311 la = h->a2;
311 la = h->a2;
@@ -320,7 +320,7 b' static PyObject *bdiff(PyObject *self, P'
320 rb = PyString_AsString(result);
320 rb = PyString_AsString(result);
321 la = lb = 0;
321 la = lb = 0;
322
322
323 for(h = l.base; h != l.head; h++) {
323 for (h = l.base; h != l.head; h++) {
324 if (h->a1 != la || h->b1 != lb) {
324 if (h->a1 != la || h->b1 != lb) {
325 len = bl[h->b1].l - bl[lb].l;
325 len = bl[h->b1].l - bl[lb].l;
326 *(uint32_t *)(encode) = htonl(al[la].l - al->l);
326 *(uint32_t *)(encode) = htonl(al[la].l - al->l);
@@ -353,3 +353,4 b' PyMODINIT_FUNC initbdiff(void)'
353 {
353 {
354 Py_InitModule3("bdiff", methods, mdiff_doc);
354 Py_InitModule3("bdiff", methods, mdiff_doc);
355 }
355 }
356
@@ -15,6 +15,8 b' demandload(globals(), "errno socket vers'
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
18
20
19 def filterfiles(filters, files):
21 def filterfiles(filters, files):
20 l = [x for x in files if x in filters]
22 l = [x for x in files if x in filters]
@@ -31,25 +33,29 b' def relpath(repo, args):'
31 return [util.normpath(os.path.join(cwd, x)) for x in args]
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 return args
34 return args
33
35
34 def matchpats(repo, cwd, pats=[], opts={}, head=''):
36 def matchpats(repo, pats=[], opts={}, head=''):
37 cwd = repo.getcwd()
38 if not pats and cwd:
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 cwd = ''
35 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
36 opts.get('exclude'), head)
43 opts.get('exclude'), head) + (cwd,)
37
44
38 def makewalk(repo, pats, opts, head=''):
45 def makewalk(repo, pats, opts, node=None, head=''):
39 cwd = repo.getcwd()
46 files, matchfn, anypats, cwd = matchpats(repo, pats, opts, head)
40 files, matchfn, anypats = matchpats(repo, cwd, pats, opts, head)
41 exact = dict(zip(files, files))
47 exact = dict(zip(files, files))
42 def walk():
48 def walk():
43 for src, fn in repo.walk(files=files, match=matchfn):
49 for src, fn in repo.walk(node=node, files=files, match=matchfn):
44 yield src, fn, util.pathto(cwd, fn), fn in exact
50 yield src, fn, util.pathto(cwd, fn), fn in exact
45 return files, matchfn, walk()
51 return files, matchfn, walk()
46
52
47 def walk(repo, pats, opts, head=''):
53 def walk(repo, pats, opts, node=None, head=''):
48 files, matchfn, results = makewalk(repo, pats, opts, head)
54 files, matchfn, results = makewalk(repo, pats, opts, node, head)
49 for r in results:
55 for r in results:
50 yield r
56 yield r
51
57
52 def walkchangerevs(ui, repo, cwd, pats, opts):
58 def walkchangerevs(ui, repo, pats, opts):
53 '''Iterate over files and the revs they changed in.
59 '''Iterate over files and the revs they changed in.
54
60
55 Callers most commonly need to iterate backwards over the history
61 Callers most commonly need to iterate backwards over the history
@@ -79,12 +85,7 b' def walkchangerevs(ui, repo, cwd, pats, '
79 if repo.changelog.count() == 0:
85 if repo.changelog.count() == 0:
80 return [], False
86 return [], False
81
87
82 cwd = repo.getcwd()
88 files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
83 if not pats and cwd:
84 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
85 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
86 files, matchfn, anypats = matchpats(repo, (pats and cwd) or '',
87 pats, opts)
88 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
89 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
89 wanted = {}
90 wanted = {}
90 slowpath = anypats
91 slowpath = anypats
@@ -387,7 +388,7 b' def help_(ui, cmd=None, with_version=Fal'
387 if with_version:
388 if with_version:
388 show_version(ui)
389 show_version(ui)
389 ui.write('\n')
390 ui.write('\n')
390 key, i = find(cmd)
391 aliases, i = find(cmd)
391 # synopsis
392 # synopsis
392 ui.write("%s\n\n" % i[2])
393 ui.write("%s\n\n" % i[2])
393
394
@@ -399,9 +400,8 b' def help_(ui, cmd=None, with_version=Fal'
399
400
400 if not ui.quiet:
401 if not ui.quiet:
401 # aliases
402 # aliases
402 aliases = ', '.join(key.split('|')[1:])
403 if len(aliases) > 1:
403 if aliases:
404 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
404 ui.write(_("\naliases: %s\n") % aliases)
405
405
406 # options
406 # options
407 if i[1]:
407 if i[1]:
@@ -482,8 +482,7 b' def add(ui, repo, *pats, **opts):'
482
482
483 The files will be added to the repository at the next commit.
483 The files will be added to the repository at the next commit.
484
484
485 If no names are given, add all files in the current directory and
485 If no names are given, add all files in the repository.
486 its subdirectories.
487 """
486 """
488
487
489 names = []
488 names = []
@@ -537,11 +536,20 b' def annotate(ui, repo, *pats, **opts):'
537 cl = repo.changelog.read(repo.changelog.node(rev))
536 cl = repo.changelog.read(repo.changelog.node(rev))
538 return trimuser(ui, cl[1], rev, ucache)
537 return trimuser(ui, cl[1], rev, ucache)
539
538
539 dcache = {}
540 def getdate(rev):
541 datestr = dcache.get(rev)
542 if datestr is None:
543 cl = repo.changelog.read(repo.changelog.node(rev))
544 datestr = dcache[rev] = util.datestr(cl[2])
545 return datestr
546
540 if not pats:
547 if not pats:
541 raise util.Abort(_('at least one file name or pattern required'))
548 raise util.Abort(_('at least one file name or pattern required'))
542
549
543 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
550 opmap = [['user', getname], ['number', str], ['changeset', getnode],
544 if not opts['user'] and not opts['changeset']:
551 ['date', getdate]]
552 if not opts['user'] and not opts['changeset'] and not opts['date']:
545 opts['number'] = 1
553 opts['number'] = 1
546
554
547 if opts['rev']:
555 if opts['rev']:
@@ -624,21 +632,16 b' def cat(ui, repo, file1, *pats, **opts):'
624 %p root-relative path name of file being printed
632 %p root-relative path name of file being printed
625 """
633 """
626 mf = {}
634 mf = {}
627 if opts['rev']:
635 rev = opts['rev']
628 change = repo.changelog.read(repo.lookup(opts['rev']))
636 if rev:
629 mf = repo.manifest.read(change[0])
637 node = repo.lookup(rev)
630 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
638 else:
639 node = repo.changelog.tip()
640 change = repo.changelog.read(node)
641 mf = repo.manifest.read(change[0])
642 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
631 r = repo.file(abs)
643 r = repo.file(abs)
632 if opts['rev']:
644 n = mf[abs]
633 try:
634 n = mf[abs]
635 except (hg.RepoError, KeyError):
636 try:
637 n = r.lookup(rev)
638 except KeyError, inst:
639 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
640 else:
641 n = r.tip()
642 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
645 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
643 fp.write(r.read(n))
646 fp.write(r.read(n))
644
647
@@ -667,7 +670,7 b' def clone(ui, source, dest=None, **opts)'
667
670
668 dest = os.path.realpath(dest)
671 dest = os.path.realpath(dest)
669
672
670 class Dircleanup:
673 class Dircleanup(object):
671 def __init__(self, dir_):
674 def __init__(self, dir_):
672 self.rmtree = shutil.rmtree
675 self.rmtree = shutil.rmtree
673 self.dir_ = dir_
676 self.dir_ = dir_
@@ -735,6 +738,7 b' def clone(ui, source, dest=None, **opts)'
735 f = repo.opener("hgrc", "w", text=True)
738 f = repo.opener("hgrc", "w", text=True)
736 f.write("[paths]\n")
739 f.write("[paths]\n")
737 f.write("default = %s\n" % abspath)
740 f.write("default = %s\n" % abspath)
741 f.close()
738
742
739 if not opts['noupdate']:
743 if not opts['noupdate']:
740 update(ui, repo)
744 update(ui, repo)
@@ -747,7 +751,7 b' def commit(ui, repo, *pats, **opts):'
747 Commit changes to the given files into the repository.
751 Commit changes to the given files into the repository.
748
752
749 If a list of files is omitted, all changes reported by "hg status"
753 If a list of files is omitted, all changes reported by "hg status"
750 from the root of the repository will be commited.
754 will be commited.
751
755
752 The HGEDITOR or EDITOR environment variables are used to start an
756 The HGEDITOR or EDITOR environment variables are used to start an
753 editor to add a commit comment.
757 editor to add a commit comment.
@@ -770,12 +774,7 b' def commit(ui, repo, *pats, **opts):'
770
774
771 if opts['addremove']:
775 if opts['addremove']:
772 addremove(ui, repo, *pats, **opts)
776 addremove(ui, repo, *pats, **opts)
773 cwd = repo.getcwd()
777 fns, match, anypats, cwd = matchpats(repo, pats, opts)
774 if not pats and cwd:
775 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
776 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
777 fns, match, anypats = matchpats(repo, (pats and repo.getcwd()) or '',
778 pats, opts)
779 if pats:
778 if pats:
780 c, a, d, u = repo.changes(files=fns, match=match)
779 c, a, d, u = repo.changes(files=fns, match=match)
781 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
780 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
@@ -787,14 +786,10 b' def commit(ui, repo, *pats, **opts):'
787 raise util.Abort(str(inst))
786 raise util.Abort(str(inst))
788
787
789 def docopy(ui, repo, pats, opts):
788 def docopy(ui, repo, pats, opts):
790 if not pats:
789 cwd = repo.getcwd()
791 raise util.Abort(_('no source or destination specified'))
790 errors = 0
792 elif len(pats) == 1:
791 copied = []
793 raise util.Abort(_('no destination specified'))
792 targets = {}
794 pats = list(pats)
795 dest = pats.pop()
796 sources = []
797 dir2dir = len(pats) == 1 and os.path.isdir(pats[0])
798
793
799 def okaytocopy(abs, rel, exact):
794 def okaytocopy(abs, rel, exact):
800 reasons = {'?': _('is not managed'),
795 reasons = {'?': _('is not managed'),
@@ -805,74 +800,133 b' def docopy(ui, repo, pats, opts):'
805 else:
800 else:
806 return True
801 return True
807
802
808 for src, abs, rel, exact in walk(repo, pats, opts):
803 def copy(abssrc, relsrc, target, exact):
809 if okaytocopy(abs, rel, exact):
804 abstarget = util.canonpath(repo.root, cwd, target)
810 sources.append((abs, rel, exact))
805 reltarget = util.pathto(cwd, abstarget)
811 if not sources:
806 prevsrc = targets.get(abstarget)
812 raise util.Abort(_('no files to copy'))
807 if prevsrc is not None:
813
808 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
814 cwd = repo.getcwd()
809 (reltarget, abssrc, prevsrc))
815 absdest = util.canonpath(repo.root, cwd, dest)
810 return
816 reldest = util.pathto(cwd, absdest)
811 if (not opts['after'] and os.path.exists(reltarget) or
817 if os.path.exists(reldest):
812 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
818 destisfile = not os.path.isdir(reldest)
813 if not opts['force']:
819 else:
814 ui.warn(_('%s: not overwriting - file exists\n') %
820 destisfile = not dir2dir and (len(sources) == 1
815 reltarget)
821 or repo.dirstate.state(absdest) != '?')
816 return
822
817 if not opts['after']:
823 if destisfile and len(sources) > 1:
818 os.unlink(reltarget)
824 raise util.Abort(_('with multiple sources, destination must be a '
819 if opts['after']:
825 'directory'))
820 if not os.path.exists(reltarget):
826
821 return
827 srcpfxlen = 0
828 if dir2dir:
829 srcpfx = util.pathto(cwd, util.canonpath(repo.root, cwd, pats[0]))
830 if os.path.exists(reldest):
831 srcpfx = os.path.split(srcpfx)[0]
832 if srcpfx:
833 srcpfx += os.sep
834 srcpfxlen = len(srcpfx)
835
836 errs, copied = 0, []
837 for abs, rel, exact in sources:
838 if destisfile:
839 mydest = reldest
840 elif dir2dir:
841 mydest = os.path.join(dest, rel[srcpfxlen:])
842 else:
822 else:
843 mydest = os.path.join(dest, os.path.basename(rel))
823 targetdir = os.path.dirname(reltarget) or '.'
844 myabsdest = util.canonpath(repo.root, cwd, mydest)
824 if not os.path.isdir(targetdir):
845 myreldest = util.pathto(cwd, myabsdest)
825 os.makedirs(targetdir)
846 if not opts['force'] and repo.dirstate.state(myabsdest) not in 'a?':
847 ui.warn(_('%s: not overwriting - file already managed\n') % myreldest)
848 continue
849 mydestdir = os.path.dirname(myreldest) or '.'
850 if not opts['after']:
851 try:
826 try:
852 if dir2dir: os.makedirs(mydestdir)
827 shutil.copyfile(relsrc, reltarget)
853 elif not destisfile: os.mkdir(mydestdir)
828 shutil.copymode(relsrc, reltarget)
854 except OSError, inst:
855 if inst.errno != errno.EEXIST: raise
856 if ui.verbose or not exact:
857 ui.status(_('copying %s to %s\n') % (rel, myreldest))
858 if not opts['after']:
859 try:
860 shutil.copyfile(rel, myreldest)
861 shutil.copymode(rel, myreldest)
862 except shutil.Error, inst:
829 except shutil.Error, inst:
863 raise util.Abort(str(inst))
830 raise util.Abort(str(inst))
864 except IOError, inst:
831 except IOError, inst:
865 if inst.errno == errno.ENOENT:
832 if inst.errno == errno.ENOENT:
866 ui.warn(_('%s: deleted in working copy\n') % rel)
833 ui.warn(_('%s: deleted in working copy\n') % relsrc)
867 else:
834 else:
868 ui.warn(_('%s: cannot copy - %s\n') % (rel, inst.strerror))
835 ui.warn(_('%s: cannot copy - %s\n') %
869 errs += 1
836 (relsrc, inst.strerror))
870 continue
837 errors += 1
871 repo.copy(abs, myabsdest)
838 return
872 copied.append((abs, rel, exact))
839 if ui.verbose or not exact:
873 if errs:
840 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
841 targets[abstarget] = abssrc
842 repo.copy(abssrc, abstarget)
843 copied.append((abssrc, relsrc, exact))
844
845 def targetpathfn(pat, dest, srcs):
846 if os.path.isdir(pat):
847 if pat.endswith(os.sep):
848 pat = pat[:-len(os.sep)]
849 if destdirexists:
850 striplen = len(os.path.split(pat)[0])
851 else:
852 striplen = len(pat)
853 if striplen:
854 striplen += len(os.sep)
855 res = lambda p: os.path.join(dest, p[striplen:])
856 elif destdirexists:
857 res = lambda p: os.path.join(dest, os.path.basename(p))
858 else:
859 res = lambda p: dest
860 return res
861
862 def targetpathafterfn(pat, dest, srcs):
863 if util.patkind(pat, None)[0]:
864 # a mercurial pattern
865 res = lambda p: os.path.join(dest, os.path.basename(p))
866 elif len(util.canonpath(repo.root, cwd, pat)) < len(srcs[0][0]):
867 # A directory. Either the target path contains the last
868 # component of the source path or it does not.
869 def evalpath(striplen):
870 score = 0
871 for s in srcs:
872 t = os.path.join(dest, s[1][striplen:])
873 if os.path.exists(t):
874 score += 1
875 return score
876
877 if pat.endswith(os.sep):
878 pat = pat[:-len(os.sep)]
879 striplen = len(pat) + len(os.sep)
880 if os.path.isdir(os.path.join(dest, os.path.split(pat)[1])):
881 score = evalpath(striplen)
882 striplen1 = len(os.path.split(pat)[0])
883 if striplen1:
884 striplen1 += len(os.sep)
885 if evalpath(striplen1) > score:
886 striplen = striplen1
887 res = lambda p: os.path.join(dest, p[striplen:])
888 else:
889 # a file
890 if destdirexists:
891 res = lambda p: os.path.join(dest, os.path.basename(p))
892 else:
893 res = lambda p: dest
894 return res
895
896
897 pats = list(pats)
898 if not pats:
899 raise util.Abort(_('no source or destination specified'))
900 if len(pats) == 1:
901 raise util.Abort(_('no destination specified'))
902 dest = pats.pop()
903 destdirexists = os.path.isdir(dest)
904 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
905 raise util.Abort(_('with multiple sources, destination must be an '
906 'existing directory'))
907 if opts['after']:
908 tfn = targetpathafterfn
909 else:
910 tfn = targetpathfn
911 copylist = []
912 for pat in pats:
913 srcs = []
914 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
915 if okaytocopy(abssrc, relsrc, exact):
916 srcs.append((abssrc, relsrc, exact))
917 if not srcs:
918 continue
919 copylist.append((tfn(pat, dest, srcs), srcs))
920 if not copylist:
921 raise util.Abort(_('no files to copy'))
922
923 for targetpath, srcs in copylist:
924 for abssrc, relsrc, exact in srcs:
925 copy(abssrc, relsrc, targetpath(relsrc), exact)
926
927 if errors:
874 ui.warn(_('(consider using --after)\n'))
928 ui.warn(_('(consider using --after)\n'))
875 return errs, copied
929 return errors, copied
876
930
877 def copy(ui, repo, *pats, **opts):
931 def copy(ui, repo, *pats, **opts):
878 """mark files as copied for the next commit
932 """mark files as copied for the next commit
@@ -1007,7 +1061,7 b' def debugrename(ui, repo, file, rev=None'
1007 change = repo.changelog.read(n)
1061 change = repo.changelog.read(n)
1008 m = repo.manifest.read(change[0])
1062 m = repo.manifest.read(change[0])
1009 n = m[relpath(repo, [file])[0]]
1063 n = m[relpath(repo, [file])[0]]
1010 except hg.RepoError, KeyError:
1064 except (hg.RepoError, KeyError):
1011 n = r.lookup(rev)
1065 n = r.lookup(rev)
1012 else:
1066 else:
1013 n = r.tip()
1067 n = r.tip()
@@ -1030,7 +1084,7 b' def debugwalk(ui, repo, *pats, **opts):'
1030 ui.write("%s\n" % line.rstrip())
1084 ui.write("%s\n" % line.rstrip())
1031
1085
1032 def diff(ui, repo, *pats, **opts):
1086 def diff(ui, repo, *pats, **opts):
1033 """diff working directory (or selected files)
1087 """diff repository (or selected files)
1034
1088
1035 Show differences between revisions for the specified files.
1089 Show differences between revisions for the specified files.
1036
1090
@@ -1056,7 +1110,7 b' def diff(ui, repo, *pats, **opts):'
1056 if len(revs) > 2:
1110 if len(revs) > 2:
1057 raise util.Abort(_("too many revisions to diff"))
1111 raise util.Abort(_("too many revisions to diff"))
1058
1112
1059 fns, matchfn, anypats = matchpats(repo, repo.getcwd(), pats, opts)
1113 fns, matchfn, anypats, cwd = matchpats(repo, pats, opts)
1060
1114
1061 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1115 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1062 text=opts['text'])
1116 text=opts['text'])
@@ -1177,7 +1231,7 b' def grep(ui, repo, pattern, *pats, **opt'
1177 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1231 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1178 begin = lend + 1
1232 begin = lend + 1
1179
1233
1180 class linestate:
1234 class linestate(object):
1181 def __init__(self, line, linenum, colstart, colend):
1235 def __init__(self, line, linenum, colstart, colend):
1182 self.line = line
1236 self.line = line
1183 self.linenum = linenum
1237 self.linenum = linenum
@@ -1227,7 +1281,7 b' def grep(ui, repo, pattern, *pats, **opt'
1227
1281
1228 fstate = {}
1282 fstate = {}
1229 skip = {}
1283 skip = {}
1230 changeiter, getchange = walkchangerevs(ui, repo, repo.getcwd(), pats, opts)
1284 changeiter, getchange = walkchangerevs(ui, repo, pats, opts)
1231 count = 0
1285 count = 0
1232 incrementing = False
1286 incrementing = False
1233 for st, rev, fns in changeiter:
1287 for st, rev, fns in changeiter:
@@ -1275,11 +1329,14 b' def heads(ui, repo, **opts):'
1275 changesets. They are where development generally takes place and
1329 changesets. They are where development generally takes place and
1276 are the usual targets for update and merge operations.
1330 are the usual targets for update and merge operations.
1277 """
1331 """
1278 heads = repo.changelog.heads()
1332 if opts['rev']:
1333 heads = repo.heads(repo.lookup(opts['rev']))
1334 else:
1335 heads = repo.heads()
1279 br = None
1336 br = None
1280 if opts['branches']:
1337 if opts['branches']:
1281 br = repo.branchlookup(heads)
1338 br = repo.branchlookup(heads)
1282 for n in repo.changelog.heads():
1339 for n in heads:
1283 show_changeset(ui, repo, changenode=n, brinfo=br)
1340 show_changeset(ui, repo, changenode=n, brinfo=br)
1284
1341
1285 def identify(ui, repo):
1342 def identify(ui, repo):
@@ -1461,11 +1518,11 b' def log(ui, repo, *pats, **opts):'
1461 Print the revision history of the specified files or the entire project.
1518 Print the revision history of the specified files or the entire project.
1462
1519
1463 By default this command outputs: changeset id and hash, tags,
1520 By default this command outputs: changeset id and hash, tags,
1464 parents, user, date and time, and a summary for each commit. The
1521 non-trivial parents, user, date and time, and a summary for each
1465 -v switch adds some more detail, such as changed files, manifest
1522 commit. When the -v/--verbose switch is used, the list of changed
1466 hashes or message signatures.
1523 files and full commit message is shown.
1467 """
1524 """
1468 class dui:
1525 class dui(object):
1469 # Implement and delegate some ui protocol. Save hunks of
1526 # Implement and delegate some ui protocol. Save hunks of
1470 # output for later display in the desired order.
1527 # output for later display in the desired order.
1471 def __init__(self, ui):
1528 def __init__(self, ui):
@@ -1487,12 +1544,7 b' def log(ui, repo, *pats, **opts):'
1487 self.write(*args)
1544 self.write(*args)
1488 def __getattr__(self, key):
1545 def __getattr__(self, key):
1489 return getattr(self.ui, key)
1546 return getattr(self.ui, key)
1490 cwd = repo.getcwd()
1547 changeiter, getchange = walkchangerevs(ui, repo, pats, opts)
1491 if not pats and cwd:
1492 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
1493 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
1494 changeiter, getchange = walkchangerevs(ui, repo, (pats and cwd) or '',
1495 pats, opts)
1496 for st, rev, fns in changeiter:
1548 for st, rev, fns in changeiter:
1497 if st == 'window':
1549 if st == 'window':
1498 du = dui(ui)
1550 du = dui(ui)
@@ -1733,7 +1785,9 b' def recover(ui, repo):'
1733 This command tries to fix the repository status after an interrupted
1785 This command tries to fix the repository status after an interrupted
1734 operation. It should only be necessary when Mercurial suggests it.
1786 operation. It should only be necessary when Mercurial suggests it.
1735 """
1787 """
1736 repo.recover()
1788 if repo.recover():
1789 return repo.verify()
1790 return False
1737
1791
1738 def remove(ui, repo, pat, *pats, **opts):
1792 def remove(ui, repo, pat, *pats, **opts):
1739 """remove the specified files on the next commit
1793 """remove the specified files on the next commit
@@ -1799,13 +1853,12 b' def revert(ui, repo, *pats, **opts):'
1799
1853
1800 If names are given, all files matching the names are reverted.
1854 If names are given, all files matching the names are reverted.
1801
1855
1802 If no names are given, all files in the current directory and
1856 If no arguments are given, all files in the repository are reverted.
1803 its subdirectories are reverted.
1804 """
1857 """
1805 node = opts['rev'] and repo.lookup(opts['rev']) or \
1858 node = opts['rev'] and repo.lookup(opts['rev']) or \
1806 repo.dirstate.parents()[0]
1859 repo.dirstate.parents()[0]
1807
1860
1808 files, choose, anypats = matchpats(repo, repo.getcwd(), pats, opts)
1861 files, choose, anypats, cwd = matchpats(repo, pats, opts)
1809 (c, a, d, u) = repo.changes(match=choose)
1862 (c, a, d, u) = repo.changes(match=choose)
1810 repo.forget(a)
1863 repo.forget(a)
1811 repo.undelete(d)
1864 repo.undelete(d)
@@ -1928,9 +1981,8 b' def serve(ui, repo, **opts):'
1928 def status(ui, repo, *pats, **opts):
1981 def status(ui, repo, *pats, **opts):
1929 """show changed files in the working directory
1982 """show changed files in the working directory
1930
1983
1931 Show changed files in the working directory. If no names are
1984 Show changed files in the repository. If names are
1932 given, all files are shown. Otherwise, only files matching the
1985 given, only files that match are shown.
1933 given names are shown.
1934
1986
1935 The codes used to show the status of files are:
1987 The codes used to show the status of files are:
1936 M = modified
1988 M = modified
@@ -1939,8 +1991,7 b' def status(ui, repo, *pats, **opts):'
1939 ? = not tracked
1991 ? = not tracked
1940 """
1992 """
1941
1993
1942 cwd = repo.getcwd()
1994 files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
1943 files, matchfn, anypats = matchpats(repo, cwd, pats, opts)
1944 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1995 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1945 for n in repo.changes(files=files, match=matchfn)]
1996 for n in repo.changes(files=files, match=matchfn)]
1946
1997
@@ -1986,8 +2037,10 b' def tag(ui, repo, name, rev=None, **opts'
1986 else:
2037 else:
1987 r = hex(repo.changelog.tip())
2038 r = hex(repo.changelog.tip())
1988
2039
1989 if name.find(revrangesep) >= 0:
2040 disallowed = (revrangesep, '\r', '\n')
1990 raise util.Abort(_("'%s' cannot be used in a tag name") % revrangesep)
2041 for c in disallowed:
2042 if name.find(c) >= 0:
2043 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
1991
2044
1992 if opts['local']:
2045 if opts['local']:
1993 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2046 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
@@ -2138,6 +2191,7 b' table = {'
2138 [('r', 'rev', '', _('annotate the specified revision')),
2191 [('r', 'rev', '', _('annotate the specified revision')),
2139 ('a', 'text', None, _('treat all files as text')),
2192 ('a', 'text', None, _('treat all files as text')),
2140 ('u', 'user', None, _('list the author')),
2193 ('u', 'user', None, _('list the author')),
2194 ('d', 'date', None, _('list the date')),
2141 ('n', 'number', None, _('list the revision number (default)')),
2195 ('n', 'number', None, _('list the revision number (default)')),
2142 ('c', 'changeset', None, _('list the changeset')),
2196 ('c', 'changeset', None, _('list the changeset')),
2143 ('I', 'include', [], _('include names matching the given patterns')),
2197 ('I', 'include', [], _('include names matching the given patterns')),
@@ -2223,8 +2277,9 b' table = {'
2223 "hg grep [OPTION]... PATTERN [FILE]..."),
2277 "hg grep [OPTION]... PATTERN [FILE]..."),
2224 "heads":
2278 "heads":
2225 (heads,
2279 (heads,
2226 [('b', 'branches', None, _('find branch info'))],
2280 [('b', 'branches', None, _('find branch info')),
2227 _('hg heads [-b]')),
2281 ('r', 'rev', "", _('show only heads which are descendants of rev'))],
2282 _('hg heads [-b] [-r <rev>]')),
2228 "help": (help_, [], _('hg help [COMMAND]')),
2283 "help": (help_, [], _('hg help [COMMAND]')),
2229 "identify|id": (identify, [], _('hg identify')),
2284 "identify|id": (identify, [], _('hg identify')),
2230 "import|patch":
2285 "import|patch":
@@ -2374,17 +2429,21 b' norepo = ("clone init version help debug'
2374 " debugindex debugindexdot paths")
2429 " debugindex debugindexdot paths")
2375
2430
2376 def find(cmd):
2431 def find(cmd):
2377 choice = []
2432 """Return (aliases, command table entry) for command string."""
2433 choice = None
2378 for e in table.keys():
2434 for e in table.keys():
2379 aliases = e.lstrip("^").split("|")
2435 aliases = e.lstrip("^").split("|")
2380 if cmd in aliases:
2436 if cmd in aliases:
2381 return e, table[e]
2437 return aliases, table[e]
2382 for a in aliases:
2438 for a in aliases:
2383 if a.startswith(cmd):
2439 if a.startswith(cmd):
2384 choice.append(e)
2440 if choice:
2385 if len(choice) == 1:
2441 raise AmbiguousCommand(cmd)
2386 e = choice[0]
2442 else:
2387 return e, table[e]
2443 choice = aliases, table[e]
2444 break
2445 if choice:
2446 return choice
2388
2447
2389 raise UnknownCommand(cmd)
2448 raise UnknownCommand(cmd)
2390
2449
@@ -2411,18 +2470,11 b' def parse(ui, args):'
2411
2470
2412 if args:
2471 if args:
2413 cmd, args = args[0], args[1:]
2472 cmd, args = args[0], args[1:]
2473 aliases, i = find(cmd)
2474 cmd = aliases[0]
2414 defaults = ui.config("defaults", cmd)
2475 defaults = ui.config("defaults", cmd)
2415 if defaults:
2476 if defaults:
2416 # reparse with command defaults added
2477 args = defaults.split() + args
2417 args = [cmd] + defaults.split() + args
2418 try:
2419 args = fancyopts.fancyopts(args, globalopts, options)
2420 except fancyopts.getopt.GetoptError, inst:
2421 raise ParseError(None, inst)
2422
2423 cmd, args = args[0], args[1:]
2424
2425 i = find(cmd)[1]
2426 c = list(i[1])
2478 c = list(i[1])
2427 else:
2479 else:
2428 cmd = None
2480 cmd = None
@@ -2460,7 +2512,7 b' def dispatch(args):'
2460
2512
2461 external = []
2513 external = []
2462 for x in u.extensions():
2514 for x in u.extensions():
2463 def on_exception(Exception, inst):
2515 def on_exception(exc, inst):
2464 u.warn(_("*** failed to import extension %s\n") % x[1])
2516 u.warn(_("*** failed to import extension %s\n") % x[1])
2465 u.warn("%s\n" % inst)
2517 u.warn("%s\n" % inst)
2466 if "--traceback" in sys.argv[1:]:
2518 if "--traceback" in sys.argv[1:]:
@@ -2502,6 +2554,9 b' def dispatch(args):'
2502 u.warn(_("hg: %s\n") % inst.args[1])
2554 u.warn(_("hg: %s\n") % inst.args[1])
2503 help_(u, 'shortlist')
2555 help_(u, 'shortlist')
2504 sys.exit(-1)
2556 sys.exit(-1)
2557 except AmbiguousCommand, inst:
2558 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2559 sys.exit(1)
2505 except UnknownCommand, inst:
2560 except UnknownCommand, inst:
2506 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2561 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2507 help_(u, 'shortlist')
2562 help_(u, 'shortlist')
@@ -2620,6 +2675,9 b' def dispatch(args):'
2620 u.debug(inst, "\n")
2675 u.debug(inst, "\n")
2621 u.warn(_("%s: invalid arguments\n") % cmd)
2676 u.warn(_("%s: invalid arguments\n") % cmd)
2622 help_(u, cmd)
2677 help_(u, cmd)
2678 except AmbiguousCommand, inst:
2679 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2680 help_(u, 'shortlist')
2623 except UnknownCommand, inst:
2681 except UnknownCommand, inst:
2624 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2682 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2625 help_(u, 'shortlist')
2683 help_(u, 'shortlist')
@@ -2629,6 +2687,8 b' def dispatch(args):'
2629 except:
2687 except:
2630 u.warn(_("** unknown exception encountered, details follow\n"))
2688 u.warn(_("** unknown exception encountered, details follow\n"))
2631 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2689 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2690 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2691 % version.get_version())
2632 raise
2692 raise
2633
2693
2634 sys.exit(-1)
2694 sys.exit(-1)
@@ -13,7 +13,7 b' from i18n import gettext as _'
13 from demandload import *
13 from demandload import *
14 demandload(globals(), "time bisect stat util re errno")
14 demandload(globals(), "time bisect stat util re errno")
15
15
16 class dirstate:
16 class dirstate(object):
17 def __init__(self, opener, ui, root):
17 def __init__(self, opener, ui, root):
18 self.opener = opener
18 self.opener = opener
19 self.root = root
19 self.root = root
@@ -101,16 +101,15 b' class dirstate:'
101 try:
101 try:
102 return self.map[key]
102 return self.map[key]
103 except TypeError:
103 except TypeError:
104 self.read()
104 self.lazyread()
105 return self[key]
105 return self[key]
106
106
107 def __contains__(self, key):
107 def __contains__(self, key):
108 if not self.map: self.read()
108 self.lazyread()
109 return key in self.map
109 return key in self.map
110
110
111 def parents(self):
111 def parents(self):
112 if not self.pl:
112 self.lazyread()
113 self.read()
114 return self.pl
113 return self.pl
115
114
116 def markdirty(self):
115 def markdirty(self):
@@ -118,8 +117,7 b' class dirstate:'
118 self.dirty = 1
117 self.dirty = 1
119
118
120 def setparents(self, p1, p2=nullid):
119 def setparents(self, p1, p2=nullid):
121 if not self.pl:
120 self.lazyread()
122 self.read()
123 self.markdirty()
121 self.markdirty()
124 self.pl = p1, p2
122 self.pl = p1, p2
125
123
@@ -129,9 +127,11 b' class dirstate:'
129 except KeyError:
127 except KeyError:
130 return "?"
128 return "?"
131
129
130 def lazyread(self):
131 if self.map is None:
132 self.read()
133
132 def read(self):
134 def read(self):
133 if self.map is not None: return self.map
134
135 self.map = {}
135 self.map = {}
136 self.pl = [nullid, nullid]
136 self.pl = [nullid, nullid]
137 try:
137 try:
@@ -154,7 +154,7 b' class dirstate:'
154 pos += l
154 pos += l
155
155
156 def copy(self, source, dest):
156 def copy(self, source, dest):
157 self.read()
157 self.lazyread()
158 self.markdirty()
158 self.markdirty()
159 self.copies[dest] = source
159 self.copies[dest] = source
160
160
@@ -169,13 +169,13 b' class dirstate:'
169 a marked for addition'''
169 a marked for addition'''
170
170
171 if not files: return
171 if not files: return
172 self.read()
172 self.lazyread()
173 self.markdirty()
173 self.markdirty()
174 for f in files:
174 for f in files:
175 if state == "r":
175 if state == "r":
176 self.map[f] = ('r', 0, 0, 0)
176 self.map[f] = ('r', 0, 0, 0)
177 else:
177 else:
178 s = os.lstat(os.path.join(self.root, f))
178 s = os.lstat(self.wjoin(f))
179 st_size = kw.get('st_size', s.st_size)
179 st_size = kw.get('st_size', s.st_size)
180 st_mtime = kw.get('st_mtime', s.st_mtime)
180 st_mtime = kw.get('st_mtime', s.st_mtime)
181 self.map[f] = (state, s.st_mode, st_size, st_mtime)
181 self.map[f] = (state, s.st_mode, st_size, st_mtime)
@@ -184,7 +184,7 b' class dirstate:'
184
184
185 def forget(self, files):
185 def forget(self, files):
186 if not files: return
186 if not files: return
187 self.read()
187 self.lazyread()
188 self.markdirty()
188 self.markdirty()
189 for f in files:
189 for f in files:
190 try:
190 try:
@@ -198,7 +198,7 b' class dirstate:'
198 self.markdirty()
198 self.markdirty()
199
199
200 def write(self):
200 def write(self):
201 st = self.opener("dirstate", "w")
201 st = self.opener("dirstate", "w", atomic=True)
202 st.write("".join(self.pl))
202 st.write("".join(self.pl))
203 for f, e in self.map.items():
203 for f, e in self.map.items():
204 c = self.copied(f)
204 c = self.copied(f)
@@ -213,7 +213,7 b' class dirstate:'
213 unknown = []
213 unknown = []
214
214
215 for x in files:
215 for x in files:
216 if x is '.':
216 if x == '.':
217 return self.map.copy()
217 return self.map.copy()
218 if x not in self.map:
218 if x not in self.map:
219 unknown.append(x)
219 unknown.append(x)
@@ -241,7 +241,7 b' class dirstate:'
241 bs += 1
241 bs += 1
242 return ret
242 return ret
243
243
244 def supported_type(self, f, st, verbose=True):
244 def supported_type(self, f, st, verbose=False):
245 if stat.S_ISREG(st.st_mode):
245 if stat.S_ISREG(st.st_mode):
246 return True
246 return True
247 if verbose:
247 if verbose:
@@ -258,7 +258,7 b' class dirstate:'
258 return False
258 return False
259
259
260 def statwalk(self, files=None, match=util.always, dc=None):
260 def statwalk(self, files=None, match=util.always, dc=None):
261 self.read()
261 self.lazyread()
262
262
263 # walk all files by default
263 # walk all files by default
264 if not files:
264 if not files:
@@ -296,7 +296,6 b' class dirstate:'
296 def walkhelper(self, files, statmatch, dc):
296 def walkhelper(self, files, statmatch, dc):
297 # recursion free walker, faster than os.walk.
297 # recursion free walker, faster than os.walk.
298 def findfiles(s):
298 def findfiles(s):
299 retfiles = []
300 work = [s]
299 work = [s]
301 while work:
300 while work:
302 top = work.pop()
301 top = work.pop()
@@ -306,7 +305,7 b' class dirstate:'
306 nd = util.normpath(top[len(self.root) + 1:])
305 nd = util.normpath(top[len(self.root) + 1:])
307 if nd == '.': nd = ''
306 if nd == '.': nd = ''
308 for f in names:
307 for f in names:
309 np = os.path.join(nd, f)
308 np = util.pconvert(os.path.join(nd, f))
310 if seen(np):
309 if seen(np):
311 continue
310 continue
312 p = os.path.join(top, f)
311 p = os.path.join(top, f)
@@ -317,12 +316,12 b' class dirstate:'
317 if statmatch(ds, st):
316 if statmatch(ds, st):
318 work.append(p)
317 work.append(p)
319 if statmatch(np, st) and np in dc:
318 if statmatch(np, st) and np in dc:
320 yield 'm', util.pconvert(np), st
319 yield 'm', np, st
321 elif statmatch(np, st):
320 elif statmatch(np, st):
322 if self.supported_type(np, st):
321 if self.supported_type(np, st):
323 yield 'f', util.pconvert(np), st
322 yield 'f', np, st
324 elif np in dc:
323 elif np in dc:
325 yield 'm', util.pconvert(np), st
324 yield 'm', np, st
326
325
327 known = {'.hg': 1}
326 known = {'.hg': 1}
328 def seen(fn):
327 def seen(fn):
@@ -332,13 +331,20 b' class dirstate:'
332 # step one, find all files that match our criteria
331 # step one, find all files that match our criteria
333 files.sort()
332 files.sort()
334 for ff in util.unique(files):
333 for ff in util.unique(files):
335 f = os.path.join(self.root, ff)
334 f = self.wjoin(ff)
336 try:
335 try:
337 st = os.lstat(f)
336 st = os.lstat(f)
338 except OSError, inst:
337 except OSError, inst:
339 if ff not in dc: self.ui.warn('%s: %s\n' % (
338 nf = util.normpath(ff)
340 util.pathto(self.getcwd(), ff),
339 found = False
341 inst.strerror))
340 for fn in dc:
341 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
342 found = True
343 break
344 if not found:
345 self.ui.warn('%s: %s\n' % (
346 util.pathto(self.getcwd(), ff),
347 inst.strerror))
342 continue
348 continue
343 if stat.S_ISDIR(st.st_mode):
349 if stat.S_ISDIR(st.st_mode):
344 cmp1 = (lambda x, y: cmp(x[1], y[1]))
350 cmp1 = (lambda x, y: cmp(x[1], y[1]))
@@ -352,7 +358,7 b' class dirstate:'
352 continue
358 continue
353 self.blockignore = True
359 self.blockignore = True
354 if statmatch(ff, st):
360 if statmatch(ff, st):
355 if self.supported_type(ff, st):
361 if self.supported_type(ff, st, verbose=True):
356 yield 'f', ff, st
362 yield 'f', ff, st
357 elif ff in dc:
363 elif ff in dc:
358 yield 'm', ff, st
364 yield 'm', ff, st
@@ -380,7 +386,7 b' class dirstate:'
380 nonexistent = True
386 nonexistent = True
381 if not st:
387 if not st:
382 try:
388 try:
383 f = os.path.join(self.root, fn)
389 f = self.wjoin(fn)
384 st = os.lstat(f)
390 st = os.lstat(f)
385 except OSError, inst:
391 except OSError, inst:
386 if inst.errno != errno.ENOENT:
392 if inst.errno != errno.ENOENT:
@@ -1,10 +1,10 b''
1 import getopt
1 import getopt
2
2
3 def fancyopts(args, options, state):
3 def fancyopts(args, options, state):
4 long=[]
4 long = []
5 short=''
5 short = ''
6 map={}
6 map = {}
7 dt={}
7 dt = {}
8
8
9 for s, l, d, c in options:
9 for s, l, d, c in options:
10 pl = l.replace('-', '_')
10 pl = l.replace('-', '_')
@@ -54,11 +54,11 b' class filelog(revlog):'
54 mt = ""
54 mt = ""
55 if meta:
55 if meta:
56 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
56 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
57 text = "\1\n" + "".join(mt) + "\1\n" + text
57 text = "\1\n%s\1\n%s" % ("".join(mt), text)
58 return self.addrevision(text, transaction, link, p1, p2)
58 return self.addrevision(text, transaction, link, p1, p2)
59
59
60 def renamed(self, node):
60 def renamed(self, node):
61 if 0 and self.parents(node)[0] != nullid:
61 if 0 and self.parents(node)[0] != nullid: # XXX
62 return False
62 return False
63 m = self.readmeta(node)
63 m = self.readmeta(node)
64 if m and m.has_key("copy"):
64 if m and m.has_key("copy"):
@@ -71,7 +71,7 b' def get_mtime(repo_path):'
71 else:
71 else:
72 return os.stat(hg_path).st_mtime
72 return os.stat(hg_path).st_mtime
73
73
74 class hgrequest:
74 class hgrequest(object):
75 def __init__(self, inp=None, out=None, env=None):
75 def __init__(self, inp=None, out=None, env=None):
76 self.inp = inp or sys.stdin
76 self.inp = inp or sys.stdin
77 self.out = out or sys.stdout
77 self.out = out or sys.stdout
@@ -104,7 +104,7 b' class hgrequest:'
104 headers.append(('Content-length', str(size)))
104 headers.append(('Content-length', str(size)))
105 self.header(headers)
105 self.header(headers)
106
106
107 class templater:
107 class templater(object):
108 def __init__(self, mapfile, filters={}, defaults={}):
108 def __init__(self, mapfile, filters={}, defaults={}):
109 self.cache = {}
109 self.cache = {}
110 self.map = {}
110 self.map = {}
@@ -165,7 +165,6 b' class templater:'
165 common_filters = {
165 common_filters = {
166 "escape": cgi.escape,
166 "escape": cgi.escape,
167 "strip": lambda x: x.strip(),
167 "strip": lambda x: x.strip(),
168 "rstrip": lambda x: x.rstrip(),
169 "age": age,
168 "age": age,
170 "date": lambda x: util.datestr(x),
169 "date": lambda x: util.datestr(x),
171 "addbreaks": nl2br,
170 "addbreaks": nl2br,
@@ -176,7 +175,7 b' common_filters = {'
176 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
175 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
177 }
176 }
178
177
179 class hgweb:
178 class hgweb(object):
180 def __init__(self, repo, name=None):
179 def __init__(self, repo, name=None):
181 if type(repo) == type(""):
180 if type(repo) == type(""):
182 self.repo = hg.repository(ui.ui(), repo)
181 self.repo = hg.repository(ui.ui(), repo)
@@ -952,14 +951,8 b' def create_server(repo):'
952 else:
951 else:
953 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
952 return BaseHTTPServer.HTTPServer((address, port), hgwebhandler)
954
953
955 def server(path, name, templates, address, port, use_ipv6=False,
956 accesslog=sys.stdout, errorlog=sys.stderr):
957 httpd = create_server(path, name, templates, address, port, use_ipv6,
958 accesslog, errorlog)
959 httpd.serve_forever()
960
961 # This is a stopgap
954 # This is a stopgap
962 class hgwebdir:
955 class hgwebdir(object):
963 def __init__(self, config):
956 def __init__(self, config):
964 def cleannames(items):
957 def cleannames(items):
965 return [(name.strip('/'), path) for name, path in items]
958 return [(name.strip('/'), path) for name, path in items]
@@ -1000,7 +993,10 b' class hgwebdir:'
1000 .replace("//", "/"))
993 .replace("//", "/"))
1001
994
1002 # update time with local timezone
995 # update time with local timezone
1003 d = (get_mtime(path), util.makedate()[1])
996 try:
997 d = (get_mtime(path), util.makedate()[1])
998 except OSError:
999 continue
1004
1000
1005 yield dict(contact=(get("ui", "username") or # preferred
1001 yield dict(contact=(get("ui", "username") or # preferred
1006 get("web", "contact") or # deprecated
1002 get("web", "contact") or # deprecated
@@ -1017,7 +1013,12 b' class hgwebdir:'
1017 if virtual:
1013 if virtual:
1018 real = dict(self.repos).get(virtual)
1014 real = dict(self.repos).get(virtual)
1019 if real:
1015 if real:
1020 hgweb(real).run(req)
1016 try:
1017 hgweb(real).run(req)
1018 except IOError, inst:
1019 req.write(tmpl("error", error=inst.strerror))
1020 except hg.RepoError, inst:
1021 req.write(tmpl("error", error=str(inst)))
1021 else:
1022 else:
1022 req.write(tmpl("notfound", repo=virtual))
1023 req.write(tmpl("notfound", repo=virtual))
1023 else:
1024 else:
@@ -7,7 +7,7 b''
7
7
8 import byterange, urllib2
8 import byterange, urllib2
9
9
10 class httprangereader:
10 class httprangereader(object):
11 def __init__(self, url):
11 def __init__(self, url):
12 self.url = url
12 self.url = url
13 self.pos = 0
13 self.pos = 0
@@ -12,7 +12,7 b' from i18n import gettext as _'
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
14
14
15 class localrepository:
15 class localrepository(object):
16 def __init__(self, ui, path=None, create=0):
16 def __init__(self, ui, path=None, create=0):
17 if not path:
17 if not path:
18 p = os.getcwd()
18 p = os.getcwd()
@@ -43,7 +43,7 b' class localrepository:'
43
43
44 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
44 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
45 try:
45 try:
46 self.ui.readconfig(os.path.join(self.path, "hgrc"))
46 self.ui.readconfig(self.join("hgrc"))
47 except IOError: pass
47 except IOError: pass
48
48
49 def hook(self, name, **args):
49 def hook(self, name, **args):
@@ -225,18 +225,20 b' class localrepository:'
225 lock = self.lock()
225 lock = self.lock()
226 if os.path.exists(self.join("journal")):
226 if os.path.exists(self.join("journal")):
227 self.ui.status(_("rolling back interrupted transaction\n"))
227 self.ui.status(_("rolling back interrupted transaction\n"))
228 return transaction.rollback(self.opener, self.join("journal"))
228 transaction.rollback(self.opener, self.join("journal"))
229 return True
229 else:
230 else:
230 self.ui.warn(_("no interrupted transaction available\n"))
231 self.ui.warn(_("no interrupted transaction available\n"))
232 return False
231
233
232 def undo(self):
234 def undo(self):
235 wlock = self.wlock()
233 lock = self.lock()
236 lock = self.lock()
234 if os.path.exists(self.join("undo")):
237 if os.path.exists(self.join("undo")):
235 self.ui.status(_("rolling back last transaction\n"))
238 self.ui.status(_("rolling back last transaction\n"))
236 transaction.rollback(self.opener, self.join("undo"))
239 transaction.rollback(self.opener, self.join("undo"))
237 self.dirstate = None
238 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
240 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
239 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
241 self.dirstate.read()
240 else:
242 else:
241 self.ui.warn(_("no undo information available\n"))
243 self.ui.warn(_("no undo information available\n"))
242
244
@@ -249,6 +251,17 b' class localrepository:'
249 return lock.lock(self.join("lock"), wait)
251 return lock.lock(self.join("lock"), wait)
250 raise inst
252 raise inst
251
253
254 def wlock(self, wait=1):
255 try:
256 wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
257 except lock.LockHeld, inst:
258 if not wait:
259 raise inst
260 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
261 wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
262 self.dirstate.read()
263 return wlock
264
252 def rawcommit(self, files, text, user, date, p1=None, p2=None):
265 def rawcommit(self, files, text, user, date, p1=None, p2=None):
253 orig_parent = self.dirstate.parents()[0] or nullid
266 orig_parent = self.dirstate.parents()[0] or nullid
254 p1 = p1 or self.dirstate.parents()[0] or nullid
267 p1 = p1 or self.dirstate.parents()[0] or nullid
@@ -265,6 +278,8 b' class localrepository:'
265 else:
278 else:
266 update_dirstate = 0
279 update_dirstate = 0
267
280
281 wlock = self.wlock()
282 lock = self.lock()
268 tr = self.transaction()
283 tr = self.transaction()
269 mm = m1.copy()
284 mm = m1.copy()
270 mfm = mf1.copy()
285 mfm = mf1.copy()
@@ -353,6 +368,7 b' class localrepository:'
353 if not self.hook("precommit"):
368 if not self.hook("precommit"):
354 return None
369 return None
355
370
371 wlock = self.wlock()
356 lock = self.lock()
372 lock = self.lock()
357 tr = self.transaction()
373 tr = self.transaction()
358
374
@@ -446,8 +462,14 b' class localrepository:'
446
462
447 def walk(self, node=None, files=[], match=util.always):
463 def walk(self, node=None, files=[], match=util.always):
448 if node:
464 if node:
465 fdict = dict.fromkeys(files)
449 for fn in self.manifest.read(self.changelog.read(node)[0]):
466 for fn in self.manifest.read(self.changelog.read(node)[0]):
450 if match(fn): yield 'm', fn
467 fdict.pop(fn, None)
468 if match(fn):
469 yield 'm', fn
470 for fn in fdict:
471 self.ui.warn(_('%s: No such file in rev %s\n') % (
472 util.pathto(self.getcwd(), fn), short(node)))
451 else:
473 else:
452 for src, fn in self.dirstate.walk(files, match):
474 for src, fn in self.dirstate.walk(files, match):
453 yield src, fn
475 yield src, fn
@@ -470,6 +492,10 b' class localrepository:'
470
492
471 # are we comparing the working directory?
493 # are we comparing the working directory?
472 if not node2:
494 if not node2:
495 try:
496 wlock = self.wlock(wait=0)
497 except lock.LockHeld:
498 wlock = None
473 l, c, a, d, u = self.dirstate.changes(files, match)
499 l, c, a, d, u = self.dirstate.changes(files, match)
474
500
475 # are we comparing working dir against its parent?
501 # are we comparing working dir against its parent?
@@ -481,6 +507,8 b' class localrepository:'
481 for f in l:
507 for f in l:
482 if fcmp(f, mf2):
508 if fcmp(f, mf2):
483 c.append(f)
509 c.append(f)
510 elif wlock is not None:
511 self.dirstate.update([f], "n")
484
512
485 for l in c, a, d, u:
513 for l in c, a, d, u:
486 l.sort()
514 l.sort()
@@ -524,6 +552,7 b' class localrepository:'
524 return (c, a, d, u)
552 return (c, a, d, u)
525
553
526 def add(self, list):
554 def add(self, list):
555 wlock = self.wlock()
527 for f in list:
556 for f in list:
528 p = self.wjoin(f)
557 p = self.wjoin(f)
529 if not os.path.exists(p):
558 if not os.path.exists(p):
@@ -536,6 +565,7 b' class localrepository:'
536 self.dirstate.update([f], "a")
565 self.dirstate.update([f], "a")
537
566
538 def forget(self, list):
567 def forget(self, list):
568 wlock = self.wlock()
539 for f in list:
569 for f in list:
540 if self.dirstate.state(f) not in 'ai':
570 if self.dirstate.state(f) not in 'ai':
541 self.ui.warn(_("%s not added!\n") % f)
571 self.ui.warn(_("%s not added!\n") % f)
@@ -549,6 +579,7 b' class localrepository:'
549 util.unlink(self.wjoin(f))
579 util.unlink(self.wjoin(f))
550 except OSError, inst:
580 except OSError, inst:
551 if inst.errno != errno.ENOENT: raise
581 if inst.errno != errno.ENOENT: raise
582 wlock = self.wlock()
552 for f in list:
583 for f in list:
553 p = self.wjoin(f)
584 p = self.wjoin(f)
554 if os.path.exists(p):
585 if os.path.exists(p):
@@ -566,6 +597,7 b' class localrepository:'
566 mn = self.changelog.read(p)[0]
597 mn = self.changelog.read(p)[0]
567 mf = self.manifest.readflags(mn)
598 mf = self.manifest.readflags(mn)
568 m = self.manifest.read(mn)
599 m = self.manifest.read(mn)
600 wlock = self.wlock()
569 for f in list:
601 for f in list:
570 if self.dirstate.state(f) not in "r":
602 if self.dirstate.state(f) not in "r":
571 self.ui.warn("%s not removed!\n" % f)
603 self.ui.warn("%s not removed!\n" % f)
@@ -582,12 +614,17 b' class localrepository:'
582 elif not os.path.isfile(p):
614 elif not os.path.isfile(p):
583 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
615 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
584 else:
616 else:
617 wlock = self.wlock()
585 if self.dirstate.state(dest) == '?':
618 if self.dirstate.state(dest) == '?':
586 self.dirstate.update([dest], "a")
619 self.dirstate.update([dest], "a")
587 self.dirstate.copy(source, dest)
620 self.dirstate.copy(source, dest)
588
621
589 def heads(self):
622 def heads(self, start=None):
590 return self.changelog.heads()
623 heads = self.changelog.heads(start)
624 # sort the output in rev descending order
625 heads = [(-self.changelog.rev(h), h) for h in heads]
626 heads.sort()
627 return [n for (r, n) in heads]
591
628
592 # branchlookup returns a dict giving a list of branches for
629 # branchlookup returns a dict giving a list of branches for
593 # each head. A branch is defined as the tag of a node or
630 # each head. A branch is defined as the tag of a node or
@@ -1372,6 +1409,9 b' class localrepository:'
1372 mw[f] = ""
1409 mw[f] = ""
1373 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1410 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1374
1411
1412 if moddirstate:
1413 wlock = self.wlock()
1414
1375 for f in d:
1415 for f in d:
1376 if f in mw: del mw[f]
1416 if f in mw: del mw[f]
1377
1417
@@ -11,11 +11,12 b' import util'
11 class LockHeld(Exception):
11 class LockHeld(Exception):
12 pass
12 pass
13
13
14 class lock:
14 class lock(object):
15 def __init__(self, file, wait=1):
15 def __init__(self, file, wait=1, releasefn=None):
16 self.f = file
16 self.f = file
17 self.held = 0
17 self.held = 0
18 self.wait = wait
18 self.wait = wait
19 self.releasefn = releasefn
19 self.lock()
20 self.lock()
20
21
21 def __del__(self):
22 def __del__(self):
@@ -43,6 +44,8 b' class lock:'
43 def release(self):
44 def release(self):
44 if self.held:
45 if self.held:
45 self.held = 0
46 self.held = 0
47 if self.releasefn:
48 self.releasefn()
46 try:
49 try:
47 os.unlink(self.f)
50 os.unlink(self.f)
48 except: pass
51 except: pass
@@ -5,17 +5,16 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import sys, struct
8 import struct
9 from revlog import *
9 from revlog import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "bisect")
12 demandload(globals(), "bisect array")
13
13
14 class manifest(revlog):
14 class manifest(revlog):
15 def __init__(self, opener):
15 def __init__(self, opener):
16 self.mapcache = None
16 self.mapcache = None
17 self.listcache = None
17 self.listcache = None
18 self.addlist = None
19 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
18 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
20
19
21 def read(self, node):
20 def read(self, node):
@@ -25,8 +24,9 b' class manifest(revlog):'
25 text = self.revision(node)
24 text = self.revision(node)
26 map = {}
25 map = {}
27 flag = {}
26 flag = {}
28 self.listcache = (text, text.splitlines(1))
27 self.listcache = array.array('c', text)
29 for l in self.listcache[1]:
28 lines = text.splitlines(1)
29 for l in lines:
30 (f, n) = l.split('\0')
30 (f, n) = l.split('\0')
31 map[f] = bin(n[:40])
31 map[f] = bin(n[:40])
32 flag[f] = (n[40:-1] == "x")
32 flag[f] = (n[40:-1] == "x")
@@ -39,57 +39,67 b' class manifest(revlog):'
39 self.read(node)
39 self.read(node)
40 return self.mapcache[2]
40 return self.mapcache[2]
41
41
42 def diff(self, a, b):
43 return mdiff.textdiff(str(a), str(b))
44
42 def add(self, map, flags, transaction, link, p1=None, p2=None,
45 def add(self, map, flags, transaction, link, p1=None, p2=None,
43 changed=None):
46 changed=None):
44 # directly generate the mdiff delta from the data collected during
47
45 # the bisect loop below
48 # returns a tuple (start, end). If the string is found
46 def gendelta(delta):
49 # m[start:end] are the line containing that string. If start == end
47 i = 0
50 # the string was not found and they indicate the proper sorted
48 result = []
51 # insertion point. This was taken from bisect_left, and modified
49 while i < len(delta):
52 # to find line start/end as it goes along.
50 start = delta[i][2]
53 #
51 end = delta[i][3]
54 # m should be a buffer or a string
52 l = delta[i][4]
55 # s is a string
53 if l == None:
56 #
54 l = ""
57 def manifestsearch(m, s, lo=0, hi=None):
55 while i < len(delta) - 1 and start <= delta[i+1][2] \
58 def advance(i, c):
56 and end >= delta[i+1][2]:
59 while i < lenm and m[i] != c:
57 if delta[i+1][3] > end:
58 end = delta[i+1][3]
59 if delta[i+1][4]:
60 l += delta[i+1][4]
61 i += 1
60 i += 1
62 result.append(struct.pack(">lll", start, end, len(l)) + l)
61 return i
63 i += 1
62 lenm = len(m)
64 return result
63 if not hi:
64 hi = lenm
65 while lo < hi:
66 mid = (lo + hi) // 2
67 start = mid
68 while start > 0 and m[start-1] != '\n':
69 start -= 1
70 end = advance(start, '\0')
71 if m[start:end] < s:
72 # we know that after the null there are 40 bytes of sha1
73 # this translates to the bisect lo = mid + 1
74 lo = advance(end + 40, '\n') + 1
75 else:
76 # this translates to the bisect hi = mid
77 hi = start
78 end = advance(lo, '\0')
79 found = m[lo:end]
80 if cmp(s, found) == 0:
81 # we know that after the null there are 40 bytes of sha1
82 end = advance(end + 40, '\n')
83 return (lo, end+1)
84 else:
85 return (lo, lo)
65
86
66 # apply the changes collected during the bisect loop to our addlist
87 # apply the changes collected during the bisect loop to our addlist
67 def addlistdelta(addlist, delta):
88 # return a delta suitable for addrevision
68 # apply the deltas to the addlist. start from the bottom up
89 def addlistdelta(addlist, x):
90 # start from the bottom up
69 # so changes to the offsets don't mess things up.
91 # so changes to the offsets don't mess things up.
70 i = len(delta)
92 i = len(x)
71 while i > 0:
93 while i > 0:
72 i -= 1
94 i -= 1
73 start = delta[i][0]
95 start = x[i][0]
74 end = delta[i][1]
96 end = x[i][1]
75 if delta[i][4]:
97 if x[i][2]:
76 addlist[start:end] = [delta[i][4]]
98 addlist[start:end] = array.array('c', x[i][2])
77 else:
99 else:
78 del addlist[start:end]
100 del addlist[start:end]
79 return addlist
101 return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2] \
80
102 for d in x ])
81 # calculate the byte offset of the start of each line in the
82 # manifest
83 def calcoffsets(addlist):
84 offsets = [0] * (len(addlist) + 1)
85 offset = 0
86 i = 0
87 while i < len(addlist):
88 offsets[i] = offset
89 offset += len(addlist[i])
90 i += 1
91 offsets[i] = offset
92 return offsets
93
103
94 # if we're using the listcache, make sure it is valid and
104 # if we're using the listcache, make sure it is valid and
95 # parented by the same node we're diffing against
105 # parented by the same node we're diffing against
@@ -98,15 +108,13 b' class manifest(revlog):'
98 files = map.keys()
108 files = map.keys()
99 files.sort()
109 files.sort()
100
110
101 self.addlist = ["%s\000%s%s\n" %
111 text = ["%s\000%s%s\n" %
102 (f, hex(map[f]), flags[f] and "x" or '')
112 (f, hex(map[f]), flags[f] and "x" or '')
103 for f in files]
113 for f in files]
114 self.listcache = array.array('c', "".join(text))
104 cachedelta = None
115 cachedelta = None
105 else:
116 else:
106 addlist = self.listcache[1]
117 addlist = self.listcache
107
108 # find the starting offset for each line in the add list
109 offsets = calcoffsets(addlist)
110
118
111 # combine the changed lists into one list for sorting
119 # combine the changed lists into one list for sorting
112 work = [[x, 0] for x in changed[0]]
120 work = [[x, 0] for x in changed[0]]
@@ -114,45 +122,52 b' class manifest(revlog):'
114 work.sort()
122 work.sort()
115
123
116 delta = []
124 delta = []
117 bs = 0
125 dstart = None
126 dend = None
127 dline = [""]
128 start = 0
129 # zero copy representation of addlist as a buffer
130 addbuf = buffer(addlist)
118
131
132 # start with a readonly loop that finds the offset of
133 # each line and creates the deltas
119 for w in work:
134 for w in work:
120 f = w[0]
135 f = w[0]
121 # bs will either be the index of the item or the insert point
136 # bs will either be the index of the item or the insert point
122 bs = bisect.bisect(addlist, f, bs)
137 start, end = manifestsearch(addbuf, f, start)
123 if bs < len(addlist):
124 fn = addlist[bs][:addlist[bs].index('\0')]
125 else:
126 fn = None
127 if w[1] == 0:
138 if w[1] == 0:
128 l = "%s\000%s%s\n" % (f, hex(map[f]),
139 l = "%s\000%s%s\n" % (f, hex(map[f]),
129 flags[f] and "x" or '')
140 flags[f] and "x" or '')
130 else:
141 else:
131 l = None
142 l = ""
132 start = bs
143 if start == end and w[1] == 1:
133 if fn != f:
144 # item we want to delete was not found, error out
134 # item not found, insert a new one
145 raise AssertionError(
135 end = bs
136 if w[1] == 1:
137 raise AssertionError(
138 _("failed to remove %s from manifest\n") % f)
146 _("failed to remove %s from manifest\n") % f)
147 if dstart != None and dstart <= start and dend >= start:
148 if dend < end:
149 dend = end
150 if l:
151 dline.append(l)
139 else:
152 else:
140 # item is found, replace/delete the existing line
153 if dstart != None:
141 end = bs + 1
154 delta.append([dstart, dend, "".join(dline)])
142 delta.append([start, end, offsets[start], offsets[end], l])
155 dstart = start
156 dend = end
157 dline = [l]
143
158
144 self.addlist = addlistdelta(addlist, delta)
159 if dstart != None:
145 if self.mapcache[0] == self.tip():
160 delta.append([dstart, dend, "".join(dline)])
146 cachedelta = "".join(gendelta(delta))
161 # apply the delta to the addlist, and get a delta for addrevision
147 else:
162 cachedelta = addlistdelta(addlist, delta)
148 cachedelta = None
149
163
150 text = "".join(self.addlist)
164 # the delta is only valid if we've been processing the tip revision
151 if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
165 if self.mapcache[0] != self.tip():
152 raise AssertionError(_("manifest delta failure\n"))
166 cachedelta = None
153 n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
167 self.listcache = addlist
168
169 n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
170 p2, cachedelta)
154 self.mapcache = (n, map, flags)
171 self.mapcache = (n, map, flags)
155 self.listcache = (text, self.addlist)
156 self.addlist = None
157
172
158 return n
173 return n
@@ -32,8 +32,8 b' def unidiff(a, ad, b, bd, fn, r=None, te'
32 l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn))
32 l = list(difflib.unified_diff(a, b, "a/" + fn, "b/" + fn))
33 if not l: return ""
33 if not l: return ""
34 # difflib uses a space, rather than a tab
34 # difflib uses a space, rather than a tab
35 l[0] = l[0][:-2] + "\t" + ad + "\n"
35 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
36 l[1] = l[1][:-2] + "\t" + bd + "\n"
36 l[1] = "%s\t%s\n" % (l[1][:-2], bd)
37
37
38 for ln in xrange(len(l)):
38 for ln in xrange(len(l)):
39 if l[ln][-1] != '\n':
39 if l[ln][-1] != '\n':
@@ -7,7 +7,7 b' This software may be used and distribute'
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 import sha, binascii
10 import binascii
11
11
12 nullid = "\0" * 20
12 nullid = "\0" * 20
13
13
@@ -5,11 +5,11 b''
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 class remoterepository:
8 class remoterepository(object):
9 def local(self):
9 def local(self):
10 return False
10 return False
11
11
12 class remotelock:
12 class remotelock(object):
13 def __init__(self, repo):
13 def __init__(self, repo):
14 self.repo = repo
14 self.repo = repo
15 def release(self):
15 def release(self):
@@ -31,15 +31,15 b' def hash(text, p1, p2):'
31
31
32 def compress(text):
32 def compress(text):
33 """ generate a possibly-compressed representation of text """
33 """ generate a possibly-compressed representation of text """
34 if not text: return text
34 if not text: return ("", text)
35 if len(text) < 44:
35 if len(text) < 44:
36 if text[0] == '\0': return text
36 if text[0] == '\0': return ("", text)
37 return 'u' + text
37 return ('u', text)
38 bin = zlib.compress(text)
38 bin = zlib.compress(text)
39 if len(bin) > len(text):
39 if len(bin) > len(text):
40 if text[0] == '\0': return text
40 if text[0] == '\0': return ("", text)
41 return 'u' + text
41 return ('u', text)
42 return bin
42 return ("", bin)
43
43
44 def decompress(bin):
44 def decompress(bin):
45 """ decompress the given input """
45 """ decompress the given input """
@@ -52,7 +52,7 b' def decompress(bin):'
52
52
53 indexformat = ">4l20s20s20s"
53 indexformat = ">4l20s20s20s"
54
54
55 class lazyparser:
55 class lazyparser(object):
56 """
56 """
57 this class avoids the need to parse the entirety of large indices
57 this class avoids the need to parse the entirety of large indices
58
58
@@ -71,6 +71,9 b' class lazyparser:'
71 self.all = 0
71 self.all = 0
72 self.revlog = revlog
72 self.revlog = revlog
73
73
74 def trunc(self, pos):
75 self.l = pos/self.s
76
74 def load(self, pos=None):
77 def load(self, pos=None):
75 if self.all: return
78 if self.all: return
76 if pos is not None:
79 if pos is not None:
@@ -91,7 +94,7 b' class lazyparser:'
91 self.map[e[6]] = i
94 self.map[e[6]] = i
92 i += 1
95 i += 1
93
96
94 class lazyindex:
97 class lazyindex(object):
95 """a lazy version of the index array"""
98 """a lazy version of the index array"""
96 def __init__(self, parser):
99 def __init__(self, parser):
97 self.p = parser
100 self.p = parser
@@ -104,10 +107,14 b' class lazyindex:'
104 return self.p.index[pos]
107 return self.p.index[pos]
105 def __getitem__(self, pos):
108 def __getitem__(self, pos):
106 return self.p.index[pos] or self.load(pos)
109 return self.p.index[pos] or self.load(pos)
110 def __delitem__(self, pos):
111 del self.p.index[pos]
107 def append(self, e):
112 def append(self, e):
108 self.p.index.append(e)
113 self.p.index.append(e)
114 def trunc(self, pos):
115 self.p.trunc(pos)
109
116
110 class lazymap:
117 class lazymap(object):
111 """a lazy version of the node map"""
118 """a lazy version of the node map"""
112 def __init__(self, parser):
119 def __init__(self, parser):
113 self.p = parser
120 self.p = parser
@@ -140,10 +147,12 b' class lazymap:'
140 raise KeyError("node " + hex(key))
147 raise KeyError("node " + hex(key))
141 def __setitem__(self, key, val):
148 def __setitem__(self, key, val):
142 self.p.map[key] = val
149 self.p.map[key] = val
150 def __delitem__(self, key):
151 del self.p.map[key]
143
152
144 class RevlogError(Exception): pass
153 class RevlogError(Exception): pass
145
154
146 class revlog:
155 class revlog(object):
147 """
156 """
148 the underlying revision storage object
157 the underlying revision storage object
149
158
@@ -400,25 +409,28 b' class revlog:'
400 assert heads
409 assert heads
401 return (orderedout, roots, heads)
410 return (orderedout, roots, heads)
402
411
403 def heads(self, stop=None):
412 def heads(self, start=None):
404 """return the list of all nodes that have no children"""
413 """return the list of all nodes that have no children
405 p = {}
414
406 h = []
415 if start is specified, only heads that are descendants of
407 stoprev = 0
416 start will be returned
408 if stop and stop in self.nodemap:
409 stoprev = self.rev(stop)
410
417
411 for r in range(self.count() - 1, -1, -1):
418 """
419 if start is None:
420 start = nullid
421 reachable = {start: 1}
422 heads = {start: 1}
423 startrev = self.rev(start)
424
425 for r in xrange(startrev + 1, self.count()):
412 n = self.node(r)
426 n = self.node(r)
413 if n not in p:
414 h.append(n)
415 if n == stop:
416 break
417 if r < stoprev:
418 break
419 for pn in self.parents(n):
427 for pn in self.parents(n):
420 p[pn] = 1
428 if pn in reachable:
421 return h
429 reachable[n] = 1
430 heads[n] = 1
431 if pn in heads:
432 del heads[pn]
433 return heads.keys()
422
434
423 def children(self, node):
435 def children(self, node):
424 """find the children of a given node"""
436 """find the children of a given node"""
@@ -543,14 +555,16 b' class revlog:'
543 end = self.end(t)
555 end = self.end(t)
544 if not d:
556 if not d:
545 prev = self.revision(self.tip())
557 prev = self.revision(self.tip())
546 d = self.diff(prev, text)
558 d = self.diff(prev, str(text))
547 data = compress(d)
559 data = compress(d)
548 dist = end - start + len(data)
560 l = len(data[1]) + len(data[0])
561 dist = end - start + l
549
562
550 # full versions are inserted when the needed deltas
563 # full versions are inserted when the needed deltas
551 # become comparable to the uncompressed text
564 # become comparable to the uncompressed text
552 if not n or dist > len(text) * 2:
565 if not n or dist > len(text) * 2:
553 data = compress(text)
566 data = compress(text)
567 l = len(data[1]) + len(data[0])
554 base = n
568 base = n
555 else:
569 else:
556 base = self.base(t)
570 base = self.base(t)
@@ -559,14 +573,17 b' class revlog:'
559 if t >= 0:
573 if t >= 0:
560 offset = self.end(t)
574 offset = self.end(t)
561
575
562 e = (offset, len(data), base, link, p1, p2, node)
576 e = (offset, l, base, link, p1, p2, node)
563
577
564 self.index.append(e)
578 self.index.append(e)
565 self.nodemap[node] = n
579 self.nodemap[node] = n
566 entry = struct.pack(indexformat, *e)
580 entry = struct.pack(indexformat, *e)
567
581
568 transaction.add(self.datafile, e[0])
582 transaction.add(self.datafile, e[0])
569 self.opener(self.datafile, "a").write(data)
583 f = self.opener(self.datafile, "a")
584 if data[0]:
585 f.write(data[0])
586 f.write(data[1])
570 transaction.add(self.indexfile, n * len(entry))
587 transaction.add(self.indexfile, n * len(entry))
571 self.opener(self.indexfile, "a").write(entry)
588 self.opener(self.indexfile, "a").write(entry)
572
589
@@ -784,6 +801,10 b' class revlog:'
784 continue
801 continue
785 delta = chunk[80:]
802 delta = chunk[80:]
786
803
804 for p in (p1, p2):
805 if not p in self.nodemap:
806 raise RevlogError(_("unknown parent %s") % short(p1))
807
787 if not chain:
808 if not chain:
788 # retrieve the parent revision of the delta chain
809 # retrieve the parent revision of the delta chain
789 chain = p1
810 chain = p1
@@ -797,7 +818,8 b' class revlog:'
797 # current size.
818 # current size.
798
819
799 if chain == prev:
820 if chain == prev:
800 cdelta = compress(delta)
821 tempd = compress(delta)
822 cdelta = tempd[0] + tempd[1]
801
823
802 if chain != prev or (end - start + len(cdelta)) > measure * 2:
824 if chain != prev or (end - start + len(cdelta)) > measure * 2:
803 # flush our writes here so we can read it in revision
825 # flush our writes here so we can read it in revision
@@ -824,6 +846,36 b' class revlog:'
824 ifh.close()
846 ifh.close()
825 return node
847 return node
826
848
849 def strip(self, rev, minlink):
850 if self.count() == 0 or rev >= self.count():
851 return
852
853 # When stripping away a revision, we need to make sure it
854 # does not actually belong to an older changeset.
855 # The minlink parameter defines the oldest revision
856 # we're allowed to strip away.
857 while minlink > self.index[rev][3]:
858 rev += 1
859 if rev >= self.count():
860 return
861
862 # first truncate the files on disk
863 end = self.start(rev)
864 self.opener(self.datafile, "a").truncate(end)
865 end = rev * struct.calcsize(indexformat)
866 self.opener(self.indexfile, "a").truncate(end)
867
868 # then reset internal state in memory to forget those revisions
869 self.cache = None
870 for p in self.index[rev:]:
871 del self.nodemap[p[6]]
872 del self.index[rev:]
873
874 # truncating the lazyindex also truncates the lazymap.
875 if isinstance(self.index, lazyindex):
876 self.index.trunc(end)
877
878
827 def checksize(self):
879 def checksize(self):
828 expected = 0
880 expected = 0
829 if self.count():
881 if self.count():
@@ -12,10 +12,9 b''
12 # of the GNU General Public License, incorporated herein by reference.
12 # of the GNU General Public License, incorporated herein by reference.
13
13
14 import os
14 import os
15 import util
16 from i18n import gettext as _
15 from i18n import gettext as _
17
16
18 class transaction:
17 class transaction(object):
19 def __init__(self, report, opener, journal, after=None):
18 def __init__(self, report, opener, journal, after=None):
20 self.journal = None
19 self.journal = None
21
20
@@ -10,7 +10,7 b' from i18n import gettext as _'
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "re socket sys util")
11 demandload(globals(), "re socket sys util")
12
12
13 class ui:
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True):
15 interactive=True):
16 self.overlay = {}
16 self.overlay = {}
@@ -106,6 +106,13 b' class Abort(Exception):'
106 def always(fn): return True
106 def always(fn): return True
107 def never(fn): return False
107 def never(fn): return False
108
108
109 def patkind(name, dflt_pat='glob'):
110 """Split a string into an optional pattern kind prefix and the
111 actual pattern."""
112 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
113 if name.startswith(prefix + ':'): return name.split(':', 1)
114 return dflt_pat, name
115
109 def globre(pat, head='^', tail='$'):
116 def globre(pat, head='^', tail='$'):
110 "convert a glob pattern into a regexp"
117 "convert a glob pattern into a regexp"
111 i, n = 0, len(pat)
118 i, n = 0, len(pat)
@@ -158,15 +165,20 b' def pathto(n1, n2):'
158 this returns a path in the form used by the local filesystem, not hg.'''
165 this returns a path in the form used by the local filesystem, not hg.'''
159 if not n1: return localpath(n2)
166 if not n1: return localpath(n2)
160 a, b = n1.split('/'), n2.split('/')
167 a, b = n1.split('/'), n2.split('/')
161 a.reverse(), b.reverse()
168 a.reverse()
169 b.reverse()
162 while a and b and a[-1] == b[-1]:
170 while a and b and a[-1] == b[-1]:
163 a.pop(), b.pop()
171 a.pop()
172 b.pop()
164 b.reverse()
173 b.reverse()
165 return os.sep.join((['..'] * len(a)) + b)
174 return os.sep.join((['..'] * len(a)) + b)
166
175
167 def canonpath(root, cwd, myname):
176 def canonpath(root, cwd, myname):
168 """return the canonical path of myname, given cwd and root"""
177 """return the canonical path of myname, given cwd and root"""
169 rootsep = root + os.sep
178 if root == os.sep:
179 rootsep = os.sep
180 else:
181 rootsep = root + os.sep
170 name = myname
182 name = myname
171 if not name.startswith(os.sep):
183 if not name.startswith(os.sep):
172 name = os.path.join(root, cwd, name)
184 name = os.path.join(root, cwd, name)
@@ -218,11 +230,6 b' def _matcher(canonroot, cwd, names, inc,'
218 make head regex a rooted bool
230 make head regex a rooted bool
219 """
231 """
220
232
221 def patkind(name, dflt_pat='glob'):
222 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
223 if name.startswith(prefix + ':'): return name.split(':', 1)
224 return dflt_pat, name
225
226 def contains_glob(name):
233 def contains_glob(name):
227 for c in name:
234 for c in name:
228 if c in _globchars: return True
235 if c in _globchars: return True
@@ -253,7 +260,7 b' def _matcher(canonroot, cwd, names, inc,'
253 try:
260 try:
254 pat = '(?:%s)' % regex(k, p, tail)
261 pat = '(?:%s)' % regex(k, p, tail)
255 matches.append(re.compile(pat).match)
262 matches.append(re.compile(pat).match)
256 except re.error, inst:
263 except re.error:
257 raise Abort("invalid pattern: %s:%s" % (k, p))
264 raise Abort("invalid pattern: %s:%s" % (k, p))
258
265
259 def buildfn(text):
266 def buildfn(text):
@@ -362,7 +369,36 b' def opener(base):'
362 remote file access from higher level code.
369 remote file access from higher level code.
363 """
370 """
364 p = base
371 p = base
365 def o(path, mode="r", text=False):
372
373 def mktempcopy(name):
374 d, fn = os.path.split(name)
375 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
376 fp = os.fdopen(fd, "wb")
377 try:
378 fp.write(file(name, "rb").read())
379 except:
380 try: os.unlink(temp)
381 except: pass
382 raise
383 fp.close()
384 st = os.lstat(name)
385 os.chmod(temp, st.st_mode)
386 return temp
387
388 class atomicfile(file):
389 """the file will only be copied on close"""
390 def __init__(self, name, mode, atomic=False):
391 self.__name = name
392 self.temp = mktempcopy(name)
393 file.__init__(self, self.temp, mode)
394 def close(self):
395 if not self.closed:
396 file.close(self)
397 rename(self.temp, self.__name)
398 def __del__(self):
399 self.close()
400
401 def o(path, mode="r", text=False, atomic=False):
366 f = os.path.join(p, path)
402 f = os.path.join(p, path)
367
403
368 if not text:
404 if not text:
@@ -376,19 +412,10 b' def opener(base):'
376 if not os.path.isdir(d):
412 if not os.path.isdir(d):
377 os.makedirs(d)
413 os.makedirs(d)
378 else:
414 else:
415 if atomic:
416 return atomicfile(f, mode)
379 if nlink > 1:
417 if nlink > 1:
380 d, fn = os.path.split(f)
418 rename(mktempcopy(f), f)
381 fd, temp = tempfile.mkstemp(prefix=fn, dir=d)
382 fp = os.fdopen(fd, "wb")
383 try:
384 fp.write(file(f, "rb").read())
385 except:
386 try: os.unlink(temp)
387 except: pass
388 raise
389 fp.close()
390 rename(temp, f)
391
392 return file(f, mode)
419 return file(f, mode)
393
420
394 return o
421 return o
@@ -484,6 +511,7 b' else:'
484 nulldev = '/dev/null'
511 nulldev = '/dev/null'
485
512
486 def rcfiles(path):
513 def rcfiles(path):
514 print 'checking', path
487 rcs = [os.path.join(path, 'hgrc')]
515 rcs = [os.path.join(path, 'hgrc')]
488 rcdir = os.path.join(path, 'hgrc.d')
516 rcdir = os.path.join(path, 'hgrc.d')
489 try:
517 try:
@@ -72,8 +72,10 b' class install_package_data(install_data)'
72 try:
72 try:
73 mercurial.version.remember_version(version)
73 mercurial.version.remember_version(version)
74 cmdclass = {'install_data': install_package_data}
74 cmdclass = {'install_data': install_package_data}
75 py2exe_opts = {}
75 if py2exe_for_demandload is not None:
76 if py2exe_for_demandload is not None:
76 cmdclass['py2exe'] = py2exe_for_demandload
77 cmdclass['py2exe'] = py2exe_for_demandload
78 py2exe_opts['console'] = ['hg']
77 setup(name='mercurial',
79 setup(name='mercurial',
78 version=mercurial.version.get_version(),
80 version=mercurial.version.get_version(),
79 author='Matt Mackall',
81 author='Matt Mackall',
@@ -90,6 +92,6 b' try:'
90 glob.glob('templates/*.tmpl'))],
92 glob.glob('templates/*.tmpl'))],
91 cmdclass=cmdclass,
93 cmdclass=cmdclass,
92 scripts=['hg', 'hgmerge'],
94 scripts=['hg', 'hgmerge'],
93 console = ['hg'])
95 **py2exe_opts)
94 finally:
96 finally:
95 mercurial.version.forget_version()
97 mercurial.version.forget_version()
@@ -1,5 +1,5 b''
1 <item>
1 <item>
2 <title>#desc|strip|firstline|rstrip|escape#</title>
2 <title>#desc|strip|firstline|strip|escape#</title>
3 <link>#url#?cs=#node|short#</link>
3 <link>#url#?cs=#node|short#</link>
4 <description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
4 <description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
5 <author>#author|obfuscate#</author>
5 <author>#author|obfuscate#</author>
@@ -1,5 +1,5 b''
1 <item>
1 <item>
2 <title>#desc|strip|firstline|rstrip|escape#</title>
2 <title>#desc|strip|firstline|strip|escape#</title>
3 <link>#url#?f=#filenode|short#;file=#file#</link>
3 <link>#url#?f=#filenode|short#;file=#file#</link>
4 <description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
4 <description><![CDATA[#desc|strip|escape|addbreaks#]]></description>
5 <author>#author|obfuscate#</author>
5 <author>#author|obfuscate#</author>
@@ -39,3 +39,4 b' indexentry = "<tr class="parity#parity#"'
39 index = index.tmpl
39 index = index.tmpl
40 archiveentry = "<a href="?ca=#node|short#;type=#type#">#type#</a> "
40 archiveentry = "<a href="?ca=#node|short#;type=#type#">#type#</a> "
41 notfound = notfound.tmpl
41 notfound = notfound.tmpl
42 error = error.tmpl
@@ -5,7 +5,7 b''
5
5
6 <h2>Mercurial Repositories</h2>
6 <h2>Mercurial Repositories</h2>
7
7
8 The specified repository "#repo#" is unknown, sorry.
8 The specified repository "#repo|escape#" is unknown, sorry.
9
9
10 Please go back to the main repository list page.
10 Please go back to the main repository list page.
11
11
@@ -1,5 +1,5 b''
1 #header#
1 #header#
2 <title>#repo#: tags</title>
2 <title>#repo|escape#: tags</title>
3 </head>
3 </head>
4 <body>
4 <body>
5
5
@@ -40,16 +40,11 b' HGTMP="${TMPDIR-/tmp}/hgtests.$RANDOM.$R'
40 }
40 }
41
41
42 TESTDIR="$PWD"
42 TESTDIR="$PWD"
43
44 if [ -d /usr/lib64 ]; then
45 lib=lib64
46 else
47 lib=lib
48 fi
49
50 INST="$HGTMP/install"
43 INST="$HGTMP/install"
44 PYTHONDIR="$INST/lib/python"
51 cd ..
45 cd ..
52 if ${PYTHON-python} setup.py install --home="$INST" > tests/install.err 2>&1
46 if ${PYTHON-python} setup.py install --home="$INST" \
47 --install-lib="$PYTHONDIR" > tests/install.err 2>&1
53 then
48 then
54 rm tests/install.err
49 rm tests/install.err
55 else
50 else
@@ -59,8 +54,7 b' fi'
59 cd "$TESTDIR"
54 cd "$TESTDIR"
60
55
61 PATH="$INST/bin:$PATH"; export PATH
56 PATH="$INST/bin:$PATH"; export PATH
62 PYTHONPATH="$INST/$lib/python"; export PYTHONPATH
57 PYTHONPATH="$PYTHONDIR"; export PYTHONPATH
63
64
58
65 run_one() {
59 run_one() {
66 rm -f "$1.err"
60 rm -f "$1.err"
@@ -14,7 +14,7 b" echo 'import/export' >> port"
14 hg commit -m 2 -u spam -d '2 0'
14 hg commit -m 2 -u spam -d '2 0'
15 echo 'import/export' >> port
15 echo 'import/export' >> port
16 hg commit -m 3 -u eggs -d '3 0'
16 hg commit -m 3 -u eggs -d '3 0'
17 head -3 port > port1
17 head -n 3 port > port1
18 mv port1 port
18 mv port1 port
19 hg commit -m 4 -u spam -d '4 0'
19 hg commit -m 4 -u spam -d '4 0'
20 hg grep port port
20 hg grep port port
@@ -6,7 +6,7 b' basic commands (use "hg help" for the fu'
6 annotate show changeset information per file line
6 annotate show changeset information per file line
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff working directory (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 init create a new repository in the given directory
11 init create a new repository in the given directory
12 log show revision history of entire repository or files
12 log show revision history of entire repository or files
@@ -22,7 +22,7 b' basic commands (use "hg help" for the fu'
22 annotate show changeset information per file line
22 annotate show changeset information per file line
23 clone make a copy of an existing repository
23 clone make a copy of an existing repository
24 commit commit the specified files or all outstanding changes
24 commit commit the specified files or all outstanding changes
25 diff diff working directory (or selected files)
25 diff diff repository (or selected files)
26 export dump the header and diffs for one or more changesets
26 export dump the header and diffs for one or more changesets
27 init create a new repository in the given directory
27 init create a new repository in the given directory
28 log show revision history of entire repository or files
28 log show revision history of entire repository or files
@@ -46,7 +46,7 b' list of commands (use "hg help -v" to sh'
46 clone make a copy of an existing repository
46 clone make a copy of an existing repository
47 commit commit the specified files or all outstanding changes
47 commit commit the specified files or all outstanding changes
48 copy mark files as copied for the next commit
48 copy mark files as copied for the next commit
49 diff diff working directory (or selected files)
49 diff diff repository (or selected files)
50 export dump the header and diffs for one or more changesets
50 export dump the header and diffs for one or more changesets
51 forget don't add the specified files on the next commit
51 forget don't add the specified files on the next commit
52 grep search for a pattern in specified files and revisions
52 grep search for a pattern in specified files and revisions
@@ -88,7 +88,7 b' list of commands (use "hg help -v" to sh'
88 clone make a copy of an existing repository
88 clone make a copy of an existing repository
89 commit commit the specified files or all outstanding changes
89 commit commit the specified files or all outstanding changes
90 copy mark files as copied for the next commit
90 copy mark files as copied for the next commit
91 diff diff working directory (or selected files)
91 diff diff repository (or selected files)
92 export dump the header and diffs for one or more changesets
92 export dump the header and diffs for one or more changesets
93 forget don't add the specified files on the next commit
93 forget don't add the specified files on the next commit
94 grep search for a pattern in specified files and revisions
94 grep search for a pattern in specified files and revisions
@@ -130,8 +130,7 b' add the specified files on the next comm'
130
130
131 The files will be added to the repository at the next commit.
131 The files will be added to the repository at the next commit.
132
132
133 If no names are given, add all files in the current directory and
133 If no names are given, add all files in the repository.
134 its subdirectories.
135
134
136 options:
135 options:
137
136
@@ -146,8 +145,7 b' add the specified files on the next comm'
146
145
147 The files will be added to the repository at the next commit.
146 The files will be added to the repository at the next commit.
148
147
149 If no names are given, add all files in the current directory and
148 If no names are given, add all files in the repository.
150 its subdirectories.
151
149
152 options:
150 options:
153
151
@@ -155,7 +153,7 b' options:'
155 -X --exclude exclude names matching the given patterns
153 -X --exclude exclude names matching the given patterns
156 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
154 hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...
157
155
158 diff working directory (or selected files)
156 diff repository (or selected files)
159
157
160 Show differences between revisions for the specified files.
158 Show differences between revisions for the specified files.
161
159
@@ -181,9 +179,8 b' hg status [OPTION]... [FILE]...'
181
179
182 show changed files in the working directory
180 show changed files in the working directory
183
181
184 Show changed files in the working directory. If no names are
182 Show changed files in the repository. If names are
185 given, all files are shown. Otherwise, only files matching the
183 given, only files that match are shown.
186 given names are shown.
187
184
188 The codes used to show the status of files are:
185 The codes used to show the status of files are:
189 M = modified
186 M = modified
@@ -191,6 +188,8 b' show changed files in the working direct'
191 R = removed
188 R = removed
192 ? = not tracked
189 ? = not tracked
193
190
191 aliases: st
192
194 options:
193 options:
195
194
196 -m --modified show only modified files
195 -m --modified show only modified files
@@ -213,7 +212,7 b' basic commands (use "hg help" for the fu'
213 annotate show changeset information per file line
212 annotate show changeset information per file line
214 clone make a copy of an existing repository
213 clone make a copy of an existing repository
215 commit commit the specified files or all outstanding changes
214 commit commit the specified files or all outstanding changes
216 diff diff working directory (or selected files)
215 diff diff repository (or selected files)
217 export dump the header and diffs for one or more changesets
216 export dump the header and diffs for one or more changesets
218 init create a new repository in the given directory
217 init create a new repository in the given directory
219 log show revision history of entire repository or files
218 log show revision history of entire repository or files
@@ -234,7 +233,7 b' basic commands (use "hg help" for the fu'
234 annotate show changeset information per file line
233 annotate show changeset information per file line
235 clone make a copy of an existing repository
234 clone make a copy of an existing repository
236 commit commit the specified files or all outstanding changes
235 commit commit the specified files or all outstanding changes
237 diff diff working directory (or selected files)
236 diff diff repository (or selected files)
238 export dump the header and diffs for one or more changesets
237 export dump the header and diffs for one or more changesets
239 init create a new repository in the given directory
238 init create a new repository in the given directory
240 log show revision history of entire repository or files
239 log show revision history of entire repository or files
@@ -42,4 +42,4 b' echo "relglob:*" > .hgignore'
42 echo "--" ; hg status
42 echo "--" ; hg status
43
43
44 cd dir
44 cd dir
45 echo "--" ; hg status
45 echo "--" ; hg status .
@@ -1,15 +1,11 b''
1 bar: unsupported file type (type is symbolic link)
2 adding foo
1 adding foo
3 bar: unsupported file type (type is symbolic link)
4 bar: unsupported file type (type is symbolic link)
5 adding bomb
2 adding bomb
6 bar: unsupported file type (type is symbolic link)
7 adding a.c
3 adding a.c
8 adding dir/a.o
4 adding dir/a.o
9 adding dir/b.o
5 adding dir/b.o
10 a.c: unsupported file type (type is fifo)
11 dir/b.o: unsupported file type (type is symbolic link)
12 R a.c
6 R a.c
13 R dir/a.o
7 R dir/a.o
14 R dir/b.o
8 R dir/b.o
15 ? .hgignore
9 ? .hgignore
10 a.c: unsupported file type (type is fifo)
11 R a.c
@@ -11,3 +11,7 b' hg history'
11 echo foo >> .hgtags
11 echo foo >> .hgtags
12 hg tag -d "0 0" "bleah2" || echo "failed"
12 hg tag -d "0 0" "bleah2" || echo "failed"
13
13
14 hg tag -l 'xx
15 newline'
16 hg tag -l 'xx:xx'
17 true
@@ -18,3 +18,5 b' summary: test'
18
18
19 abort: working copy of .hgtags is changed (please commit .hgtags manually)
19 abort: working copy of .hgtags is changed (please commit .hgtags manually)
20 failed
20 failed
21 abort: '\n' cannot be used in a tag name
22 abort: ':' cannot be used in a tag name
@@ -20,14 +20,14 b' hg addremove'
20 hg commit -m "commit #0" -d "0 0"
20 hg commit -m "commit #0" -d "0 0"
21 hg debugwalk
21 hg debugwalk
22 cd mammals
22 cd mammals
23 hg debugwalk
23 hg debugwalk .
24 hg debugwalk Procyonidae
24 hg debugwalk Procyonidae
25 cd Procyonidae
25 cd Procyonidae
26 hg debugwalk
26 hg debugwalk .
27 hg debugwalk ..
27 hg debugwalk ..
28 cd ..
28 cd ..
29 hg debugwalk ../beans
29 hg debugwalk ../beans
30 hg debugwalk
30 hg debugwalk .
31 cd ..
31 cd ..
32 hg debugwalk -Ibeans
32 hg debugwalk -Ibeans
33 hg debugwalk 'glob:mammals/../beans/b*'
33 hg debugwalk 'glob:mammals/../beans/b*'
General Comments 0
You need to be logged in to leave comments. Login now