##// END OF EJS Templates
"default" is the default branch name
Alexis S. L. Carvalho -
r4176:f9bbcebc default
parent child Browse files
Show More
@@ -1,103 +1,107 b''
1 # changelog.py - changelog class for mercurial
1 # changelog.py - changelog class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
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 from revlog import *
8 from revlog import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import demandload
10 from demandload import demandload
11 demandload(globals(), "os time util")
11 demandload(globals(), "os time util")
12
12
13 def _string_escape(text):
13 def _string_escape(text):
14 """
14 """
15 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
15 >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)}
16 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
16 >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d
17 >>> s
17 >>> s
18 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
18 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n'
19 >>> res = _string_escape(s)
19 >>> res = _string_escape(s)
20 >>> s == _string_unescape(res)
20 >>> s == _string_unescape(res)
21 True
21 True
22 """
22 """
23 # subset of the string_escape codec
23 # subset of the string_escape codec
24 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
24 text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r')
25 return text.replace('\0', '\\0')
25 return text.replace('\0', '\\0')
26
26
27 def _string_unescape(text):
27 def _string_unescape(text):
28 return text.decode('string_escape')
28 return text.decode('string_escape')
29
29
30 class changelog(revlog):
30 class changelog(revlog):
31 def __init__(self, opener, defversion=REVLOGV0):
31 def __init__(self, opener, defversion=REVLOGV0):
32 revlog.__init__(self, opener, "00changelog.i", "00changelog.d",
32 revlog.__init__(self, opener, "00changelog.i", "00changelog.d",
33 defversion)
33 defversion)
34
34
35 def decode_extra(self, text):
35 def decode_extra(self, text):
36 extra = {}
36 extra = {}
37 for l in text.split('\0'):
37 for l in text.split('\0'):
38 if not l:
38 if not l:
39 continue
39 continue
40 k, v = _string_unescape(l).split(':', 1)
40 k, v = _string_unescape(l).split(':', 1)
41 extra[k] = v
41 extra[k] = v
42 return extra
42 return extra
43
43
44 def encode_extra(self, d):
44 def encode_extra(self, d):
45 items = [_string_escape(":".join(t)) for t in d.iteritems()]
45 items = [_string_escape(":".join(t)) for t in d.iteritems()]
46 return "\0".join(items)
46 return "\0".join(items)
47
47
48 def extract(self, text):
48 def extract(self, text):
49 """
49 """
50 format used:
50 format used:
51 nodeid\n : manifest node in ascii
51 nodeid\n : manifest node in ascii
52 user\n : user, no \n or \r allowed
52 user\n : user, no \n or \r allowed
53 time tz extra\n : date (time is int or float, timezone is int)
53 time tz extra\n : date (time is int or float, timezone is int)
54 : extra is metadatas, encoded and separated by '\0'
54 : extra is metadatas, encoded and separated by '\0'
55 : older versions ignore it
55 : older versions ignore it
56 files\n\n : files modified by the cset, no \n or \r allowed
56 files\n\n : files modified by the cset, no \n or \r allowed
57 (.*) : comment (free text, ideally utf-8)
57 (.*) : comment (free text, ideally utf-8)
58
58
59 changelog v0 doesn't use extra
59 changelog v0 doesn't use extra
60 """
60 """
61 if not text:
61 if not text:
62 return (nullid, "", (0, 0), [], "", {})
62 return (nullid, "", (0, 0), [], "", {'branch': 'default'})
63 last = text.index("\n\n")
63 last = text.index("\n\n")
64 desc = util.tolocal(text[last + 2:])
64 desc = util.tolocal(text[last + 2:])
65 l = text[:last].split('\n')
65 l = text[:last].split('\n')
66 manifest = bin(l[0])
66 manifest = bin(l[0])
67 user = util.tolocal(l[1])
67 user = util.tolocal(l[1])
68
68
69 extra_data = l[2].split(' ', 2)
69 extra_data = l[2].split(' ', 2)
70 if len(extra_data) != 3:
70 if len(extra_data) != 3:
71 time = float(extra_data.pop(0))
71 time = float(extra_data.pop(0))
72 try:
72 try:
73 # various tools did silly things with the time zone field.
73 # various tools did silly things with the time zone field.
74 timezone = int(extra_data[0])
74 timezone = int(extra_data[0])
75 except:
75 except:
76 timezone = 0
76 timezone = 0
77 extra = {}
77 extra = {}
78 else:
78 else:
79 time, timezone, extra = extra_data
79 time, timezone, extra = extra_data
80 time, timezone = float(time), int(timezone)
80 time, timezone = float(time), int(timezone)
81 extra = self.decode_extra(extra)
81 extra = self.decode_extra(extra)
82 if not extra.get('branch'):
83 extra['branch'] = 'default'
82 files = l[3:]
84 files = l[3:]
83 return (manifest, user, (time, timezone), files, desc, extra)
85 return (manifest, user, (time, timezone), files, desc, extra)
84
86
85 def read(self, node):
87 def read(self, node):
86 return self.extract(self.revision(node))
88 return self.extract(self.revision(node))
87
89
88 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
90 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
89 user=None, date=None, extra={}):
91 user=None, date=None, extra={}):
90
92
91 user, desc = util.fromlocal(user), util.fromlocal(desc)
93 user, desc = util.fromlocal(user), util.fromlocal(desc)
92
94
93 if date:
95 if date:
94 parseddate = "%d %d" % util.parsedate(date)
96 parseddate = "%d %d" % util.parsedate(date)
95 else:
97 else:
96 parseddate = "%d %d" % util.makedate()
98 parseddate = "%d %d" % util.makedate()
99 if extra and extra.get("branch") in ("default", ""):
100 del extra["branch"]
97 if extra:
101 if extra:
98 extra = self.encode_extra(extra)
102 extra = self.encode_extra(extra)
99 parseddate = "%s %s" % (parseddate, extra)
103 parseddate = "%s %s" % (parseddate, extra)
100 list.sort()
104 list.sort()
101 l = [hex(manifest), user, parseddate] + list + ["", desc]
105 l = [hex(manifest), user, parseddate] + list + ["", desc]
102 text = "\n".join(l)
106 text = "\n".join(l)
103 return self.addrevision(text, transaction, self.count(), p1, p2)
107 return self.addrevision(text, transaction, self.count(), p1, p2)
@@ -1,775 +1,776 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
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 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), 'os sys')
11 demandload(globals(), 'os sys')
12 demandload(globals(), 'mdiff util templater patch')
12 demandload(globals(), 'mdiff util templater patch')
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def revpair(repo, revs):
16 def revpair(repo, revs):
17 '''return pair of nodes, given list of revisions. second item can
17 '''return pair of nodes, given list of revisions. second item can
18 be None, meaning use working dir.'''
18 be None, meaning use working dir.'''
19
19
20 def revfix(repo, val, defval):
20 def revfix(repo, val, defval):
21 if not val and val != 0 and defval is not None:
21 if not val and val != 0 and defval is not None:
22 val = defval
22 val = defval
23 return repo.lookup(val)
23 return repo.lookup(val)
24
24
25 if not revs:
25 if not revs:
26 return repo.dirstate.parents()[0], None
26 return repo.dirstate.parents()[0], None
27 end = None
27 end = None
28 if len(revs) == 1:
28 if len(revs) == 1:
29 if revrangesep in revs[0]:
29 if revrangesep in revs[0]:
30 start, end = revs[0].split(revrangesep, 1)
30 start, end = revs[0].split(revrangesep, 1)
31 start = revfix(repo, start, 0)
31 start = revfix(repo, start, 0)
32 end = revfix(repo, end, repo.changelog.count() - 1)
32 end = revfix(repo, end, repo.changelog.count() - 1)
33 else:
33 else:
34 start = revfix(repo, revs[0], None)
34 start = revfix(repo, revs[0], None)
35 elif len(revs) == 2:
35 elif len(revs) == 2:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
37 raise util.Abort(_('too many revisions specified'))
37 raise util.Abort(_('too many revisions specified'))
38 start = revfix(repo, revs[0], None)
38 start = revfix(repo, revs[0], None)
39 end = revfix(repo, revs[1], None)
39 end = revfix(repo, revs[1], None)
40 else:
40 else:
41 raise util.Abort(_('too many revisions specified'))
41 raise util.Abort(_('too many revisions specified'))
42 return start, end
42 return start, end
43
43
44 def revrange(repo, revs):
44 def revrange(repo, revs):
45 """Yield revision as strings from a list of revision specifications."""
45 """Yield revision as strings from a list of revision specifications."""
46
46
47 def revfix(repo, val, defval):
47 def revfix(repo, val, defval):
48 if not val and val != 0 and defval is not None:
48 if not val and val != 0 and defval is not None:
49 return defval
49 return defval
50 return repo.changelog.rev(repo.lookup(val))
50 return repo.changelog.rev(repo.lookup(val))
51
51
52 seen, l = {}, []
52 seen, l = {}, []
53 for spec in revs:
53 for spec in revs:
54 if revrangesep in spec:
54 if revrangesep in spec:
55 start, end = spec.split(revrangesep, 1)
55 start, end = spec.split(revrangesep, 1)
56 start = revfix(repo, start, 0)
56 start = revfix(repo, start, 0)
57 end = revfix(repo, end, repo.changelog.count() - 1)
57 end = revfix(repo, end, repo.changelog.count() - 1)
58 step = start > end and -1 or 1
58 step = start > end and -1 or 1
59 for rev in xrange(start, end+step, step):
59 for rev in xrange(start, end+step, step):
60 if rev in seen:
60 if rev in seen:
61 continue
61 continue
62 seen[rev] = 1
62 seen[rev] = 1
63 l.append(rev)
63 l.append(rev)
64 else:
64 else:
65 rev = revfix(repo, spec, None)
65 rev = revfix(repo, spec, None)
66 if rev in seen:
66 if rev in seen:
67 continue
67 continue
68 seen[rev] = 1
68 seen[rev] = 1
69 l.append(rev)
69 l.append(rev)
70
70
71 return l
71 return l
72
72
73 def make_filename(repo, pat, node,
73 def make_filename(repo, pat, node,
74 total=None, seqno=None, revwidth=None, pathname=None):
74 total=None, seqno=None, revwidth=None, pathname=None):
75 node_expander = {
75 node_expander = {
76 'H': lambda: hex(node),
76 'H': lambda: hex(node),
77 'R': lambda: str(repo.changelog.rev(node)),
77 'R': lambda: str(repo.changelog.rev(node)),
78 'h': lambda: short(node),
78 'h': lambda: short(node),
79 }
79 }
80 expander = {
80 expander = {
81 '%': lambda: '%',
81 '%': lambda: '%',
82 'b': lambda: os.path.basename(repo.root),
82 'b': lambda: os.path.basename(repo.root),
83 }
83 }
84
84
85 try:
85 try:
86 if node:
86 if node:
87 expander.update(node_expander)
87 expander.update(node_expander)
88 if node and revwidth is not None:
88 if node and revwidth is not None:
89 expander['r'] = (lambda:
89 expander['r'] = (lambda:
90 str(repo.changelog.rev(node)).zfill(revwidth))
90 str(repo.changelog.rev(node)).zfill(revwidth))
91 if total is not None:
91 if total is not None:
92 expander['N'] = lambda: str(total)
92 expander['N'] = lambda: str(total)
93 if seqno is not None:
93 if seqno is not None:
94 expander['n'] = lambda: str(seqno)
94 expander['n'] = lambda: str(seqno)
95 if total is not None and seqno is not None:
95 if total is not None and seqno is not None:
96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
97 if pathname is not None:
97 if pathname is not None:
98 expander['s'] = lambda: os.path.basename(pathname)
98 expander['s'] = lambda: os.path.basename(pathname)
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
100 expander['p'] = lambda: pathname
100 expander['p'] = lambda: pathname
101
101
102 newname = []
102 newname = []
103 patlen = len(pat)
103 patlen = len(pat)
104 i = 0
104 i = 0
105 while i < patlen:
105 while i < patlen:
106 c = pat[i]
106 c = pat[i]
107 if c == '%':
107 if c == '%':
108 i += 1
108 i += 1
109 c = pat[i]
109 c = pat[i]
110 c = expander[c]()
110 c = expander[c]()
111 newname.append(c)
111 newname.append(c)
112 i += 1
112 i += 1
113 return ''.join(newname)
113 return ''.join(newname)
114 except KeyError, inst:
114 except KeyError, inst:
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
116 inst.args[0])
116 inst.args[0])
117
117
118 def make_file(repo, pat, node=None,
118 def make_file(repo, pat, node=None,
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
120 if not pat or pat == '-':
120 if not pat or pat == '-':
121 return 'w' in mode and sys.stdout or sys.stdin
121 return 'w' in mode and sys.stdout or sys.stdin
122 if hasattr(pat, 'write') and 'w' in mode:
122 if hasattr(pat, 'write') and 'w' in mode:
123 return pat
123 return pat
124 if hasattr(pat, 'read') and 'r' in mode:
124 if hasattr(pat, 'read') and 'r' in mode:
125 return pat
125 return pat
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
127 pathname),
127 pathname),
128 mode)
128 mode)
129
129
130 def matchpats(repo, pats=[], opts={}, head='', globbed=False):
130 def matchpats(repo, pats=[], opts={}, head='', globbed=False):
131 cwd = repo.getcwd()
131 cwd = repo.getcwd()
132 if not pats and cwd:
132 if not pats and cwd:
133 opts['include'] = [os.path.join(cwd, i)
133 opts['include'] = [os.path.join(cwd, i)
134 for i in opts.get('include', [])]
134 for i in opts.get('include', [])]
135 opts['exclude'] = [os.path.join(cwd, x)
135 opts['exclude'] = [os.path.join(cwd, x)
136 for x in opts.get('exclude', [])]
136 for x in opts.get('exclude', [])]
137 cwd = ''
137 cwd = ''
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
139 opts.get('exclude'), head, globbed=globbed)
139 opts.get('exclude'), head, globbed=globbed)
140
140
141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None,
141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None,
142 globbed=False):
142 globbed=False):
143 files, matchfn, anypats = matchpats(repo, pats, opts, head,
143 files, matchfn, anypats = matchpats(repo, pats, opts, head,
144 globbed=globbed)
144 globbed=globbed)
145 exact = dict.fromkeys(files)
145 exact = dict.fromkeys(files)
146 for src, fn in repo.walk(node=node, files=files, match=matchfn,
146 for src, fn in repo.walk(node=node, files=files, match=matchfn,
147 badmatch=badmatch):
147 badmatch=badmatch):
148 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
148 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
149
149
150 def findrenames(repo, added=None, removed=None, threshold=0.5):
150 def findrenames(repo, added=None, removed=None, threshold=0.5):
151 if added is None or removed is None:
151 if added is None or removed is None:
152 added, removed = repo.status()[1:3]
152 added, removed = repo.status()[1:3]
153 changes = repo.changelog.read(repo.dirstate.parents()[0])
153 changes = repo.changelog.read(repo.dirstate.parents()[0])
154 mf = repo.manifest.read(changes[0])
154 mf = repo.manifest.read(changes[0])
155 for a in added:
155 for a in added:
156 aa = repo.wread(a)
156 aa = repo.wread(a)
157 bestscore, bestname = None, None
157 bestscore, bestname = None, None
158 for r in removed:
158 for r in removed:
159 rr = repo.file(r).read(mf[r])
159 rr = repo.file(r).read(mf[r])
160 delta = mdiff.textdiff(aa, rr)
160 delta = mdiff.textdiff(aa, rr)
161 if len(delta) < len(aa):
161 if len(delta) < len(aa):
162 myscore = 1.0 - (float(len(delta)) / len(aa))
162 myscore = 1.0 - (float(len(delta)) / len(aa))
163 if bestscore is None or myscore > bestscore:
163 if bestscore is None or myscore > bestscore:
164 bestscore, bestname = myscore, r
164 bestscore, bestname = myscore, r
165 if bestname and bestscore >= threshold:
165 if bestname and bestscore >= threshold:
166 yield bestname, a, bestscore
166 yield bestname, a, bestscore
167
167
168 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
168 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
169 similarity=None):
169 similarity=None):
170 if dry_run is None:
170 if dry_run is None:
171 dry_run = opts.get('dry_run')
171 dry_run = opts.get('dry_run')
172 if similarity is None:
172 if similarity is None:
173 similarity = float(opts.get('similarity') or 0)
173 similarity = float(opts.get('similarity') or 0)
174 add, remove = [], []
174 add, remove = [], []
175 mapping = {}
175 mapping = {}
176 for src, abs, rel, exact in walk(repo, pats, opts):
176 for src, abs, rel, exact in walk(repo, pats, opts):
177 if src == 'f' and repo.dirstate.state(abs) == '?':
177 if src == 'f' and repo.dirstate.state(abs) == '?':
178 add.append(abs)
178 add.append(abs)
179 mapping[abs] = rel, exact
179 mapping[abs] = rel, exact
180 if repo.ui.verbose or not exact:
180 if repo.ui.verbose or not exact:
181 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
181 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
182 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
182 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
183 remove.append(abs)
183 remove.append(abs)
184 mapping[abs] = rel, exact
184 mapping[abs] = rel, exact
185 if repo.ui.verbose or not exact:
185 if repo.ui.verbose or not exact:
186 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
186 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
187 if not dry_run:
187 if not dry_run:
188 repo.add(add, wlock=wlock)
188 repo.add(add, wlock=wlock)
189 repo.remove(remove, wlock=wlock)
189 repo.remove(remove, wlock=wlock)
190 if similarity > 0:
190 if similarity > 0:
191 for old, new, score in findrenames(repo, add, remove, similarity):
191 for old, new, score in findrenames(repo, add, remove, similarity):
192 oldrel, oldexact = mapping[old]
192 oldrel, oldexact = mapping[old]
193 newrel, newexact = mapping[new]
193 newrel, newexact = mapping[new]
194 if repo.ui.verbose or not oldexact or not newexact:
194 if repo.ui.verbose or not oldexact or not newexact:
195 repo.ui.status(_('recording removal of %s as rename to %s '
195 repo.ui.status(_('recording removal of %s as rename to %s '
196 '(%d%% similar)\n') %
196 '(%d%% similar)\n') %
197 (oldrel, newrel, score * 100))
197 (oldrel, newrel, score * 100))
198 if not dry_run:
198 if not dry_run:
199 repo.copy(old, new, wlock=wlock)
199 repo.copy(old, new, wlock=wlock)
200
200
201 class changeset_printer(object):
201 class changeset_printer(object):
202 '''show changeset information when templating not requested.'''
202 '''show changeset information when templating not requested.'''
203
203
204 def __init__(self, ui, repo, patch, brinfo, buffered):
204 def __init__(self, ui, repo, patch, brinfo, buffered):
205 self.ui = ui
205 self.ui = ui
206 self.repo = repo
206 self.repo = repo
207 self.buffered = buffered
207 self.buffered = buffered
208 self.patch = patch
208 self.patch = patch
209 self.brinfo = brinfo
209 self.brinfo = brinfo
210 self.header = {}
210 self.header = {}
211 self.hunk = {}
211 self.hunk = {}
212 self.lastheader = None
212 self.lastheader = None
213
213
214 def flush(self, rev):
214 def flush(self, rev):
215 if rev in self.header:
215 if rev in self.header:
216 h = self.header[rev]
216 h = self.header[rev]
217 if h != self.lastheader:
217 if h != self.lastheader:
218 self.lastheader = h
218 self.lastheader = h
219 self.ui.write(h)
219 self.ui.write(h)
220 del self.header[rev]
220 del self.header[rev]
221 if rev in self.hunk:
221 if rev in self.hunk:
222 self.ui.write(self.hunk[rev])
222 self.ui.write(self.hunk[rev])
223 del self.hunk[rev]
223 del self.hunk[rev]
224 return 1
224 return 1
225 return 0
225 return 0
226
226
227 def show(self, rev=0, changenode=None, copies=None, **props):
227 def show(self, rev=0, changenode=None, copies=None, **props):
228 if self.buffered:
228 if self.buffered:
229 self.ui.pushbuffer()
229 self.ui.pushbuffer()
230 self._show(rev, changenode, copies, props)
230 self._show(rev, changenode, copies, props)
231 self.hunk[rev] = self.ui.popbuffer()
231 self.hunk[rev] = self.ui.popbuffer()
232 else:
232 else:
233 self._show(rev, changenode, copies, props)
233 self._show(rev, changenode, copies, props)
234
234
235 def _show(self, rev, changenode, copies, props):
235 def _show(self, rev, changenode, copies, props):
236 '''show a single changeset or file revision'''
236 '''show a single changeset or file revision'''
237 log = self.repo.changelog
237 log = self.repo.changelog
238 if changenode is None:
238 if changenode is None:
239 changenode = log.node(rev)
239 changenode = log.node(rev)
240 elif not rev:
240 elif not rev:
241 rev = log.rev(changenode)
241 rev = log.rev(changenode)
242
242
243 if self.ui.quiet:
243 if self.ui.quiet:
244 self.ui.write("%d:%s\n" % (rev, short(changenode)))
244 self.ui.write("%d:%s\n" % (rev, short(changenode)))
245 return
245 return
246
246
247 changes = log.read(changenode)
247 changes = log.read(changenode)
248 date = util.datestr(changes[2])
248 date = util.datestr(changes[2])
249 extra = changes[5]
249 extra = changes[5]
250 branch = extra.get("branch")
250 branch = extra.get("branch")
251
251
252 hexfunc = self.ui.debugflag and hex or short
252 hexfunc = self.ui.debugflag and hex or short
253
253
254 parents = log.parentrevs(rev)
254 parents = log.parentrevs(rev)
255 if not self.ui.debugflag:
255 if not self.ui.debugflag:
256 if parents[1] == nullrev:
256 if parents[1] == nullrev:
257 if parents[0] >= rev - 1:
257 if parents[0] >= rev - 1:
258 parents = []
258 parents = []
259 else:
259 else:
260 parents = [parents[0]]
260 parents = [parents[0]]
261 parents = [(p, hexfunc(log.node(p))) for p in parents]
261 parents = [(p, hexfunc(log.node(p))) for p in parents]
262
262
263 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
263 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
264
264
265 if branch:
265 # don't show the default branch name
266 if branch != 'default':
266 branch = util.tolocal(branch)
267 branch = util.tolocal(branch)
267 self.ui.write(_("branch: %s\n") % branch)
268 self.ui.write(_("branch: %s\n") % branch)
268 for tag in self.repo.nodetags(changenode):
269 for tag in self.repo.nodetags(changenode):
269 self.ui.write(_("tag: %s\n") % tag)
270 self.ui.write(_("tag: %s\n") % tag)
270 for parent in parents:
271 for parent in parents:
271 self.ui.write(_("parent: %d:%s\n") % parent)
272 self.ui.write(_("parent: %d:%s\n") % parent)
272
273
273 if self.brinfo:
274 if self.brinfo:
274 br = self.repo.branchlookup([changenode])
275 br = self.repo.branchlookup([changenode])
275 if br:
276 if br:
276 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
277 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
277
278
278 if self.ui.debugflag:
279 if self.ui.debugflag:
279 self.ui.write(_("manifest: %d:%s\n") %
280 self.ui.write(_("manifest: %d:%s\n") %
280 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
281 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
281 self.ui.write(_("user: %s\n") % changes[1])
282 self.ui.write(_("user: %s\n") % changes[1])
282 self.ui.write(_("date: %s\n") % date)
283 self.ui.write(_("date: %s\n") % date)
283
284
284 if self.ui.debugflag:
285 if self.ui.debugflag:
285 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
286 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
286 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
287 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
287 files):
288 files):
288 if value:
289 if value:
289 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
290 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
290 elif changes[3] and self.ui.verbose:
291 elif changes[3] and self.ui.verbose:
291 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
292 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
292 if copies and self.ui.verbose:
293 if copies and self.ui.verbose:
293 copies = ['%s (%s)' % c for c in copies]
294 copies = ['%s (%s)' % c for c in copies]
294 self.ui.write(_("copies: %s\n") % ' '.join(copies))
295 self.ui.write(_("copies: %s\n") % ' '.join(copies))
295
296
296 if extra and self.ui.debugflag:
297 if extra and self.ui.debugflag:
297 extraitems = extra.items()
298 extraitems = extra.items()
298 extraitems.sort()
299 extraitems.sort()
299 for key, value in extraitems:
300 for key, value in extraitems:
300 self.ui.write(_("extra: %s=%s\n")
301 self.ui.write(_("extra: %s=%s\n")
301 % (key, value.encode('string_escape')))
302 % (key, value.encode('string_escape')))
302
303
303 description = changes[4].strip()
304 description = changes[4].strip()
304 if description:
305 if description:
305 if self.ui.verbose:
306 if self.ui.verbose:
306 self.ui.write(_("description:\n"))
307 self.ui.write(_("description:\n"))
307 self.ui.write(description)
308 self.ui.write(description)
308 self.ui.write("\n\n")
309 self.ui.write("\n\n")
309 else:
310 else:
310 self.ui.write(_("summary: %s\n") %
311 self.ui.write(_("summary: %s\n") %
311 description.splitlines()[0])
312 description.splitlines()[0])
312 self.ui.write("\n")
313 self.ui.write("\n")
313
314
314 self.showpatch(changenode)
315 self.showpatch(changenode)
315
316
316 def showpatch(self, node):
317 def showpatch(self, node):
317 if self.patch:
318 if self.patch:
318 prev = self.repo.changelog.parents(node)[0]
319 prev = self.repo.changelog.parents(node)[0]
319 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
320 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
320 self.ui.write("\n")
321 self.ui.write("\n")
321
322
322 class changeset_templater(changeset_printer):
323 class changeset_templater(changeset_printer):
323 '''format changeset information.'''
324 '''format changeset information.'''
324
325
325 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
326 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
326 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
327 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
327 self.t = templater.templater(mapfile, templater.common_filters,
328 self.t = templater.templater(mapfile, templater.common_filters,
328 cache={'parent': '{rev}:{node|short} ',
329 cache={'parent': '{rev}:{node|short} ',
329 'manifest': '{rev}:{node|short}',
330 'manifest': '{rev}:{node|short}',
330 'filecopy': '{name} ({source})'})
331 'filecopy': '{name} ({source})'})
331
332
332 def use_template(self, t):
333 def use_template(self, t):
333 '''set template string to use'''
334 '''set template string to use'''
334 self.t.cache['changeset'] = t
335 self.t.cache['changeset'] = t
335
336
336 def _show(self, rev, changenode, copies, props):
337 def _show(self, rev, changenode, copies, props):
337 '''show a single changeset or file revision'''
338 '''show a single changeset or file revision'''
338 log = self.repo.changelog
339 log = self.repo.changelog
339 if changenode is None:
340 if changenode is None:
340 changenode = log.node(rev)
341 changenode = log.node(rev)
341 elif not rev:
342 elif not rev:
342 rev = log.rev(changenode)
343 rev = log.rev(changenode)
343
344
344 changes = log.read(changenode)
345 changes = log.read(changenode)
345
346
346 def showlist(name, values, plural=None, **args):
347 def showlist(name, values, plural=None, **args):
347 '''expand set of values.
348 '''expand set of values.
348 name is name of key in template map.
349 name is name of key in template map.
349 values is list of strings or dicts.
350 values is list of strings or dicts.
350 plural is plural of name, if not simply name + 's'.
351 plural is plural of name, if not simply name + 's'.
351
352
352 expansion works like this, given name 'foo'.
353 expansion works like this, given name 'foo'.
353
354
354 if values is empty, expand 'no_foos'.
355 if values is empty, expand 'no_foos'.
355
356
356 if 'foo' not in template map, return values as a string,
357 if 'foo' not in template map, return values as a string,
357 joined by space.
358 joined by space.
358
359
359 expand 'start_foos'.
360 expand 'start_foos'.
360
361
361 for each value, expand 'foo'. if 'last_foo' in template
362 for each value, expand 'foo'. if 'last_foo' in template
362 map, expand it instead of 'foo' for last key.
363 map, expand it instead of 'foo' for last key.
363
364
364 expand 'end_foos'.
365 expand 'end_foos'.
365 '''
366 '''
366 if plural: names = plural
367 if plural: names = plural
367 else: names = name + 's'
368 else: names = name + 's'
368 if not values:
369 if not values:
369 noname = 'no_' + names
370 noname = 'no_' + names
370 if noname in self.t:
371 if noname in self.t:
371 yield self.t(noname, **args)
372 yield self.t(noname, **args)
372 return
373 return
373 if name not in self.t:
374 if name not in self.t:
374 if isinstance(values[0], str):
375 if isinstance(values[0], str):
375 yield ' '.join(values)
376 yield ' '.join(values)
376 else:
377 else:
377 for v in values:
378 for v in values:
378 yield dict(v, **args)
379 yield dict(v, **args)
379 return
380 return
380 startname = 'start_' + names
381 startname = 'start_' + names
381 if startname in self.t:
382 if startname in self.t:
382 yield self.t(startname, **args)
383 yield self.t(startname, **args)
383 vargs = args.copy()
384 vargs = args.copy()
384 def one(v, tag=name):
385 def one(v, tag=name):
385 try:
386 try:
386 vargs.update(v)
387 vargs.update(v)
387 except (AttributeError, ValueError):
388 except (AttributeError, ValueError):
388 try:
389 try:
389 for a, b in v:
390 for a, b in v:
390 vargs[a] = b
391 vargs[a] = b
391 except ValueError:
392 except ValueError:
392 vargs[name] = v
393 vargs[name] = v
393 return self.t(tag, **vargs)
394 return self.t(tag, **vargs)
394 lastname = 'last_' + name
395 lastname = 'last_' + name
395 if lastname in self.t:
396 if lastname in self.t:
396 last = values.pop()
397 last = values.pop()
397 else:
398 else:
398 last = None
399 last = None
399 for v in values:
400 for v in values:
400 yield one(v)
401 yield one(v)
401 if last is not None:
402 if last is not None:
402 yield one(last, tag=lastname)
403 yield one(last, tag=lastname)
403 endname = 'end_' + names
404 endname = 'end_' + names
404 if endname in self.t:
405 if endname in self.t:
405 yield self.t(endname, **args)
406 yield self.t(endname, **args)
406
407
407 def showbranches(**args):
408 def showbranches(**args):
408 branch = changes[5].get("branch")
409 branch = changes[5].get("branch")
409 if branch:
410 if branch != 'default':
410 branch = util.tolocal(branch)
411 branch = util.tolocal(branch)
411 return showlist('branch', [branch], plural='branches', **args)
412 return showlist('branch', [branch], plural='branches', **args)
412 # add old style branches if requested
413 # add old style branches if requested
413 if self.brinfo:
414 if self.brinfo:
414 br = self.repo.branchlookup([changenode])
415 br = self.repo.branchlookup([changenode])
415 if changenode in br:
416 if changenode in br:
416 return showlist('branch', br[changenode],
417 return showlist('branch', br[changenode],
417 plural='branches', **args)
418 plural='branches', **args)
418
419
419 def showparents(**args):
420 def showparents(**args):
420 parents = [[('rev', log.rev(p)), ('node', hex(p))]
421 parents = [[('rev', log.rev(p)), ('node', hex(p))]
421 for p in log.parents(changenode)
422 for p in log.parents(changenode)
422 if self.ui.debugflag or p != nullid]
423 if self.ui.debugflag or p != nullid]
423 if (not self.ui.debugflag and len(parents) == 1 and
424 if (not self.ui.debugflag and len(parents) == 1 and
424 parents[0][0][1] == rev - 1):
425 parents[0][0][1] == rev - 1):
425 return
426 return
426 return showlist('parent', parents, **args)
427 return showlist('parent', parents, **args)
427
428
428 def showtags(**args):
429 def showtags(**args):
429 return showlist('tag', self.repo.nodetags(changenode), **args)
430 return showlist('tag', self.repo.nodetags(changenode), **args)
430
431
431 def showextras(**args):
432 def showextras(**args):
432 extras = changes[5].items()
433 extras = changes[5].items()
433 extras.sort()
434 extras.sort()
434 for key, value in extras:
435 for key, value in extras:
435 args = args.copy()
436 args = args.copy()
436 args.update(dict(key=key, value=value))
437 args.update(dict(key=key, value=value))
437 yield self.t('extra', **args)
438 yield self.t('extra', **args)
438
439
439 def showcopies(**args):
440 def showcopies(**args):
440 c = [{'name': x[0], 'source': x[1]} for x in copies]
441 c = [{'name': x[0], 'source': x[1]} for x in copies]
441 return showlist('file_copy', c, plural='file_copies', **args)
442 return showlist('file_copy', c, plural='file_copies', **args)
442
443
443 if self.ui.debugflag:
444 if self.ui.debugflag:
444 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
445 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
445 def showfiles(**args):
446 def showfiles(**args):
446 return showlist('file', files[0], **args)
447 return showlist('file', files[0], **args)
447 def showadds(**args):
448 def showadds(**args):
448 return showlist('file_add', files[1], **args)
449 return showlist('file_add', files[1], **args)
449 def showdels(**args):
450 def showdels(**args):
450 return showlist('file_del', files[2], **args)
451 return showlist('file_del', files[2], **args)
451 def showmanifest(**args):
452 def showmanifest(**args):
452 args = args.copy()
453 args = args.copy()
453 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
454 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
454 node=hex(changes[0])))
455 node=hex(changes[0])))
455 return self.t('manifest', **args)
456 return self.t('manifest', **args)
456 else:
457 else:
457 def showfiles(**args):
458 def showfiles(**args):
458 return showlist('file', changes[3], **args)
459 return showlist('file', changes[3], **args)
459 showadds = ''
460 showadds = ''
460 showdels = ''
461 showdels = ''
461 showmanifest = ''
462 showmanifest = ''
462
463
463 defprops = {
464 defprops = {
464 'author': changes[1],
465 'author': changes[1],
465 'branches': showbranches,
466 'branches': showbranches,
466 'date': changes[2],
467 'date': changes[2],
467 'desc': changes[4],
468 'desc': changes[4],
468 'file_adds': showadds,
469 'file_adds': showadds,
469 'file_dels': showdels,
470 'file_dels': showdels,
470 'files': showfiles,
471 'files': showfiles,
471 'file_copies': showcopies,
472 'file_copies': showcopies,
472 'manifest': showmanifest,
473 'manifest': showmanifest,
473 'node': hex(changenode),
474 'node': hex(changenode),
474 'parents': showparents,
475 'parents': showparents,
475 'rev': rev,
476 'rev': rev,
476 'tags': showtags,
477 'tags': showtags,
477 'extras': showextras,
478 'extras': showextras,
478 }
479 }
479 props = props.copy()
480 props = props.copy()
480 props.update(defprops)
481 props.update(defprops)
481
482
482 try:
483 try:
483 if self.ui.debugflag and 'header_debug' in self.t:
484 if self.ui.debugflag and 'header_debug' in self.t:
484 key = 'header_debug'
485 key = 'header_debug'
485 elif self.ui.quiet and 'header_quiet' in self.t:
486 elif self.ui.quiet and 'header_quiet' in self.t:
486 key = 'header_quiet'
487 key = 'header_quiet'
487 elif self.ui.verbose and 'header_verbose' in self.t:
488 elif self.ui.verbose and 'header_verbose' in self.t:
488 key = 'header_verbose'
489 key = 'header_verbose'
489 elif 'header' in self.t:
490 elif 'header' in self.t:
490 key = 'header'
491 key = 'header'
491 else:
492 else:
492 key = ''
493 key = ''
493 if key:
494 if key:
494 h = templater.stringify(self.t(key, **props))
495 h = templater.stringify(self.t(key, **props))
495 if self.buffered:
496 if self.buffered:
496 self.header[rev] = h
497 self.header[rev] = h
497 else:
498 else:
498 self.ui.write(h)
499 self.ui.write(h)
499 if self.ui.debugflag and 'changeset_debug' in self.t:
500 if self.ui.debugflag and 'changeset_debug' in self.t:
500 key = 'changeset_debug'
501 key = 'changeset_debug'
501 elif self.ui.quiet and 'changeset_quiet' in self.t:
502 elif self.ui.quiet and 'changeset_quiet' in self.t:
502 key = 'changeset_quiet'
503 key = 'changeset_quiet'
503 elif self.ui.verbose and 'changeset_verbose' in self.t:
504 elif self.ui.verbose and 'changeset_verbose' in self.t:
504 key = 'changeset_verbose'
505 key = 'changeset_verbose'
505 else:
506 else:
506 key = 'changeset'
507 key = 'changeset'
507 self.ui.write(templater.stringify(self.t(key, **props)))
508 self.ui.write(templater.stringify(self.t(key, **props)))
508 self.showpatch(changenode)
509 self.showpatch(changenode)
509 except KeyError, inst:
510 except KeyError, inst:
510 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
511 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
511 inst.args[0]))
512 inst.args[0]))
512 except SyntaxError, inst:
513 except SyntaxError, inst:
513 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
514 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
514
515
515 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
516 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
516 """show one changeset using template or regular display.
517 """show one changeset using template or regular display.
517
518
518 Display format will be the first non-empty hit of:
519 Display format will be the first non-empty hit of:
519 1. option 'template'
520 1. option 'template'
520 2. option 'style'
521 2. option 'style'
521 3. [ui] setting 'logtemplate'
522 3. [ui] setting 'logtemplate'
522 4. [ui] setting 'style'
523 4. [ui] setting 'style'
523 If all of these values are either the unset or the empty string,
524 If all of these values are either the unset or the empty string,
524 regular display via changeset_printer() is done.
525 regular display via changeset_printer() is done.
525 """
526 """
526 # options
527 # options
527 patch = False
528 patch = False
528 if opts.get('patch'):
529 if opts.get('patch'):
529 patch = matchfn or util.always
530 patch = matchfn or util.always
530
531
531 br = None
532 br = None
532 if opts.get('branches'):
533 if opts.get('branches'):
533 ui.warn(_("the --branches option is deprecated, "
534 ui.warn(_("the --branches option is deprecated, "
534 "please use 'hg branches' instead\n"))
535 "please use 'hg branches' instead\n"))
535 br = True
536 br = True
536 tmpl = opts.get('template')
537 tmpl = opts.get('template')
537 mapfile = None
538 mapfile = None
538 if tmpl:
539 if tmpl:
539 tmpl = templater.parsestring(tmpl, quoted=False)
540 tmpl = templater.parsestring(tmpl, quoted=False)
540 else:
541 else:
541 mapfile = opts.get('style')
542 mapfile = opts.get('style')
542 # ui settings
543 # ui settings
543 if not mapfile:
544 if not mapfile:
544 tmpl = ui.config('ui', 'logtemplate')
545 tmpl = ui.config('ui', 'logtemplate')
545 if tmpl:
546 if tmpl:
546 tmpl = templater.parsestring(tmpl)
547 tmpl = templater.parsestring(tmpl)
547 else:
548 else:
548 mapfile = ui.config('ui', 'style')
549 mapfile = ui.config('ui', 'style')
549
550
550 if tmpl or mapfile:
551 if tmpl or mapfile:
551 if mapfile:
552 if mapfile:
552 if not os.path.split(mapfile)[0]:
553 if not os.path.split(mapfile)[0]:
553 mapname = (templater.templatepath('map-cmdline.' + mapfile)
554 mapname = (templater.templatepath('map-cmdline.' + mapfile)
554 or templater.templatepath(mapfile))
555 or templater.templatepath(mapfile))
555 if mapname: mapfile = mapname
556 if mapname: mapfile = mapname
556 try:
557 try:
557 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
558 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
558 except SyntaxError, inst:
559 except SyntaxError, inst:
559 raise util.Abort(inst.args[0])
560 raise util.Abort(inst.args[0])
560 if tmpl: t.use_template(tmpl)
561 if tmpl: t.use_template(tmpl)
561 return t
562 return t
562 return changeset_printer(ui, repo, patch, br, buffered)
563 return changeset_printer(ui, repo, patch, br, buffered)
563
564
564 def finddate(ui, repo, date):
565 def finddate(ui, repo, date):
565 """Find the tipmost changeset that matches the given date spec"""
566 """Find the tipmost changeset that matches the given date spec"""
566 df = util.matchdate(date + " to " + date)
567 df = util.matchdate(date + " to " + date)
567 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
568 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
568 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
569 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
569 results = {}
570 results = {}
570 for st, rev, fns in changeiter:
571 for st, rev, fns in changeiter:
571 if st == 'add':
572 if st == 'add':
572 d = get(rev)[2]
573 d = get(rev)[2]
573 if df(d[0]):
574 if df(d[0]):
574 results[rev] = d
575 results[rev] = d
575 elif st == 'iter':
576 elif st == 'iter':
576 if rev in results:
577 if rev in results:
577 ui.status("Found revision %s from %s\n" %
578 ui.status("Found revision %s from %s\n" %
578 (rev, util.datestr(results[rev])))
579 (rev, util.datestr(results[rev])))
579 return str(rev)
580 return str(rev)
580
581
581 raise util.Abort(_("revision matching date not found"))
582 raise util.Abort(_("revision matching date not found"))
582
583
583 def walkchangerevs(ui, repo, pats, change, opts):
584 def walkchangerevs(ui, repo, pats, change, opts):
584 '''Iterate over files and the revs they changed in.
585 '''Iterate over files and the revs they changed in.
585
586
586 Callers most commonly need to iterate backwards over the history
587 Callers most commonly need to iterate backwards over the history
587 it is interested in. Doing so has awful (quadratic-looking)
588 it is interested in. Doing so has awful (quadratic-looking)
588 performance, so we use iterators in a "windowed" way.
589 performance, so we use iterators in a "windowed" way.
589
590
590 We walk a window of revisions in the desired order. Within the
591 We walk a window of revisions in the desired order. Within the
591 window, we first walk forwards to gather data, then in the desired
592 window, we first walk forwards to gather data, then in the desired
592 order (usually backwards) to display it.
593 order (usually backwards) to display it.
593
594
594 This function returns an (iterator, matchfn) tuple. The iterator
595 This function returns an (iterator, matchfn) tuple. The iterator
595 yields 3-tuples. They will be of one of the following forms:
596 yields 3-tuples. They will be of one of the following forms:
596
597
597 "window", incrementing, lastrev: stepping through a window,
598 "window", incrementing, lastrev: stepping through a window,
598 positive if walking forwards through revs, last rev in the
599 positive if walking forwards through revs, last rev in the
599 sequence iterated over - use to reset state for the current window
600 sequence iterated over - use to reset state for the current window
600
601
601 "add", rev, fns: out-of-order traversal of the given file names
602 "add", rev, fns: out-of-order traversal of the given file names
602 fns, which changed during revision rev - use to gather data for
603 fns, which changed during revision rev - use to gather data for
603 possible display
604 possible display
604
605
605 "iter", rev, None: in-order traversal of the revs earlier iterated
606 "iter", rev, None: in-order traversal of the revs earlier iterated
606 over with "add" - use to display data'''
607 over with "add" - use to display data'''
607
608
608 def increasing_windows(start, end, windowsize=8, sizelimit=512):
609 def increasing_windows(start, end, windowsize=8, sizelimit=512):
609 if start < end:
610 if start < end:
610 while start < end:
611 while start < end:
611 yield start, min(windowsize, end-start)
612 yield start, min(windowsize, end-start)
612 start += windowsize
613 start += windowsize
613 if windowsize < sizelimit:
614 if windowsize < sizelimit:
614 windowsize *= 2
615 windowsize *= 2
615 else:
616 else:
616 while start > end:
617 while start > end:
617 yield start, min(windowsize, start-end-1)
618 yield start, min(windowsize, start-end-1)
618 start -= windowsize
619 start -= windowsize
619 if windowsize < sizelimit:
620 if windowsize < sizelimit:
620 windowsize *= 2
621 windowsize *= 2
621
622
622 files, matchfn, anypats = matchpats(repo, pats, opts)
623 files, matchfn, anypats = matchpats(repo, pats, opts)
623 follow = opts.get('follow') or opts.get('follow_first')
624 follow = opts.get('follow') or opts.get('follow_first')
624
625
625 if repo.changelog.count() == 0:
626 if repo.changelog.count() == 0:
626 return [], matchfn
627 return [], matchfn
627
628
628 if follow:
629 if follow:
629 defrange = '%s:0' % repo.changectx().rev()
630 defrange = '%s:0' % repo.changectx().rev()
630 else:
631 else:
631 defrange = 'tip:0'
632 defrange = 'tip:0'
632 revs = revrange(repo, opts['rev'] or [defrange])
633 revs = revrange(repo, opts['rev'] or [defrange])
633 wanted = {}
634 wanted = {}
634 slowpath = anypats or opts.get('removed')
635 slowpath = anypats or opts.get('removed')
635 fncache = {}
636 fncache = {}
636
637
637 if not slowpath and not files:
638 if not slowpath and not files:
638 # No files, no patterns. Display all revs.
639 # No files, no patterns. Display all revs.
639 wanted = dict.fromkeys(revs)
640 wanted = dict.fromkeys(revs)
640 copies = []
641 copies = []
641 if not slowpath:
642 if not slowpath:
642 # Only files, no patterns. Check the history of each file.
643 # Only files, no patterns. Check the history of each file.
643 def filerevgen(filelog, node):
644 def filerevgen(filelog, node):
644 cl_count = repo.changelog.count()
645 cl_count = repo.changelog.count()
645 if node is None:
646 if node is None:
646 last = filelog.count() - 1
647 last = filelog.count() - 1
647 else:
648 else:
648 last = filelog.rev(node)
649 last = filelog.rev(node)
649 for i, window in increasing_windows(last, nullrev):
650 for i, window in increasing_windows(last, nullrev):
650 revs = []
651 revs = []
651 for j in xrange(i - window, i + 1):
652 for j in xrange(i - window, i + 1):
652 n = filelog.node(j)
653 n = filelog.node(j)
653 revs.append((filelog.linkrev(n),
654 revs.append((filelog.linkrev(n),
654 follow and filelog.renamed(n)))
655 follow and filelog.renamed(n)))
655 revs.reverse()
656 revs.reverse()
656 for rev in revs:
657 for rev in revs:
657 # only yield rev for which we have the changelog, it can
658 # only yield rev for which we have the changelog, it can
658 # happen while doing "hg log" during a pull or commit
659 # happen while doing "hg log" during a pull or commit
659 if rev[0] < cl_count:
660 if rev[0] < cl_count:
660 yield rev
661 yield rev
661 def iterfiles():
662 def iterfiles():
662 for filename in files:
663 for filename in files:
663 yield filename, None
664 yield filename, None
664 for filename_node in copies:
665 for filename_node in copies:
665 yield filename_node
666 yield filename_node
666 minrev, maxrev = min(revs), max(revs)
667 minrev, maxrev = min(revs), max(revs)
667 for file_, node in iterfiles():
668 for file_, node in iterfiles():
668 filelog = repo.file(file_)
669 filelog = repo.file(file_)
669 # A zero count may be a directory or deleted file, so
670 # A zero count may be a directory or deleted file, so
670 # try to find matching entries on the slow path.
671 # try to find matching entries on the slow path.
671 if filelog.count() == 0:
672 if filelog.count() == 0:
672 slowpath = True
673 slowpath = True
673 break
674 break
674 for rev, copied in filerevgen(filelog, node):
675 for rev, copied in filerevgen(filelog, node):
675 if rev <= maxrev:
676 if rev <= maxrev:
676 if rev < minrev:
677 if rev < minrev:
677 break
678 break
678 fncache.setdefault(rev, [])
679 fncache.setdefault(rev, [])
679 fncache[rev].append(file_)
680 fncache[rev].append(file_)
680 wanted[rev] = 1
681 wanted[rev] = 1
681 if follow and copied:
682 if follow and copied:
682 copies.append(copied)
683 copies.append(copied)
683 if slowpath:
684 if slowpath:
684 if follow:
685 if follow:
685 raise util.Abort(_('can only follow copies/renames for explicit '
686 raise util.Abort(_('can only follow copies/renames for explicit '
686 'file names'))
687 'file names'))
687
688
688 # The slow path checks files modified in every changeset.
689 # The slow path checks files modified in every changeset.
689 def changerevgen():
690 def changerevgen():
690 for i, window in increasing_windows(repo.changelog.count()-1,
691 for i, window in increasing_windows(repo.changelog.count()-1,
691 nullrev):
692 nullrev):
692 for j in xrange(i - window, i + 1):
693 for j in xrange(i - window, i + 1):
693 yield j, change(j)[3]
694 yield j, change(j)[3]
694
695
695 for rev, changefiles in changerevgen():
696 for rev, changefiles in changerevgen():
696 matches = filter(matchfn, changefiles)
697 matches = filter(matchfn, changefiles)
697 if matches:
698 if matches:
698 fncache[rev] = matches
699 fncache[rev] = matches
699 wanted[rev] = 1
700 wanted[rev] = 1
700
701
701 class followfilter:
702 class followfilter:
702 def __init__(self, onlyfirst=False):
703 def __init__(self, onlyfirst=False):
703 self.startrev = nullrev
704 self.startrev = nullrev
704 self.roots = []
705 self.roots = []
705 self.onlyfirst = onlyfirst
706 self.onlyfirst = onlyfirst
706
707
707 def match(self, rev):
708 def match(self, rev):
708 def realparents(rev):
709 def realparents(rev):
709 if self.onlyfirst:
710 if self.onlyfirst:
710 return repo.changelog.parentrevs(rev)[0:1]
711 return repo.changelog.parentrevs(rev)[0:1]
711 else:
712 else:
712 return filter(lambda x: x != nullrev,
713 return filter(lambda x: x != nullrev,
713 repo.changelog.parentrevs(rev))
714 repo.changelog.parentrevs(rev))
714
715
715 if self.startrev == nullrev:
716 if self.startrev == nullrev:
716 self.startrev = rev
717 self.startrev = rev
717 return True
718 return True
718
719
719 if rev > self.startrev:
720 if rev > self.startrev:
720 # forward: all descendants
721 # forward: all descendants
721 if not self.roots:
722 if not self.roots:
722 self.roots.append(self.startrev)
723 self.roots.append(self.startrev)
723 for parent in realparents(rev):
724 for parent in realparents(rev):
724 if parent in self.roots:
725 if parent in self.roots:
725 self.roots.append(rev)
726 self.roots.append(rev)
726 return True
727 return True
727 else:
728 else:
728 # backwards: all parents
729 # backwards: all parents
729 if not self.roots:
730 if not self.roots:
730 self.roots.extend(realparents(self.startrev))
731 self.roots.extend(realparents(self.startrev))
731 if rev in self.roots:
732 if rev in self.roots:
732 self.roots.remove(rev)
733 self.roots.remove(rev)
733 self.roots.extend(realparents(rev))
734 self.roots.extend(realparents(rev))
734 return True
735 return True
735
736
736 return False
737 return False
737
738
738 # it might be worthwhile to do this in the iterator if the rev range
739 # it might be worthwhile to do this in the iterator if the rev range
739 # is descending and the prune args are all within that range
740 # is descending and the prune args are all within that range
740 for rev in opts.get('prune', ()):
741 for rev in opts.get('prune', ()):
741 rev = repo.changelog.rev(repo.lookup(rev))
742 rev = repo.changelog.rev(repo.lookup(rev))
742 ff = followfilter()
743 ff = followfilter()
743 stop = min(revs[0], revs[-1])
744 stop = min(revs[0], revs[-1])
744 for x in xrange(rev, stop-1, -1):
745 for x in xrange(rev, stop-1, -1):
745 if ff.match(x) and x in wanted:
746 if ff.match(x) and x in wanted:
746 del wanted[x]
747 del wanted[x]
747
748
748 def iterate():
749 def iterate():
749 if follow and not files:
750 if follow and not files:
750 ff = followfilter(onlyfirst=opts.get('follow_first'))
751 ff = followfilter(onlyfirst=opts.get('follow_first'))
751 def want(rev):
752 def want(rev):
752 if ff.match(rev) and rev in wanted:
753 if ff.match(rev) and rev in wanted:
753 return True
754 return True
754 return False
755 return False
755 else:
756 else:
756 def want(rev):
757 def want(rev):
757 return rev in wanted
758 return rev in wanted
758
759
759 for i, window in increasing_windows(0, len(revs)):
760 for i, window in increasing_windows(0, len(revs)):
760 yield 'window', revs[0] < revs[-1], revs[-1]
761 yield 'window', revs[0] < revs[-1], revs[-1]
761 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
762 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
762 srevs = list(nrevs)
763 srevs = list(nrevs)
763 srevs.sort()
764 srevs.sort()
764 for rev in srevs:
765 for rev in srevs:
765 fns = fncache.get(rev)
766 fns = fncache.get(rev)
766 if not fns:
767 if not fns:
767 def fns_generator():
768 def fns_generator():
768 for f in change(rev)[3]:
769 for f in change(rev)[3]:
769 if matchfn(f):
770 if matchfn(f):
770 yield f
771 yield f
771 fns = fns_generator()
772 fns = fns_generator()
772 yield 'add', rev, fns
773 yield 'add', rev, fns
773 for rev in nrevs:
774 for rev in nrevs:
774 yield 'iter', rev, None
775 yield 'iter', rev, None
775 return iterate(), matchfn
776 return iterate(), matchfn
@@ -1,3344 +1,3344 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
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 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
11 demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat")
12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo")
13 demandload(globals(), "difflib patch time help mdiff tempfile")
13 demandload(globals(), "difflib patch time help mdiff tempfile")
14 demandload(globals(), "traceback errno version atexit socket")
14 demandload(globals(), "traceback errno version atexit socket")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
15 demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver")
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, rev, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202
202
203 bail_if_changed(repo)
203 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
204 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
205 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
206 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
207 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
208 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
209 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
210 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
211 if p2 != nullid:
212 if not opts['parent']:
212 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
213 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
214 '--parent'))
215 p = repo.lookup(opts['parent'])
215 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
216 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
217 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
218 (short(p), short(node)))
219 parent = p
219 parent = p
220 else:
220 else:
221 if opts['parent']:
221 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
223 parent = p1
224 hg.clean(repo, node, show_stats=False)
224 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
225 revert_opts = opts.copy()
226 revert_opts['date'] = None
226 revert_opts['date'] = None
227 revert_opts['all'] = True
227 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
228 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
229 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
230 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
231 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
232 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
234 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
235 commit(ui, repo, **commit_opts)
236 def nice(node):
236 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
238 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
239 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
240 if op1 != node:
241 if opts['merge']:
241 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 n = _lookup(repo, hex(op1))
243 n = _lookup(repo, hex(op1))
244 hg.merge(repo, n)
244 hg.merge(repo, n)
245 else:
245 else:
246 ui.status(_('the backout changeset is a new head - '
246 ui.status(_('the backout changeset is a new head - '
247 'do not forget to merge\n'))
247 'do not forget to merge\n'))
248 ui.status(_('(use "backout --merge" '
248 ui.status(_('(use "backout --merge" '
249 'if you want to auto-merge)\n'))
249 'if you want to auto-merge)\n'))
250
250
251 def branch(ui, repo, label=None):
251 def branch(ui, repo, label=None):
252 """set or show the current branch name
252 """set or show the current branch name
253
253
254 With <name>, set the current branch name. Otherwise, show the
254 With <name>, set the current branch name. Otherwise, show the
255 current branch name.
255 current branch name.
256 """
256 """
257
257
258 if label is not None:
258 if label is not None:
259 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
259 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
260 else:
260 else:
261 b = util.tolocal(repo.workingctx().branch())
261 b = util.tolocal(repo.workingctx().branch())
262 if b:
262 if b:
263 ui.write("%s\n" % b)
263 ui.write("%s\n" % b)
264
264
265 def branches(ui, repo):
265 def branches(ui, repo):
266 """list repository named branches
266 """list repository named branches
267
267
268 List the repository's named branches.
268 List the repository's named branches.
269 """
269 """
270 b = repo.branchtags()
270 b = repo.branchtags()
271 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
271 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
272 l.sort()
272 l.sort()
273 for r, n, t in l:
273 for r, n, t in l:
274 hexfunc = ui.debugflag and hex or short
274 hexfunc = ui.debugflag and hex or short
275 if ui.quiet:
275 if ui.quiet:
276 ui.write("%s\n" % t)
276 ui.write("%s\n" % t)
277 else:
277 else:
278 spaces = " " * (30 - util.locallen(t))
278 spaces = " " * (30 - util.locallen(t))
279 ui.write("%s%s %s:%s\n" % (t, spaces, -r, hexfunc(n)))
279 ui.write("%s%s %s:%s\n" % (t, spaces, -r, hexfunc(n)))
280
280
281 def bundle(ui, repo, fname, dest=None, **opts):
281 def bundle(ui, repo, fname, dest=None, **opts):
282 """create a changegroup file
282 """create a changegroup file
283
283
284 Generate a compressed changegroup file collecting changesets not
284 Generate a compressed changegroup file collecting changesets not
285 found in the other repository.
285 found in the other repository.
286
286
287 If no destination repository is specified the destination is assumed
287 If no destination repository is specified the destination is assumed
288 to have all the nodes specified by one or more --base parameters.
288 to have all the nodes specified by one or more --base parameters.
289
289
290 The bundle file can then be transferred using conventional means and
290 The bundle file can then be transferred using conventional means and
291 applied to another repository with the unbundle or pull command.
291 applied to another repository with the unbundle or pull command.
292 This is useful when direct push and pull are not available or when
292 This is useful when direct push and pull are not available or when
293 exporting an entire repository is undesirable.
293 exporting an entire repository is undesirable.
294
294
295 Applying bundles preserves all changeset contents including
295 Applying bundles preserves all changeset contents including
296 permissions, copy/rename information, and revision history.
296 permissions, copy/rename information, and revision history.
297 """
297 """
298 revs = opts.get('rev') or None
298 revs = opts.get('rev') or None
299 if revs:
299 if revs:
300 revs = [repo.lookup(rev) for rev in revs]
300 revs = [repo.lookup(rev) for rev in revs]
301 base = opts.get('base')
301 base = opts.get('base')
302 if base:
302 if base:
303 if dest:
303 if dest:
304 raise util.Abort(_("--base is incompatible with specifiying "
304 raise util.Abort(_("--base is incompatible with specifiying "
305 "a destination"))
305 "a destination"))
306 base = [repo.lookup(rev) for rev in base]
306 base = [repo.lookup(rev) for rev in base]
307 # create the right base
307 # create the right base
308 # XXX: nodesbetween / changegroup* should be "fixed" instead
308 # XXX: nodesbetween / changegroup* should be "fixed" instead
309 o = []
309 o = []
310 has = {nullid: None}
310 has = {nullid: None}
311 for n in base:
311 for n in base:
312 has.update(repo.changelog.reachable(n))
312 has.update(repo.changelog.reachable(n))
313 if revs:
313 if revs:
314 visit = list(revs)
314 visit = list(revs)
315 else:
315 else:
316 visit = repo.changelog.heads()
316 visit = repo.changelog.heads()
317 seen = {}
317 seen = {}
318 while visit:
318 while visit:
319 n = visit.pop(0)
319 n = visit.pop(0)
320 parents = [p for p in repo.changelog.parents(n) if p not in has]
320 parents = [p for p in repo.changelog.parents(n) if p not in has]
321 if len(parents) == 0:
321 if len(parents) == 0:
322 o.insert(0, n)
322 o.insert(0, n)
323 else:
323 else:
324 for p in parents:
324 for p in parents:
325 if p not in seen:
325 if p not in seen:
326 seen[p] = 1
326 seen[p] = 1
327 visit.append(p)
327 visit.append(p)
328 else:
328 else:
329 setremoteconfig(ui, opts)
329 setremoteconfig(ui, opts)
330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
331 other = hg.repository(ui, dest)
331 other = hg.repository(ui, dest)
332 o = repo.findoutgoing(other, force=opts['force'])
332 o = repo.findoutgoing(other, force=opts['force'])
333
333
334 if revs:
334 if revs:
335 cg = repo.changegroupsubset(o, revs, 'bundle')
335 cg = repo.changegroupsubset(o, revs, 'bundle')
336 else:
336 else:
337 cg = repo.changegroup(o, 'bundle')
337 cg = repo.changegroup(o, 'bundle')
338 changegroup.writebundle(cg, fname, "HG10BZ")
338 changegroup.writebundle(cg, fname, "HG10BZ")
339
339
340 def cat(ui, repo, file1, *pats, **opts):
340 def cat(ui, repo, file1, *pats, **opts):
341 """output the current or given revision of files
341 """output the current or given revision of files
342
342
343 Print the specified files as they were at the given revision.
343 Print the specified files as they were at the given revision.
344 If no revision is given, the parent of the working directory is used,
344 If no revision is given, the parent of the working directory is used,
345 or tip if no revision is checked out.
345 or tip if no revision is checked out.
346
346
347 Output may be to a file, in which case the name of the file is
347 Output may be to a file, in which case the name of the file is
348 given using a format string. The formatting rules are the same as
348 given using a format string. The formatting rules are the same as
349 for the export command, with the following additions:
349 for the export command, with the following additions:
350
350
351 %s basename of file being printed
351 %s basename of file being printed
352 %d dirname of file being printed, or '.' if in repo root
352 %d dirname of file being printed, or '.' if in repo root
353 %p root-relative path name of file being printed
353 %p root-relative path name of file being printed
354 """
354 """
355 ctx = repo.changectx(opts['rev'])
355 ctx = repo.changectx(opts['rev'])
356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
357 ctx.node()):
357 ctx.node()):
358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
359 fp.write(ctx.filectx(abs).data())
359 fp.write(ctx.filectx(abs).data())
360
360
361 def clone(ui, source, dest=None, **opts):
361 def clone(ui, source, dest=None, **opts):
362 """make a copy of an existing repository
362 """make a copy of an existing repository
363
363
364 Create a copy of an existing repository in a new directory.
364 Create a copy of an existing repository in a new directory.
365
365
366 If no destination directory name is specified, it defaults to the
366 If no destination directory name is specified, it defaults to the
367 basename of the source.
367 basename of the source.
368
368
369 The location of the source is added to the new repository's
369 The location of the source is added to the new repository's
370 .hg/hgrc file, as the default to be used for future pulls.
370 .hg/hgrc file, as the default to be used for future pulls.
371
371
372 For efficiency, hardlinks are used for cloning whenever the source
372 For efficiency, hardlinks are used for cloning whenever the source
373 and destination are on the same filesystem (note this applies only
373 and destination are on the same filesystem (note this applies only
374 to the repository data, not to the checked out files). Some
374 to the repository data, not to the checked out files). Some
375 filesystems, such as AFS, implement hardlinking incorrectly, but
375 filesystems, such as AFS, implement hardlinking incorrectly, but
376 do not report errors. In these cases, use the --pull option to
376 do not report errors. In these cases, use the --pull option to
377 avoid hardlinking.
377 avoid hardlinking.
378
378
379 You can safely clone repositories and checked out files using full
379 You can safely clone repositories and checked out files using full
380 hardlinks with
380 hardlinks with
381
381
382 $ cp -al REPO REPOCLONE
382 $ cp -al REPO REPOCLONE
383
383
384 which is the fastest way to clone. However, the operation is not
384 which is the fastest way to clone. However, the operation is not
385 atomic (making sure REPO is not modified during the operation is
385 atomic (making sure REPO is not modified during the operation is
386 up to you) and you have to make sure your editor breaks hardlinks
386 up to you) and you have to make sure your editor breaks hardlinks
387 (Emacs and most Linux Kernel tools do so).
387 (Emacs and most Linux Kernel tools do so).
388
388
389 If you use the -r option to clone up to a specific revision, no
389 If you use the -r option to clone up to a specific revision, no
390 subsequent revisions will be present in the cloned repository.
390 subsequent revisions will be present in the cloned repository.
391 This option implies --pull, even on local repositories.
391 This option implies --pull, even on local repositories.
392
392
393 See pull for valid source format details.
393 See pull for valid source format details.
394
394
395 It is possible to specify an ssh:// URL as the destination, but no
395 It is possible to specify an ssh:// URL as the destination, but no
396 .hg/hgrc and working directory will be created on the remote side.
396 .hg/hgrc and working directory will be created on the remote side.
397 Look at the help text for the pull command for important details
397 Look at the help text for the pull command for important details
398 about ssh:// URLs.
398 about ssh:// URLs.
399 """
399 """
400 setremoteconfig(ui, opts)
400 setremoteconfig(ui, opts)
401 hg.clone(ui, ui.expandpath(source), dest,
401 hg.clone(ui, ui.expandpath(source), dest,
402 pull=opts['pull'],
402 pull=opts['pull'],
403 stream=opts['uncompressed'],
403 stream=opts['uncompressed'],
404 rev=opts['rev'],
404 rev=opts['rev'],
405 update=not opts['noupdate'])
405 update=not opts['noupdate'])
406
406
407 def commit(ui, repo, *pats, **opts):
407 def commit(ui, repo, *pats, **opts):
408 """commit the specified files or all outstanding changes
408 """commit the specified files or all outstanding changes
409
409
410 Commit changes to the given files into the repository.
410 Commit changes to the given files into the repository.
411
411
412 If a list of files is omitted, all changes reported by "hg status"
412 If a list of files is omitted, all changes reported by "hg status"
413 will be committed.
413 will be committed.
414
414
415 If no commit message is specified, the editor configured in your hgrc
415 If no commit message is specified, the editor configured in your hgrc
416 or in the EDITOR environment variable is started to enter a message.
416 or in the EDITOR environment variable is started to enter a message.
417 """
417 """
418 message = logmessage(opts)
418 message = logmessage(opts)
419
419
420 if opts['addremove']:
420 if opts['addremove']:
421 cmdutil.addremove(repo, pats, opts)
421 cmdutil.addremove(repo, pats, opts)
422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
423 if pats:
423 if pats:
424 status = repo.status(files=fns, match=match)
424 status = repo.status(files=fns, match=match)
425 modified, added, removed, deleted, unknown = status[:5]
425 modified, added, removed, deleted, unknown = status[:5]
426 files = modified + added + removed
426 files = modified + added + removed
427 slist = None
427 slist = None
428 for f in fns:
428 for f in fns:
429 if f not in files:
429 if f not in files:
430 rf = repo.wjoin(f)
430 rf = repo.wjoin(f)
431 if f in unknown:
431 if f in unknown:
432 raise util.Abort(_("file %s not tracked!") % rf)
432 raise util.Abort(_("file %s not tracked!") % rf)
433 try:
433 try:
434 mode = os.lstat(rf)[stat.ST_MODE]
434 mode = os.lstat(rf)[stat.ST_MODE]
435 except OSError:
435 except OSError:
436 raise util.Abort(_("file %s not found!") % rf)
436 raise util.Abort(_("file %s not found!") % rf)
437 if stat.S_ISDIR(mode):
437 if stat.S_ISDIR(mode):
438 name = f + '/'
438 name = f + '/'
439 if slist is None:
439 if slist is None:
440 slist = list(files)
440 slist = list(files)
441 slist.sort()
441 slist.sort()
442 i = bisect.bisect(slist, name)
442 i = bisect.bisect(slist, name)
443 if i >= len(slist) or not slist[i].startswith(name):
443 if i >= len(slist) or not slist[i].startswith(name):
444 raise util.Abort(_("no match under directory %s!")
444 raise util.Abort(_("no match under directory %s!")
445 % rf)
445 % rf)
446 elif not stat.S_ISREG(mode):
446 elif not stat.S_ISREG(mode):
447 raise util.Abort(_("can't commit %s: "
447 raise util.Abort(_("can't commit %s: "
448 "unsupported file type!") % rf)
448 "unsupported file type!") % rf)
449 else:
449 else:
450 files = []
450 files = []
451 try:
451 try:
452 repo.commit(files, message, opts['user'], opts['date'], match,
452 repo.commit(files, message, opts['user'], opts['date'], match,
453 force_editor=opts.get('force_editor'))
453 force_editor=opts.get('force_editor'))
454 except ValueError, inst:
454 except ValueError, inst:
455 raise util.Abort(str(inst))
455 raise util.Abort(str(inst))
456
456
457 def docopy(ui, repo, pats, opts, wlock):
457 def docopy(ui, repo, pats, opts, wlock):
458 # called with the repo lock held
458 # called with the repo lock held
459 #
459 #
460 # hgsep => pathname that uses "/" to separate directories
460 # hgsep => pathname that uses "/" to separate directories
461 # ossep => pathname that uses os.sep to separate directories
461 # ossep => pathname that uses os.sep to separate directories
462 cwd = repo.getcwd()
462 cwd = repo.getcwd()
463 errors = 0
463 errors = 0
464 copied = []
464 copied = []
465 targets = {}
465 targets = {}
466
466
467 # abs: hgsep
467 # abs: hgsep
468 # rel: ossep
468 # rel: ossep
469 # return: hgsep
469 # return: hgsep
470 def okaytocopy(abs, rel, exact):
470 def okaytocopy(abs, rel, exact):
471 reasons = {'?': _('is not managed'),
471 reasons = {'?': _('is not managed'),
472 'a': _('has been marked for add'),
472 'a': _('has been marked for add'),
473 'r': _('has been marked for remove')}
473 'r': _('has been marked for remove')}
474 state = repo.dirstate.state(abs)
474 state = repo.dirstate.state(abs)
475 reason = reasons.get(state)
475 reason = reasons.get(state)
476 if reason:
476 if reason:
477 if state == 'a':
477 if state == 'a':
478 origsrc = repo.dirstate.copied(abs)
478 origsrc = repo.dirstate.copied(abs)
479 if origsrc is not None:
479 if origsrc is not None:
480 return origsrc
480 return origsrc
481 if exact:
481 if exact:
482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
483 else:
483 else:
484 return abs
484 return abs
485
485
486 # origsrc: hgsep
486 # origsrc: hgsep
487 # abssrc: hgsep
487 # abssrc: hgsep
488 # relsrc: ossep
488 # relsrc: ossep
489 # target: ossep
489 # target: ossep
490 def copy(origsrc, abssrc, relsrc, target, exact):
490 def copy(origsrc, abssrc, relsrc, target, exact):
491 abstarget = util.canonpath(repo.root, cwd, target)
491 abstarget = util.canonpath(repo.root, cwd, target)
492 reltarget = util.pathto(cwd, abstarget)
492 reltarget = util.pathto(cwd, abstarget)
493 prevsrc = targets.get(abstarget)
493 prevsrc = targets.get(abstarget)
494 if prevsrc is not None:
494 if prevsrc is not None:
495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
496 (reltarget, util.localpath(abssrc),
496 (reltarget, util.localpath(abssrc),
497 util.localpath(prevsrc)))
497 util.localpath(prevsrc)))
498 return
498 return
499 if (not opts['after'] and os.path.exists(reltarget) or
499 if (not opts['after'] and os.path.exists(reltarget) or
500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
501 if not opts['force']:
501 if not opts['force']:
502 ui.warn(_('%s: not overwriting - file exists\n') %
502 ui.warn(_('%s: not overwriting - file exists\n') %
503 reltarget)
503 reltarget)
504 return
504 return
505 if not opts['after'] and not opts.get('dry_run'):
505 if not opts['after'] and not opts.get('dry_run'):
506 os.unlink(reltarget)
506 os.unlink(reltarget)
507 if opts['after']:
507 if opts['after']:
508 if not os.path.exists(reltarget):
508 if not os.path.exists(reltarget):
509 return
509 return
510 else:
510 else:
511 targetdir = os.path.dirname(reltarget) or '.'
511 targetdir = os.path.dirname(reltarget) or '.'
512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
513 os.makedirs(targetdir)
513 os.makedirs(targetdir)
514 try:
514 try:
515 restore = repo.dirstate.state(abstarget) == 'r'
515 restore = repo.dirstate.state(abstarget) == 'r'
516 if restore and not opts.get('dry_run'):
516 if restore and not opts.get('dry_run'):
517 repo.undelete([abstarget], wlock)
517 repo.undelete([abstarget], wlock)
518 try:
518 try:
519 if not opts.get('dry_run'):
519 if not opts.get('dry_run'):
520 util.copyfile(relsrc, reltarget)
520 util.copyfile(relsrc, reltarget)
521 restore = False
521 restore = False
522 finally:
522 finally:
523 if restore:
523 if restore:
524 repo.remove([abstarget], wlock)
524 repo.remove([abstarget], wlock)
525 except IOError, inst:
525 except IOError, inst:
526 if inst.errno == errno.ENOENT:
526 if inst.errno == errno.ENOENT:
527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
528 else:
528 else:
529 ui.warn(_('%s: cannot copy - %s\n') %
529 ui.warn(_('%s: cannot copy - %s\n') %
530 (relsrc, inst.strerror))
530 (relsrc, inst.strerror))
531 errors += 1
531 errors += 1
532 return
532 return
533 if ui.verbose or not exact:
533 if ui.verbose or not exact:
534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
535 targets[abstarget] = abssrc
535 targets[abstarget] = abssrc
536 if abstarget != origsrc and not opts.get('dry_run'):
536 if abstarget != origsrc and not opts.get('dry_run'):
537 repo.copy(origsrc, abstarget, wlock)
537 repo.copy(origsrc, abstarget, wlock)
538 copied.append((abssrc, relsrc, exact))
538 copied.append((abssrc, relsrc, exact))
539
539
540 # pat: ossep
540 # pat: ossep
541 # dest ossep
541 # dest ossep
542 # srcs: list of (hgsep, hgsep, ossep, bool)
542 # srcs: list of (hgsep, hgsep, ossep, bool)
543 # return: function that takes hgsep and returns ossep
543 # return: function that takes hgsep and returns ossep
544 def targetpathfn(pat, dest, srcs):
544 def targetpathfn(pat, dest, srcs):
545 if os.path.isdir(pat):
545 if os.path.isdir(pat):
546 abspfx = util.canonpath(repo.root, cwd, pat)
546 abspfx = util.canonpath(repo.root, cwd, pat)
547 abspfx = util.localpath(abspfx)
547 abspfx = util.localpath(abspfx)
548 if destdirexists:
548 if destdirexists:
549 striplen = len(os.path.split(abspfx)[0])
549 striplen = len(os.path.split(abspfx)[0])
550 else:
550 else:
551 striplen = len(abspfx)
551 striplen = len(abspfx)
552 if striplen:
552 if striplen:
553 striplen += len(os.sep)
553 striplen += len(os.sep)
554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
555 elif destdirexists:
555 elif destdirexists:
556 res = lambda p: os.path.join(dest,
556 res = lambda p: os.path.join(dest,
557 os.path.basename(util.localpath(p)))
557 os.path.basename(util.localpath(p)))
558 else:
558 else:
559 res = lambda p: dest
559 res = lambda p: dest
560 return res
560 return res
561
561
562 # pat: ossep
562 # pat: ossep
563 # dest ossep
563 # dest ossep
564 # srcs: list of (hgsep, hgsep, ossep, bool)
564 # srcs: list of (hgsep, hgsep, ossep, bool)
565 # return: function that takes hgsep and returns ossep
565 # return: function that takes hgsep and returns ossep
566 def targetpathafterfn(pat, dest, srcs):
566 def targetpathafterfn(pat, dest, srcs):
567 if util.patkind(pat, None)[0]:
567 if util.patkind(pat, None)[0]:
568 # a mercurial pattern
568 # a mercurial pattern
569 res = lambda p: os.path.join(dest,
569 res = lambda p: os.path.join(dest,
570 os.path.basename(util.localpath(p)))
570 os.path.basename(util.localpath(p)))
571 else:
571 else:
572 abspfx = util.canonpath(repo.root, cwd, pat)
572 abspfx = util.canonpath(repo.root, cwd, pat)
573 if len(abspfx) < len(srcs[0][0]):
573 if len(abspfx) < len(srcs[0][0]):
574 # A directory. Either the target path contains the last
574 # A directory. Either the target path contains the last
575 # component of the source path or it does not.
575 # component of the source path or it does not.
576 def evalpath(striplen):
576 def evalpath(striplen):
577 score = 0
577 score = 0
578 for s in srcs:
578 for s in srcs:
579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
580 if os.path.exists(t):
580 if os.path.exists(t):
581 score += 1
581 score += 1
582 return score
582 return score
583
583
584 abspfx = util.localpath(abspfx)
584 abspfx = util.localpath(abspfx)
585 striplen = len(abspfx)
585 striplen = len(abspfx)
586 if striplen:
586 if striplen:
587 striplen += len(os.sep)
587 striplen += len(os.sep)
588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
589 score = evalpath(striplen)
589 score = evalpath(striplen)
590 striplen1 = len(os.path.split(abspfx)[0])
590 striplen1 = len(os.path.split(abspfx)[0])
591 if striplen1:
591 if striplen1:
592 striplen1 += len(os.sep)
592 striplen1 += len(os.sep)
593 if evalpath(striplen1) > score:
593 if evalpath(striplen1) > score:
594 striplen = striplen1
594 striplen = striplen1
595 res = lambda p: os.path.join(dest,
595 res = lambda p: os.path.join(dest,
596 util.localpath(p)[striplen:])
596 util.localpath(p)[striplen:])
597 else:
597 else:
598 # a file
598 # a file
599 if destdirexists:
599 if destdirexists:
600 res = lambda p: os.path.join(dest,
600 res = lambda p: os.path.join(dest,
601 os.path.basename(util.localpath(p)))
601 os.path.basename(util.localpath(p)))
602 else:
602 else:
603 res = lambda p: dest
603 res = lambda p: dest
604 return res
604 return res
605
605
606
606
607 pats = util.expand_glob(pats)
607 pats = util.expand_glob(pats)
608 if not pats:
608 if not pats:
609 raise util.Abort(_('no source or destination specified'))
609 raise util.Abort(_('no source or destination specified'))
610 if len(pats) == 1:
610 if len(pats) == 1:
611 raise util.Abort(_('no destination specified'))
611 raise util.Abort(_('no destination specified'))
612 dest = pats.pop()
612 dest = pats.pop()
613 destdirexists = os.path.isdir(dest)
613 destdirexists = os.path.isdir(dest)
614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
615 raise util.Abort(_('with multiple sources, destination must be an '
615 raise util.Abort(_('with multiple sources, destination must be an '
616 'existing directory'))
616 'existing directory'))
617 if opts['after']:
617 if opts['after']:
618 tfn = targetpathafterfn
618 tfn = targetpathafterfn
619 else:
619 else:
620 tfn = targetpathfn
620 tfn = targetpathfn
621 copylist = []
621 copylist = []
622 for pat in pats:
622 for pat in pats:
623 srcs = []
623 srcs = []
624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
625 globbed=True):
625 globbed=True):
626 origsrc = okaytocopy(abssrc, relsrc, exact)
626 origsrc = okaytocopy(abssrc, relsrc, exact)
627 if origsrc:
627 if origsrc:
628 srcs.append((origsrc, abssrc, relsrc, exact))
628 srcs.append((origsrc, abssrc, relsrc, exact))
629 if not srcs:
629 if not srcs:
630 continue
630 continue
631 copylist.append((tfn(pat, dest, srcs), srcs))
631 copylist.append((tfn(pat, dest, srcs), srcs))
632 if not copylist:
632 if not copylist:
633 raise util.Abort(_('no files to copy'))
633 raise util.Abort(_('no files to copy'))
634
634
635 for targetpath, srcs in copylist:
635 for targetpath, srcs in copylist:
636 for origsrc, abssrc, relsrc, exact in srcs:
636 for origsrc, abssrc, relsrc, exact in srcs:
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
638
638
639 if errors:
639 if errors:
640 ui.warn(_('(consider using --after)\n'))
640 ui.warn(_('(consider using --after)\n'))
641 return errors, copied
641 return errors, copied
642
642
643 def copy(ui, repo, *pats, **opts):
643 def copy(ui, repo, *pats, **opts):
644 """mark files as copied for the next commit
644 """mark files as copied for the next commit
645
645
646 Mark dest as having copies of source files. If dest is a
646 Mark dest as having copies of source files. If dest is a
647 directory, copies are put in that directory. If dest is a file,
647 directory, copies are put in that directory. If dest is a file,
648 there can only be one source.
648 there can only be one source.
649
649
650 By default, this command copies the contents of files as they
650 By default, this command copies the contents of files as they
651 stand in the working directory. If invoked with --after, the
651 stand in the working directory. If invoked with --after, the
652 operation is recorded, but no copying is performed.
652 operation is recorded, but no copying is performed.
653
653
654 This command takes effect in the next commit. To undo a copy
654 This command takes effect in the next commit. To undo a copy
655 before that, see hg revert.
655 before that, see hg revert.
656 """
656 """
657 wlock = repo.wlock(0)
657 wlock = repo.wlock(0)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
659 return errs
659 return errs
660
660
661 def debugancestor(ui, index, rev1, rev2):
661 def debugancestor(ui, index, rev1, rev2):
662 """find the ancestor revision of two revisions in a given index"""
662 """find the ancestor revision of two revisions in a given index"""
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
666
666
667 def debugcomplete(ui, cmd='', **opts):
667 def debugcomplete(ui, cmd='', **opts):
668 """returns the completion list associated with the given command"""
668 """returns the completion list associated with the given command"""
669
669
670 if opts['options']:
670 if opts['options']:
671 options = []
671 options = []
672 otables = [globalopts]
672 otables = [globalopts]
673 if cmd:
673 if cmd:
674 aliases, entry = findcmd(ui, cmd)
674 aliases, entry = findcmd(ui, cmd)
675 otables.append(entry[1])
675 otables.append(entry[1])
676 for t in otables:
676 for t in otables:
677 for o in t:
677 for o in t:
678 if o[0]:
678 if o[0]:
679 options.append('-%s' % o[0])
679 options.append('-%s' % o[0])
680 options.append('--%s' % o[1])
680 options.append('--%s' % o[1])
681 ui.write("%s\n" % "\n".join(options))
681 ui.write("%s\n" % "\n".join(options))
682 return
682 return
683
683
684 clist = findpossible(ui, cmd).keys()
684 clist = findpossible(ui, cmd).keys()
685 clist.sort()
685 clist.sort()
686 ui.write("%s\n" % "\n".join(clist))
686 ui.write("%s\n" % "\n".join(clist))
687
687
688 def debugrebuildstate(ui, repo, rev=None):
688 def debugrebuildstate(ui, repo, rev=None):
689 """rebuild the dirstate as it would look like for the given revision"""
689 """rebuild the dirstate as it would look like for the given revision"""
690 if not rev:
690 if not rev:
691 rev = repo.changelog.tip()
691 rev = repo.changelog.tip()
692 else:
692 else:
693 rev = repo.lookup(rev)
693 rev = repo.lookup(rev)
694 change = repo.changelog.read(rev)
694 change = repo.changelog.read(rev)
695 n = change[0]
695 n = change[0]
696 files = repo.manifest.read(n)
696 files = repo.manifest.read(n)
697 wlock = repo.wlock()
697 wlock = repo.wlock()
698 repo.dirstate.rebuild(rev, files)
698 repo.dirstate.rebuild(rev, files)
699
699
700 def debugcheckstate(ui, repo):
700 def debugcheckstate(ui, repo):
701 """validate the correctness of the current dirstate"""
701 """validate the correctness of the current dirstate"""
702 parent1, parent2 = repo.dirstate.parents()
702 parent1, parent2 = repo.dirstate.parents()
703 repo.dirstate.read()
703 repo.dirstate.read()
704 dc = repo.dirstate.map
704 dc = repo.dirstate.map
705 keys = dc.keys()
705 keys = dc.keys()
706 keys.sort()
706 keys.sort()
707 m1n = repo.changelog.read(parent1)[0]
707 m1n = repo.changelog.read(parent1)[0]
708 m2n = repo.changelog.read(parent2)[0]
708 m2n = repo.changelog.read(parent2)[0]
709 m1 = repo.manifest.read(m1n)
709 m1 = repo.manifest.read(m1n)
710 m2 = repo.manifest.read(m2n)
710 m2 = repo.manifest.read(m2n)
711 errors = 0
711 errors = 0
712 for f in dc:
712 for f in dc:
713 state = repo.dirstate.state(f)
713 state = repo.dirstate.state(f)
714 if state in "nr" and f not in m1:
714 if state in "nr" and f not in m1:
715 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
715 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
716 errors += 1
716 errors += 1
717 if state in "a" and f in m1:
717 if state in "a" and f in m1:
718 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
718 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
719 errors += 1
719 errors += 1
720 if state in "m" and f not in m1 and f not in m2:
720 if state in "m" and f not in m1 and f not in m2:
721 ui.warn(_("%s in state %s, but not in either manifest\n") %
721 ui.warn(_("%s in state %s, but not in either manifest\n") %
722 (f, state))
722 (f, state))
723 errors += 1
723 errors += 1
724 for f in m1:
724 for f in m1:
725 state = repo.dirstate.state(f)
725 state = repo.dirstate.state(f)
726 if state not in "nrm":
726 if state not in "nrm":
727 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
727 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
728 errors += 1
728 errors += 1
729 if errors:
729 if errors:
730 error = _(".hg/dirstate inconsistent with current parent's manifest")
730 error = _(".hg/dirstate inconsistent with current parent's manifest")
731 raise util.Abort(error)
731 raise util.Abort(error)
732
732
733 def showconfig(ui, repo, *values, **opts):
733 def showconfig(ui, repo, *values, **opts):
734 """show combined config settings from all hgrc files
734 """show combined config settings from all hgrc files
735
735
736 With no args, print names and values of all config items.
736 With no args, print names and values of all config items.
737
737
738 With one arg of the form section.name, print just the value of
738 With one arg of the form section.name, print just the value of
739 that config item.
739 that config item.
740
740
741 With multiple args, print names and values of all config items
741 With multiple args, print names and values of all config items
742 with matching section names."""
742 with matching section names."""
743
743
744 untrusted = bool(opts.get('untrusted'))
744 untrusted = bool(opts.get('untrusted'))
745 if values:
745 if values:
746 if len([v for v in values if '.' in v]) > 1:
746 if len([v for v in values if '.' in v]) > 1:
747 raise util.Abort(_('only one config item permitted'))
747 raise util.Abort(_('only one config item permitted'))
748 for section, name, value in ui.walkconfig(untrusted=untrusted):
748 for section, name, value in ui.walkconfig(untrusted=untrusted):
749 sectname = section + '.' + name
749 sectname = section + '.' + name
750 if values:
750 if values:
751 for v in values:
751 for v in values:
752 if v == section:
752 if v == section:
753 ui.write('%s=%s\n' % (sectname, value))
753 ui.write('%s=%s\n' % (sectname, value))
754 elif v == sectname:
754 elif v == sectname:
755 ui.write(value, '\n')
755 ui.write(value, '\n')
756 else:
756 else:
757 ui.write('%s=%s\n' % (sectname, value))
757 ui.write('%s=%s\n' % (sectname, value))
758
758
759 def debugsetparents(ui, repo, rev1, rev2=None):
759 def debugsetparents(ui, repo, rev1, rev2=None):
760 """manually set the parents of the current working directory
760 """manually set the parents of the current working directory
761
761
762 This is useful for writing repository conversion tools, but should
762 This is useful for writing repository conversion tools, but should
763 be used with care.
763 be used with care.
764 """
764 """
765
765
766 if not rev2:
766 if not rev2:
767 rev2 = hex(nullid)
767 rev2 = hex(nullid)
768
768
769 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
769 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
770
770
771 def debugstate(ui, repo):
771 def debugstate(ui, repo):
772 """show the contents of the current dirstate"""
772 """show the contents of the current dirstate"""
773 repo.dirstate.read()
773 repo.dirstate.read()
774 dc = repo.dirstate.map
774 dc = repo.dirstate.map
775 keys = dc.keys()
775 keys = dc.keys()
776 keys.sort()
776 keys.sort()
777 for file_ in keys:
777 for file_ in keys:
778 if dc[file_][3] == -1:
778 if dc[file_][3] == -1:
779 # Pad or slice to locale representation
779 # Pad or slice to locale representation
780 locale_len = len(time.strftime("%x %X", time.localtime(0)))
780 locale_len = len(time.strftime("%x %X", time.localtime(0)))
781 timestr = 'unset'
781 timestr = 'unset'
782 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
782 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
783 else:
783 else:
784 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
784 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
785 ui.write("%c %3o %10d %s %s\n"
785 ui.write("%c %3o %10d %s %s\n"
786 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
786 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
787 timestr, file_))
787 timestr, file_))
788 for f in repo.dirstate.copies():
788 for f in repo.dirstate.copies():
789 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
789 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
790
790
791 def debugdata(ui, file_, rev):
791 def debugdata(ui, file_, rev):
792 """dump the contents of an data file revision"""
792 """dump the contents of an data file revision"""
793 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
793 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
794 file_[:-2] + ".i", file_, 0)
794 file_[:-2] + ".i", file_, 0)
795 try:
795 try:
796 ui.write(r.revision(r.lookup(rev)))
796 ui.write(r.revision(r.lookup(rev)))
797 except KeyError:
797 except KeyError:
798 raise util.Abort(_('invalid revision identifier %s') % rev)
798 raise util.Abort(_('invalid revision identifier %s') % rev)
799
799
800 def debugdate(ui, date, range=None, **opts):
800 def debugdate(ui, date, range=None, **opts):
801 """parse and display a date"""
801 """parse and display a date"""
802 if opts["extended"]:
802 if opts["extended"]:
803 d = util.parsedate(date, util.extendeddateformats)
803 d = util.parsedate(date, util.extendeddateformats)
804 else:
804 else:
805 d = util.parsedate(date)
805 d = util.parsedate(date)
806 ui.write("internal: %s %s\n" % d)
806 ui.write("internal: %s %s\n" % d)
807 ui.write("standard: %s\n" % util.datestr(d))
807 ui.write("standard: %s\n" % util.datestr(d))
808 if range:
808 if range:
809 m = util.matchdate(range)
809 m = util.matchdate(range)
810 ui.write("match: %s\n" % m(d[0]))
810 ui.write("match: %s\n" % m(d[0]))
811
811
812 def debugindex(ui, file_):
812 def debugindex(ui, file_):
813 """dump the contents of an index file"""
813 """dump the contents of an index file"""
814 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
814 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
815 ui.write(" rev offset length base linkrev" +
815 ui.write(" rev offset length base linkrev" +
816 " nodeid p1 p2\n")
816 " nodeid p1 p2\n")
817 for i in xrange(r.count()):
817 for i in xrange(r.count()):
818 node = r.node(i)
818 node = r.node(i)
819 pp = r.parents(node)
819 pp = r.parents(node)
820 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
820 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
821 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
821 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
822 short(node), short(pp[0]), short(pp[1])))
822 short(node), short(pp[0]), short(pp[1])))
823
823
824 def debugindexdot(ui, file_):
824 def debugindexdot(ui, file_):
825 """dump an index DAG as a .dot file"""
825 """dump an index DAG as a .dot file"""
826 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
826 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
827 ui.write("digraph G {\n")
827 ui.write("digraph G {\n")
828 for i in xrange(r.count()):
828 for i in xrange(r.count()):
829 node = r.node(i)
829 node = r.node(i)
830 pp = r.parents(node)
830 pp = r.parents(node)
831 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
831 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
832 if pp[1] != nullid:
832 if pp[1] != nullid:
833 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
833 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
834 ui.write("}\n")
834 ui.write("}\n")
835
835
836 def debuginstall(ui):
836 def debuginstall(ui):
837 '''test Mercurial installation'''
837 '''test Mercurial installation'''
838
838
839 def writetemp(contents):
839 def writetemp(contents):
840 (fd, name) = tempfile.mkstemp()
840 (fd, name) = tempfile.mkstemp()
841 f = os.fdopen(fd, "wb")
841 f = os.fdopen(fd, "wb")
842 f.write(contents)
842 f.write(contents)
843 f.close()
843 f.close()
844 return name
844 return name
845
845
846 problems = 0
846 problems = 0
847
847
848 # encoding
848 # encoding
849 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
849 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
850 try:
850 try:
851 util.fromlocal("test")
851 util.fromlocal("test")
852 except util.Abort, inst:
852 except util.Abort, inst:
853 ui.write(" %s\n" % inst)
853 ui.write(" %s\n" % inst)
854 ui.write(_(" (check that your locale is properly set)\n"))
854 ui.write(_(" (check that your locale is properly set)\n"))
855 problems += 1
855 problems += 1
856
856
857 # compiled modules
857 # compiled modules
858 ui.status(_("Checking extensions...\n"))
858 ui.status(_("Checking extensions...\n"))
859 try:
859 try:
860 import bdiff, mpatch, base85
860 import bdiff, mpatch, base85
861 except Exception, inst:
861 except Exception, inst:
862 ui.write(" %s\n" % inst)
862 ui.write(" %s\n" % inst)
863 ui.write(_(" One or more extensions could not be found"))
863 ui.write(_(" One or more extensions could not be found"))
864 ui.write(_(" (check that you compiled the extensions)\n"))
864 ui.write(_(" (check that you compiled the extensions)\n"))
865 problems += 1
865 problems += 1
866
866
867 # templates
867 # templates
868 ui.status(_("Checking templates...\n"))
868 ui.status(_("Checking templates...\n"))
869 try:
869 try:
870 import templater
870 import templater
871 t = templater.templater(templater.templatepath("map-cmdline.default"))
871 t = templater.templater(templater.templatepath("map-cmdline.default"))
872 except Exception, inst:
872 except Exception, inst:
873 ui.write(" %s\n" % inst)
873 ui.write(" %s\n" % inst)
874 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
874 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
875 problems += 1
875 problems += 1
876
876
877 # patch
877 # patch
878 ui.status(_("Checking patch...\n"))
878 ui.status(_("Checking patch...\n"))
879 path = os.environ.get('PATH', '')
879 path = os.environ.get('PATH', '')
880 patcher = util.find_in_path('gpatch', path,
880 patcher = util.find_in_path('gpatch', path,
881 util.find_in_path('patch', path, None))
881 util.find_in_path('patch', path, None))
882 if not patcher:
882 if not patcher:
883 ui.write(_(" Can't find patch or gpatch in PATH\n"))
883 ui.write(_(" Can't find patch or gpatch in PATH\n"))
884 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
884 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
885 problems += 1
885 problems += 1
886 else:
886 else:
887 # actually attempt a patch here
887 # actually attempt a patch here
888 a = "1\n2\n3\n4\n"
888 a = "1\n2\n3\n4\n"
889 b = "1\n2\n3\ninsert\n4\n"
889 b = "1\n2\n3\ninsert\n4\n"
890 d = mdiff.unidiff(a, None, b, None, "a")
890 d = mdiff.unidiff(a, None, b, None, "a")
891 fa = writetemp(a)
891 fa = writetemp(a)
892 fd = writetemp(d)
892 fd = writetemp(d)
893 fp = os.popen('%s %s %s' % (patcher, fa, fd))
893 fp = os.popen('%s %s %s' % (patcher, fa, fd))
894 files = []
894 files = []
895 output = ""
895 output = ""
896 for line in fp:
896 for line in fp:
897 output += line
897 output += line
898 if line.startswith('patching file '):
898 if line.startswith('patching file '):
899 pf = util.parse_patch_output(line.rstrip())
899 pf = util.parse_patch_output(line.rstrip())
900 files.append(pf)
900 files.append(pf)
901 if files != [fa]:
901 if files != [fa]:
902 ui.write(_(" unexpected patch output!"))
902 ui.write(_(" unexpected patch output!"))
903 ui.write(_(" (you may have an incompatible version of patch)\n"))
903 ui.write(_(" (you may have an incompatible version of patch)\n"))
904 ui.write(output)
904 ui.write(output)
905 problems += 1
905 problems += 1
906 a = file(fa).read()
906 a = file(fa).read()
907 if a != b:
907 if a != b:
908 ui.write(_(" patch test failed!"))
908 ui.write(_(" patch test failed!"))
909 ui.write(_(" (you may have an incompatible version of patch)\n"))
909 ui.write(_(" (you may have an incompatible version of patch)\n"))
910 problems += 1
910 problems += 1
911 os.unlink(fa)
911 os.unlink(fa)
912 os.unlink(fd)
912 os.unlink(fd)
913
913
914 # merge helper
914 # merge helper
915 ui.status(_("Checking merge helper...\n"))
915 ui.status(_("Checking merge helper...\n"))
916 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
916 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
917 or "hgmerge")
917 or "hgmerge")
918 cmdpath = util.find_in_path(cmd, path)
918 cmdpath = util.find_in_path(cmd, path)
919 if not cmdpath:
919 if not cmdpath:
920 cmdpath = util.find_in_path(cmd.split()[0], path)
920 cmdpath = util.find_in_path(cmd.split()[0], path)
921 if not cmdpath:
921 if not cmdpath:
922 if cmd == 'hgmerge':
922 if cmd == 'hgmerge':
923 ui.write(_(" No merge helper set and can't find default"
923 ui.write(_(" No merge helper set and can't find default"
924 " hgmerge script in PATH\n"))
924 " hgmerge script in PATH\n"))
925 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
925 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
926 else:
926 else:
927 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
927 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
928 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
928 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
929 problems += 1
929 problems += 1
930 else:
930 else:
931 # actually attempt a patch here
931 # actually attempt a patch here
932 fa = writetemp("1\n2\n3\n4\n")
932 fa = writetemp("1\n2\n3\n4\n")
933 fl = writetemp("1\n2\n3\ninsert\n4\n")
933 fl = writetemp("1\n2\n3\ninsert\n4\n")
934 fr = writetemp("begin\n1\n2\n3\n4\n")
934 fr = writetemp("begin\n1\n2\n3\n4\n")
935 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
935 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
936 if r:
936 if r:
937 ui.write(_(" got unexpected merge error %d!") % r)
937 ui.write(_(" got unexpected merge error %d!") % r)
938 problems += 1
938 problems += 1
939 m = file(fl).read()
939 m = file(fl).read()
940 if m != "begin\n1\n2\n3\ninsert\n4\n":
940 if m != "begin\n1\n2\n3\ninsert\n4\n":
941 ui.write(_(" got unexpected merge results!") % r)
941 ui.write(_(" got unexpected merge results!") % r)
942 ui.write(_(" (your merge helper may have the"
942 ui.write(_(" (your merge helper may have the"
943 " wrong argument order)\n"))
943 " wrong argument order)\n"))
944 ui.write(m)
944 ui.write(m)
945 os.unlink(fa)
945 os.unlink(fa)
946 os.unlink(fl)
946 os.unlink(fl)
947 os.unlink(fr)
947 os.unlink(fr)
948
948
949 # editor
949 # editor
950 ui.status(_("Checking commit editor...\n"))
950 ui.status(_("Checking commit editor...\n"))
951 editor = (os.environ.get("HGEDITOR") or
951 editor = (os.environ.get("HGEDITOR") or
952 ui.config("ui", "editor") or
952 ui.config("ui", "editor") or
953 os.environ.get("EDITOR", "vi"))
953 os.environ.get("EDITOR", "vi"))
954 cmdpath = util.find_in_path(editor, path)
954 cmdpath = util.find_in_path(editor, path)
955 if not cmdpath:
955 if not cmdpath:
956 cmdpath = util.find_in_path(editor.split()[0], path)
956 cmdpath = util.find_in_path(editor.split()[0], path)
957 if not cmdpath:
957 if not cmdpath:
958 if editor == 'vi':
958 if editor == 'vi':
959 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
959 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
960 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
960 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
961 else:
961 else:
962 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
962 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
963 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
963 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
964 problems += 1
964 problems += 1
965
965
966 # check username
966 # check username
967 ui.status(_("Checking username...\n"))
967 ui.status(_("Checking username...\n"))
968 user = os.environ.get("HGUSER")
968 user = os.environ.get("HGUSER")
969 if user is None:
969 if user is None:
970 user = ui.config("ui", "username")
970 user = ui.config("ui", "username")
971 if user is None:
971 if user is None:
972 user = os.environ.get("EMAIL")
972 user = os.environ.get("EMAIL")
973 if not user:
973 if not user:
974 ui.warn(" ")
974 ui.warn(" ")
975 ui.username()
975 ui.username()
976 ui.write(_(" (specify a username in your .hgrc file)\n"))
976 ui.write(_(" (specify a username in your .hgrc file)\n"))
977
977
978 if not problems:
978 if not problems:
979 ui.status(_("No problems detected\n"))
979 ui.status(_("No problems detected\n"))
980 else:
980 else:
981 ui.write(_("%s problems detected,"
981 ui.write(_("%s problems detected,"
982 " please check your install!\n") % problems)
982 " please check your install!\n") % problems)
983
983
984 return problems
984 return problems
985
985
986 def debugrename(ui, repo, file1, *pats, **opts):
986 def debugrename(ui, repo, file1, *pats, **opts):
987 """dump rename information"""
987 """dump rename information"""
988
988
989 ctx = repo.changectx(opts.get('rev', 'tip'))
989 ctx = repo.changectx(opts.get('rev', 'tip'))
990 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
990 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
991 ctx.node()):
991 ctx.node()):
992 m = ctx.filectx(abs).renamed()
992 m = ctx.filectx(abs).renamed()
993 if m:
993 if m:
994 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
994 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
995 else:
995 else:
996 ui.write(_("%s not renamed\n") % rel)
996 ui.write(_("%s not renamed\n") % rel)
997
997
998 def debugwalk(ui, repo, *pats, **opts):
998 def debugwalk(ui, repo, *pats, **opts):
999 """show how files match on given patterns"""
999 """show how files match on given patterns"""
1000 items = list(cmdutil.walk(repo, pats, opts))
1000 items = list(cmdutil.walk(repo, pats, opts))
1001 if not items:
1001 if not items:
1002 return
1002 return
1003 fmt = '%%s %%-%ds %%-%ds %%s' % (
1003 fmt = '%%s %%-%ds %%-%ds %%s' % (
1004 max([len(abs) for (src, abs, rel, exact) in items]),
1004 max([len(abs) for (src, abs, rel, exact) in items]),
1005 max([len(rel) for (src, abs, rel, exact) in items]))
1005 max([len(rel) for (src, abs, rel, exact) in items]))
1006 for src, abs, rel, exact in items:
1006 for src, abs, rel, exact in items:
1007 line = fmt % (src, abs, rel, exact and 'exact' or '')
1007 line = fmt % (src, abs, rel, exact and 'exact' or '')
1008 ui.write("%s\n" % line.rstrip())
1008 ui.write("%s\n" % line.rstrip())
1009
1009
1010 def diff(ui, repo, *pats, **opts):
1010 def diff(ui, repo, *pats, **opts):
1011 """diff repository (or selected files)
1011 """diff repository (or selected files)
1012
1012
1013 Show differences between revisions for the specified files.
1013 Show differences between revisions for the specified files.
1014
1014
1015 Differences between files are shown using the unified diff format.
1015 Differences between files are shown using the unified diff format.
1016
1016
1017 NOTE: diff may generate unexpected results for merges, as it will
1017 NOTE: diff may generate unexpected results for merges, as it will
1018 default to comparing against the working directory's first parent
1018 default to comparing against the working directory's first parent
1019 changeset if no revisions are specified.
1019 changeset if no revisions are specified.
1020
1020
1021 When two revision arguments are given, then changes are shown
1021 When two revision arguments are given, then changes are shown
1022 between those revisions. If only one revision is specified then
1022 between those revisions. If only one revision is specified then
1023 that revision is compared to the working directory, and, when no
1023 that revision is compared to the working directory, and, when no
1024 revisions are specified, the working directory files are compared
1024 revisions are specified, the working directory files are compared
1025 to its parent.
1025 to its parent.
1026
1026
1027 Without the -a option, diff will avoid generating diffs of files
1027 Without the -a option, diff will avoid generating diffs of files
1028 it detects as binary. With -a, diff will generate a diff anyway,
1028 it detects as binary. With -a, diff will generate a diff anyway,
1029 probably with undesirable results.
1029 probably with undesirable results.
1030 """
1030 """
1031 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1031 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1032
1032
1033 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1033 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1034
1034
1035 patch.diff(repo, node1, node2, fns, match=matchfn,
1035 patch.diff(repo, node1, node2, fns, match=matchfn,
1036 opts=patch.diffopts(ui, opts))
1036 opts=patch.diffopts(ui, opts))
1037
1037
1038 def export(ui, repo, *changesets, **opts):
1038 def export(ui, repo, *changesets, **opts):
1039 """dump the header and diffs for one or more changesets
1039 """dump the header and diffs for one or more changesets
1040
1040
1041 Print the changeset header and diffs for one or more revisions.
1041 Print the changeset header and diffs for one or more revisions.
1042
1042
1043 The information shown in the changeset header is: author,
1043 The information shown in the changeset header is: author,
1044 changeset hash, parent(s) and commit comment.
1044 changeset hash, parent(s) and commit comment.
1045
1045
1046 NOTE: export may generate unexpected diff output for merge changesets,
1046 NOTE: export may generate unexpected diff output for merge changesets,
1047 as it will compare the merge changeset against its first parent only.
1047 as it will compare the merge changeset against its first parent only.
1048
1048
1049 Output may be to a file, in which case the name of the file is
1049 Output may be to a file, in which case the name of the file is
1050 given using a format string. The formatting rules are as follows:
1050 given using a format string. The formatting rules are as follows:
1051
1051
1052 %% literal "%" character
1052 %% literal "%" character
1053 %H changeset hash (40 bytes of hexadecimal)
1053 %H changeset hash (40 bytes of hexadecimal)
1054 %N number of patches being generated
1054 %N number of patches being generated
1055 %R changeset revision number
1055 %R changeset revision number
1056 %b basename of the exporting repository
1056 %b basename of the exporting repository
1057 %h short-form changeset hash (12 bytes of hexadecimal)
1057 %h short-form changeset hash (12 bytes of hexadecimal)
1058 %n zero-padded sequence number, starting at 1
1058 %n zero-padded sequence number, starting at 1
1059 %r zero-padded changeset revision number
1059 %r zero-padded changeset revision number
1060
1060
1061 Without the -a option, export will avoid generating diffs of files
1061 Without the -a option, export will avoid generating diffs of files
1062 it detects as binary. With -a, export will generate a diff anyway,
1062 it detects as binary. With -a, export will generate a diff anyway,
1063 probably with undesirable results.
1063 probably with undesirable results.
1064
1064
1065 With the --switch-parent option, the diff will be against the second
1065 With the --switch-parent option, the diff will be against the second
1066 parent. It can be useful to review a merge.
1066 parent. It can be useful to review a merge.
1067 """
1067 """
1068 if not changesets:
1068 if not changesets:
1069 raise util.Abort(_("export requires at least one changeset"))
1069 raise util.Abort(_("export requires at least one changeset"))
1070 revs = cmdutil.revrange(repo, changesets)
1070 revs = cmdutil.revrange(repo, changesets)
1071 if len(revs) > 1:
1071 if len(revs) > 1:
1072 ui.note(_('exporting patches:\n'))
1072 ui.note(_('exporting patches:\n'))
1073 else:
1073 else:
1074 ui.note(_('exporting patch:\n'))
1074 ui.note(_('exporting patch:\n'))
1075 patch.export(repo, revs, template=opts['output'],
1075 patch.export(repo, revs, template=opts['output'],
1076 switch_parent=opts['switch_parent'],
1076 switch_parent=opts['switch_parent'],
1077 opts=patch.diffopts(ui, opts))
1077 opts=patch.diffopts(ui, opts))
1078
1078
1079 def grep(ui, repo, pattern, *pats, **opts):
1079 def grep(ui, repo, pattern, *pats, **opts):
1080 """search for a pattern in specified files and revisions
1080 """search for a pattern in specified files and revisions
1081
1081
1082 Search revisions of files for a regular expression.
1082 Search revisions of files for a regular expression.
1083
1083
1084 This command behaves differently than Unix grep. It only accepts
1084 This command behaves differently than Unix grep. It only accepts
1085 Python/Perl regexps. It searches repository history, not the
1085 Python/Perl regexps. It searches repository history, not the
1086 working directory. It always prints the revision number in which
1086 working directory. It always prints the revision number in which
1087 a match appears.
1087 a match appears.
1088
1088
1089 By default, grep only prints output for the first revision of a
1089 By default, grep only prints output for the first revision of a
1090 file in which it finds a match. To get it to print every revision
1090 file in which it finds a match. To get it to print every revision
1091 that contains a change in match status ("-" for a match that
1091 that contains a change in match status ("-" for a match that
1092 becomes a non-match, or "+" for a non-match that becomes a match),
1092 becomes a non-match, or "+" for a non-match that becomes a match),
1093 use the --all flag.
1093 use the --all flag.
1094 """
1094 """
1095 reflags = 0
1095 reflags = 0
1096 if opts['ignore_case']:
1096 if opts['ignore_case']:
1097 reflags |= re.I
1097 reflags |= re.I
1098 regexp = re.compile(pattern, reflags)
1098 regexp = re.compile(pattern, reflags)
1099 sep, eol = ':', '\n'
1099 sep, eol = ':', '\n'
1100 if opts['print0']:
1100 if opts['print0']:
1101 sep = eol = '\0'
1101 sep = eol = '\0'
1102
1102
1103 fcache = {}
1103 fcache = {}
1104 def getfile(fn):
1104 def getfile(fn):
1105 if fn not in fcache:
1105 if fn not in fcache:
1106 fcache[fn] = repo.file(fn)
1106 fcache[fn] = repo.file(fn)
1107 return fcache[fn]
1107 return fcache[fn]
1108
1108
1109 def matchlines(body):
1109 def matchlines(body):
1110 begin = 0
1110 begin = 0
1111 linenum = 0
1111 linenum = 0
1112 while True:
1112 while True:
1113 match = regexp.search(body, begin)
1113 match = regexp.search(body, begin)
1114 if not match:
1114 if not match:
1115 break
1115 break
1116 mstart, mend = match.span()
1116 mstart, mend = match.span()
1117 linenum += body.count('\n', begin, mstart) + 1
1117 linenum += body.count('\n', begin, mstart) + 1
1118 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1118 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1119 lend = body.find('\n', mend)
1119 lend = body.find('\n', mend)
1120 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1120 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1121 begin = lend + 1
1121 begin = lend + 1
1122
1122
1123 class linestate(object):
1123 class linestate(object):
1124 def __init__(self, line, linenum, colstart, colend):
1124 def __init__(self, line, linenum, colstart, colend):
1125 self.line = line
1125 self.line = line
1126 self.linenum = linenum
1126 self.linenum = linenum
1127 self.colstart = colstart
1127 self.colstart = colstart
1128 self.colend = colend
1128 self.colend = colend
1129
1129
1130 def __eq__(self, other):
1130 def __eq__(self, other):
1131 return self.line == other.line
1131 return self.line == other.line
1132
1132
1133 matches = {}
1133 matches = {}
1134 copies = {}
1134 copies = {}
1135 def grepbody(fn, rev, body):
1135 def grepbody(fn, rev, body):
1136 matches[rev].setdefault(fn, [])
1136 matches[rev].setdefault(fn, [])
1137 m = matches[rev][fn]
1137 m = matches[rev][fn]
1138 for lnum, cstart, cend, line in matchlines(body):
1138 for lnum, cstart, cend, line in matchlines(body):
1139 s = linestate(line, lnum, cstart, cend)
1139 s = linestate(line, lnum, cstart, cend)
1140 m.append(s)
1140 m.append(s)
1141
1141
1142 def difflinestates(a, b):
1142 def difflinestates(a, b):
1143 sm = difflib.SequenceMatcher(None, a, b)
1143 sm = difflib.SequenceMatcher(None, a, b)
1144 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1144 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1145 if tag == 'insert':
1145 if tag == 'insert':
1146 for i in xrange(blo, bhi):
1146 for i in xrange(blo, bhi):
1147 yield ('+', b[i])
1147 yield ('+', b[i])
1148 elif tag == 'delete':
1148 elif tag == 'delete':
1149 for i in xrange(alo, ahi):
1149 for i in xrange(alo, ahi):
1150 yield ('-', a[i])
1150 yield ('-', a[i])
1151 elif tag == 'replace':
1151 elif tag == 'replace':
1152 for i in xrange(alo, ahi):
1152 for i in xrange(alo, ahi):
1153 yield ('-', a[i])
1153 yield ('-', a[i])
1154 for i in xrange(blo, bhi):
1154 for i in xrange(blo, bhi):
1155 yield ('+', b[i])
1155 yield ('+', b[i])
1156
1156
1157 prev = {}
1157 prev = {}
1158 def display(fn, rev, states, prevstates):
1158 def display(fn, rev, states, prevstates):
1159 counts = {'-': 0, '+': 0}
1159 counts = {'-': 0, '+': 0}
1160 filerevmatches = {}
1160 filerevmatches = {}
1161 if incrementing or not opts['all']:
1161 if incrementing or not opts['all']:
1162 a, b, r = prevstates, states, rev
1162 a, b, r = prevstates, states, rev
1163 else:
1163 else:
1164 a, b, r = states, prevstates, prev.get(fn, -1)
1164 a, b, r = states, prevstates, prev.get(fn, -1)
1165 for change, l in difflinestates(a, b):
1165 for change, l in difflinestates(a, b):
1166 cols = [fn, str(r)]
1166 cols = [fn, str(r)]
1167 if opts['line_number']:
1167 if opts['line_number']:
1168 cols.append(str(l.linenum))
1168 cols.append(str(l.linenum))
1169 if opts['all']:
1169 if opts['all']:
1170 cols.append(change)
1170 cols.append(change)
1171 if opts['user']:
1171 if opts['user']:
1172 cols.append(ui.shortuser(get(r)[1]))
1172 cols.append(ui.shortuser(get(r)[1]))
1173 if opts['files_with_matches']:
1173 if opts['files_with_matches']:
1174 c = (fn, r)
1174 c = (fn, r)
1175 if c in filerevmatches:
1175 if c in filerevmatches:
1176 continue
1176 continue
1177 filerevmatches[c] = 1
1177 filerevmatches[c] = 1
1178 else:
1178 else:
1179 cols.append(l.line)
1179 cols.append(l.line)
1180 ui.write(sep.join(cols), eol)
1180 ui.write(sep.join(cols), eol)
1181 counts[change] += 1
1181 counts[change] += 1
1182 return counts['+'], counts['-']
1182 return counts['+'], counts['-']
1183
1183
1184 fstate = {}
1184 fstate = {}
1185 skip = {}
1185 skip = {}
1186 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1186 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1187 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1187 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1188 count = 0
1188 count = 0
1189 incrementing = False
1189 incrementing = False
1190 follow = opts.get('follow')
1190 follow = opts.get('follow')
1191 for st, rev, fns in changeiter:
1191 for st, rev, fns in changeiter:
1192 if st == 'window':
1192 if st == 'window':
1193 incrementing = rev
1193 incrementing = rev
1194 matches.clear()
1194 matches.clear()
1195 elif st == 'add':
1195 elif st == 'add':
1196 mf = repo.changectx(rev).manifest()
1196 mf = repo.changectx(rev).manifest()
1197 matches[rev] = {}
1197 matches[rev] = {}
1198 for fn in fns:
1198 for fn in fns:
1199 if fn in skip:
1199 if fn in skip:
1200 continue
1200 continue
1201 fstate.setdefault(fn, {})
1201 fstate.setdefault(fn, {})
1202 try:
1202 try:
1203 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1203 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1204 if follow:
1204 if follow:
1205 copied = getfile(fn).renamed(mf[fn])
1205 copied = getfile(fn).renamed(mf[fn])
1206 if copied:
1206 if copied:
1207 copies.setdefault(rev, {})[fn] = copied[0]
1207 copies.setdefault(rev, {})[fn] = copied[0]
1208 except KeyError:
1208 except KeyError:
1209 pass
1209 pass
1210 elif st == 'iter':
1210 elif st == 'iter':
1211 states = matches[rev].items()
1211 states = matches[rev].items()
1212 states.sort()
1212 states.sort()
1213 for fn, m in states:
1213 for fn, m in states:
1214 copy = copies.get(rev, {}).get(fn)
1214 copy = copies.get(rev, {}).get(fn)
1215 if fn in skip:
1215 if fn in skip:
1216 if copy:
1216 if copy:
1217 skip[copy] = True
1217 skip[copy] = True
1218 continue
1218 continue
1219 if incrementing or not opts['all'] or fstate[fn]:
1219 if incrementing or not opts['all'] or fstate[fn]:
1220 pos, neg = display(fn, rev, m, fstate[fn])
1220 pos, neg = display(fn, rev, m, fstate[fn])
1221 count += pos + neg
1221 count += pos + neg
1222 if pos and not opts['all']:
1222 if pos and not opts['all']:
1223 skip[fn] = True
1223 skip[fn] = True
1224 if copy:
1224 if copy:
1225 skip[copy] = True
1225 skip[copy] = True
1226 fstate[fn] = m
1226 fstate[fn] = m
1227 if copy:
1227 if copy:
1228 fstate[copy] = m
1228 fstate[copy] = m
1229 prev[fn] = rev
1229 prev[fn] = rev
1230
1230
1231 if not incrementing:
1231 if not incrementing:
1232 fstate = fstate.items()
1232 fstate = fstate.items()
1233 fstate.sort()
1233 fstate.sort()
1234 for fn, state in fstate:
1234 for fn, state in fstate:
1235 if fn in skip:
1235 if fn in skip:
1236 continue
1236 continue
1237 if fn not in copies.get(prev[fn], {}):
1237 if fn not in copies.get(prev[fn], {}):
1238 display(fn, rev, {}, state)
1238 display(fn, rev, {}, state)
1239 return (count == 0 and 1) or 0
1239 return (count == 0 and 1) or 0
1240
1240
1241 def heads(ui, repo, **opts):
1241 def heads(ui, repo, **opts):
1242 """show current repository heads
1242 """show current repository heads
1243
1243
1244 Show all repository head changesets.
1244 Show all repository head changesets.
1245
1245
1246 Repository "heads" are changesets that don't have children
1246 Repository "heads" are changesets that don't have children
1247 changesets. They are where development generally takes place and
1247 changesets. They are where development generally takes place and
1248 are the usual targets for update and merge operations.
1248 are the usual targets for update and merge operations.
1249 """
1249 """
1250 if opts['rev']:
1250 if opts['rev']:
1251 heads = repo.heads(repo.lookup(opts['rev']))
1251 heads = repo.heads(repo.lookup(opts['rev']))
1252 else:
1252 else:
1253 heads = repo.heads()
1253 heads = repo.heads()
1254 displayer = cmdutil.show_changeset(ui, repo, opts)
1254 displayer = cmdutil.show_changeset(ui, repo, opts)
1255 for n in heads:
1255 for n in heads:
1256 displayer.show(changenode=n)
1256 displayer.show(changenode=n)
1257
1257
1258 def help_(ui, name=None, with_version=False):
1258 def help_(ui, name=None, with_version=False):
1259 """show help for a command, extension, or list of commands
1259 """show help for a command, extension, or list of commands
1260
1260
1261 With no arguments, print a list of commands and short help.
1261 With no arguments, print a list of commands and short help.
1262
1262
1263 Given a command name, print help for that command.
1263 Given a command name, print help for that command.
1264
1264
1265 Given an extension name, print help for that extension, and the
1265 Given an extension name, print help for that extension, and the
1266 commands it provides."""
1266 commands it provides."""
1267 option_lists = []
1267 option_lists = []
1268
1268
1269 def helpcmd(name):
1269 def helpcmd(name):
1270 if with_version:
1270 if with_version:
1271 version_(ui)
1271 version_(ui)
1272 ui.write('\n')
1272 ui.write('\n')
1273 aliases, i = findcmd(ui, name)
1273 aliases, i = findcmd(ui, name)
1274 # synopsis
1274 # synopsis
1275 ui.write("%s\n\n" % i[2])
1275 ui.write("%s\n\n" % i[2])
1276
1276
1277 # description
1277 # description
1278 doc = i[0].__doc__
1278 doc = i[0].__doc__
1279 if not doc:
1279 if not doc:
1280 doc = _("(No help text available)")
1280 doc = _("(No help text available)")
1281 if ui.quiet:
1281 if ui.quiet:
1282 doc = doc.splitlines(0)[0]
1282 doc = doc.splitlines(0)[0]
1283 ui.write("%s\n" % doc.rstrip())
1283 ui.write("%s\n" % doc.rstrip())
1284
1284
1285 if not ui.quiet:
1285 if not ui.quiet:
1286 # aliases
1286 # aliases
1287 if len(aliases) > 1:
1287 if len(aliases) > 1:
1288 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1288 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1289
1289
1290 # options
1290 # options
1291 if i[1]:
1291 if i[1]:
1292 option_lists.append(("options", i[1]))
1292 option_lists.append(("options", i[1]))
1293
1293
1294 def helplist(select=None):
1294 def helplist(select=None):
1295 h = {}
1295 h = {}
1296 cmds = {}
1296 cmds = {}
1297 for c, e in table.items():
1297 for c, e in table.items():
1298 f = c.split("|", 1)[0]
1298 f = c.split("|", 1)[0]
1299 if select and not select(f):
1299 if select and not select(f):
1300 continue
1300 continue
1301 if name == "shortlist" and not f.startswith("^"):
1301 if name == "shortlist" and not f.startswith("^"):
1302 continue
1302 continue
1303 f = f.lstrip("^")
1303 f = f.lstrip("^")
1304 if not ui.debugflag and f.startswith("debug"):
1304 if not ui.debugflag and f.startswith("debug"):
1305 continue
1305 continue
1306 doc = e[0].__doc__
1306 doc = e[0].__doc__
1307 if not doc:
1307 if not doc:
1308 doc = _("(No help text available)")
1308 doc = _("(No help text available)")
1309 h[f] = doc.splitlines(0)[0].rstrip()
1309 h[f] = doc.splitlines(0)[0].rstrip()
1310 cmds[f] = c.lstrip("^")
1310 cmds[f] = c.lstrip("^")
1311
1311
1312 fns = h.keys()
1312 fns = h.keys()
1313 fns.sort()
1313 fns.sort()
1314 m = max(map(len, fns))
1314 m = max(map(len, fns))
1315 for f in fns:
1315 for f in fns:
1316 if ui.verbose:
1316 if ui.verbose:
1317 commands = cmds[f].replace("|",", ")
1317 commands = cmds[f].replace("|",", ")
1318 ui.write(" %s:\n %s\n"%(commands, h[f]))
1318 ui.write(" %s:\n %s\n"%(commands, h[f]))
1319 else:
1319 else:
1320 ui.write(' %-*s %s\n' % (m, f, h[f]))
1320 ui.write(' %-*s %s\n' % (m, f, h[f]))
1321
1321
1322 def helptopic(name):
1322 def helptopic(name):
1323 v = None
1323 v = None
1324 for i in help.helptable:
1324 for i in help.helptable:
1325 l = i.split('|')
1325 l = i.split('|')
1326 if name in l:
1326 if name in l:
1327 v = i
1327 v = i
1328 header = l[-1]
1328 header = l[-1]
1329 if not v:
1329 if not v:
1330 raise UnknownCommand(name)
1330 raise UnknownCommand(name)
1331
1331
1332 # description
1332 # description
1333 doc = help.helptable[v]
1333 doc = help.helptable[v]
1334 if not doc:
1334 if not doc:
1335 doc = _("(No help text available)")
1335 doc = _("(No help text available)")
1336 if callable(doc):
1336 if callable(doc):
1337 doc = doc()
1337 doc = doc()
1338
1338
1339 ui.write("%s\n" % header)
1339 ui.write("%s\n" % header)
1340 ui.write("%s\n" % doc.rstrip())
1340 ui.write("%s\n" % doc.rstrip())
1341
1341
1342 def helpext(name):
1342 def helpext(name):
1343 try:
1343 try:
1344 mod = findext(name)
1344 mod = findext(name)
1345 except KeyError:
1345 except KeyError:
1346 raise UnknownCommand(name)
1346 raise UnknownCommand(name)
1347
1347
1348 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1348 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1349 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1349 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1350 for d in doc[1:]:
1350 for d in doc[1:]:
1351 ui.write(d, '\n')
1351 ui.write(d, '\n')
1352
1352
1353 ui.status('\n')
1353 ui.status('\n')
1354
1354
1355 try:
1355 try:
1356 ct = mod.cmdtable
1356 ct = mod.cmdtable
1357 except AttributeError:
1357 except AttributeError:
1358 ui.status(_('no commands defined\n'))
1358 ui.status(_('no commands defined\n'))
1359 return
1359 return
1360
1360
1361 if ui.verbose:
1361 if ui.verbose:
1362 ui.status(_('list of commands:\n\n'))
1362 ui.status(_('list of commands:\n\n'))
1363 else:
1363 else:
1364 ui.status(_('list of commands (use "hg help -v %s" '
1364 ui.status(_('list of commands (use "hg help -v %s" '
1365 'to show aliases and global options):\n\n') % name)
1365 'to show aliases and global options):\n\n') % name)
1366
1366
1367 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1367 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1368 helplist(modcmds.has_key)
1368 helplist(modcmds.has_key)
1369
1369
1370 if name and name != 'shortlist':
1370 if name and name != 'shortlist':
1371 i = None
1371 i = None
1372 for f in (helpcmd, helptopic, helpext):
1372 for f in (helpcmd, helptopic, helpext):
1373 try:
1373 try:
1374 f(name)
1374 f(name)
1375 i = None
1375 i = None
1376 break
1376 break
1377 except UnknownCommand, inst:
1377 except UnknownCommand, inst:
1378 i = inst
1378 i = inst
1379 if i:
1379 if i:
1380 raise i
1380 raise i
1381
1381
1382 else:
1382 else:
1383 # program name
1383 # program name
1384 if ui.verbose or with_version:
1384 if ui.verbose or with_version:
1385 version_(ui)
1385 version_(ui)
1386 else:
1386 else:
1387 ui.status(_("Mercurial Distributed SCM\n"))
1387 ui.status(_("Mercurial Distributed SCM\n"))
1388 ui.status('\n')
1388 ui.status('\n')
1389
1389
1390 # list of commands
1390 # list of commands
1391 if name == "shortlist":
1391 if name == "shortlist":
1392 ui.status(_('basic commands (use "hg help" '
1392 ui.status(_('basic commands (use "hg help" '
1393 'for the full list or option "-v" for details):\n\n'))
1393 'for the full list or option "-v" for details):\n\n'))
1394 elif ui.verbose:
1394 elif ui.verbose:
1395 ui.status(_('list of commands:\n\n'))
1395 ui.status(_('list of commands:\n\n'))
1396 else:
1396 else:
1397 ui.status(_('list of commands (use "hg help -v" '
1397 ui.status(_('list of commands (use "hg help -v" '
1398 'to show aliases and global options):\n\n'))
1398 'to show aliases and global options):\n\n'))
1399
1399
1400 helplist()
1400 helplist()
1401
1401
1402 # global options
1402 # global options
1403 if ui.verbose:
1403 if ui.verbose:
1404 option_lists.append(("global options", globalopts))
1404 option_lists.append(("global options", globalopts))
1405
1405
1406 # list all option lists
1406 # list all option lists
1407 opt_output = []
1407 opt_output = []
1408 for title, options in option_lists:
1408 for title, options in option_lists:
1409 opt_output.append(("\n%s:\n" % title, None))
1409 opt_output.append(("\n%s:\n" % title, None))
1410 for shortopt, longopt, default, desc in options:
1410 for shortopt, longopt, default, desc in options:
1411 if "DEPRECATED" in desc and not ui.verbose: continue
1411 if "DEPRECATED" in desc and not ui.verbose: continue
1412 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1412 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1413 longopt and " --%s" % longopt),
1413 longopt and " --%s" % longopt),
1414 "%s%s" % (desc,
1414 "%s%s" % (desc,
1415 default
1415 default
1416 and _(" (default: %s)") % default
1416 and _(" (default: %s)") % default
1417 or "")))
1417 or "")))
1418
1418
1419 if opt_output:
1419 if opt_output:
1420 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1420 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1421 for first, second in opt_output:
1421 for first, second in opt_output:
1422 if second:
1422 if second:
1423 ui.write(" %-*s %s\n" % (opts_len, first, second))
1423 ui.write(" %-*s %s\n" % (opts_len, first, second))
1424 else:
1424 else:
1425 ui.write("%s\n" % first)
1425 ui.write("%s\n" % first)
1426
1426
1427 def identify(ui, repo):
1427 def identify(ui, repo):
1428 """print information about the working copy
1428 """print information about the working copy
1429
1429
1430 Print a short summary of the current state of the repo.
1430 Print a short summary of the current state of the repo.
1431
1431
1432 This summary identifies the repository state using one or two parent
1432 This summary identifies the repository state using one or two parent
1433 hash identifiers, followed by a "+" if there are uncommitted changes
1433 hash identifiers, followed by a "+" if there are uncommitted changes
1434 in the working directory, followed by a list of tags for this revision.
1434 in the working directory, followed by a list of tags for this revision.
1435 """
1435 """
1436 parents = [p for p in repo.dirstate.parents() if p != nullid]
1436 parents = [p for p in repo.dirstate.parents() if p != nullid]
1437 if not parents:
1437 if not parents:
1438 ui.write(_("unknown\n"))
1438 ui.write(_("unknown\n"))
1439 return
1439 return
1440
1440
1441 hexfunc = ui.debugflag and hex or short
1441 hexfunc = ui.debugflag and hex or short
1442 modified, added, removed, deleted = repo.status()[:4]
1442 modified, added, removed, deleted = repo.status()[:4]
1443 output = ["%s%s" %
1443 output = ["%s%s" %
1444 ('+'.join([hexfunc(parent) for parent in parents]),
1444 ('+'.join([hexfunc(parent) for parent in parents]),
1445 (modified or added or removed or deleted) and "+" or "")]
1445 (modified or added or removed or deleted) and "+" or "")]
1446
1446
1447 if not ui.quiet:
1447 if not ui.quiet:
1448
1448
1449 branch = util.tolocal(repo.workingctx().branch())
1449 branch = util.tolocal(repo.workingctx().branch())
1450 if branch:
1450 if branch != 'default':
1451 output.append("(%s)" % branch)
1451 output.append("(%s)" % branch)
1452
1452
1453 # multiple tags for a single parent separated by '/'
1453 # multiple tags for a single parent separated by '/'
1454 parenttags = ['/'.join(tags)
1454 parenttags = ['/'.join(tags)
1455 for tags in map(repo.nodetags, parents) if tags]
1455 for tags in map(repo.nodetags, parents) if tags]
1456 # tags for multiple parents separated by ' + '
1456 # tags for multiple parents separated by ' + '
1457 if parenttags:
1457 if parenttags:
1458 output.append(' + '.join(parenttags))
1458 output.append(' + '.join(parenttags))
1459
1459
1460 ui.write("%s\n" % ' '.join(output))
1460 ui.write("%s\n" % ' '.join(output))
1461
1461
1462 def import_(ui, repo, patch1, *patches, **opts):
1462 def import_(ui, repo, patch1, *patches, **opts):
1463 """import an ordered set of patches
1463 """import an ordered set of patches
1464
1464
1465 Import a list of patches and commit them individually.
1465 Import a list of patches and commit them individually.
1466
1466
1467 If there are outstanding changes in the working directory, import
1467 If there are outstanding changes in the working directory, import
1468 will abort unless given the -f flag.
1468 will abort unless given the -f flag.
1469
1469
1470 You can import a patch straight from a mail message. Even patches
1470 You can import a patch straight from a mail message. Even patches
1471 as attachments work (body part must be type text/plain or
1471 as attachments work (body part must be type text/plain or
1472 text/x-patch to be used). From and Subject headers of email
1472 text/x-patch to be used). From and Subject headers of email
1473 message are used as default committer and commit message. All
1473 message are used as default committer and commit message. All
1474 text/plain body parts before first diff are added to commit
1474 text/plain body parts before first diff are added to commit
1475 message.
1475 message.
1476
1476
1477 If imported patch was generated by hg export, user and description
1477 If imported patch was generated by hg export, user and description
1478 from patch override values from message headers and body. Values
1478 from patch override values from message headers and body. Values
1479 given on command line with -m and -u override these.
1479 given on command line with -m and -u override these.
1480
1480
1481 To read a patch from standard input, use patch name "-".
1481 To read a patch from standard input, use patch name "-".
1482 """
1482 """
1483 patches = (patch1,) + patches
1483 patches = (patch1,) + patches
1484
1484
1485 if not opts['force']:
1485 if not opts['force']:
1486 bail_if_changed(repo)
1486 bail_if_changed(repo)
1487
1487
1488 d = opts["base"]
1488 d = opts["base"]
1489 strip = opts["strip"]
1489 strip = opts["strip"]
1490
1490
1491 wlock = repo.wlock()
1491 wlock = repo.wlock()
1492 lock = repo.lock()
1492 lock = repo.lock()
1493
1493
1494 for p in patches:
1494 for p in patches:
1495 pf = os.path.join(d, p)
1495 pf = os.path.join(d, p)
1496
1496
1497 if pf == '-':
1497 if pf == '-':
1498 ui.status(_("applying patch from stdin\n"))
1498 ui.status(_("applying patch from stdin\n"))
1499 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1499 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1500 else:
1500 else:
1501 ui.status(_("applying %s\n") % p)
1501 ui.status(_("applying %s\n") % p)
1502 tmpname, message, user, date = patch.extract(ui, file(pf))
1502 tmpname, message, user, date = patch.extract(ui, file(pf))
1503
1503
1504 if tmpname is None:
1504 if tmpname is None:
1505 raise util.Abort(_('no diffs found'))
1505 raise util.Abort(_('no diffs found'))
1506
1506
1507 try:
1507 try:
1508 cmdline_message = logmessage(opts)
1508 cmdline_message = logmessage(opts)
1509 if cmdline_message:
1509 if cmdline_message:
1510 # pickup the cmdline msg
1510 # pickup the cmdline msg
1511 message = cmdline_message
1511 message = cmdline_message
1512 elif message:
1512 elif message:
1513 # pickup the patch msg
1513 # pickup the patch msg
1514 message = message.strip()
1514 message = message.strip()
1515 else:
1515 else:
1516 # launch the editor
1516 # launch the editor
1517 message = None
1517 message = None
1518 ui.debug(_('message:\n%s\n') % message)
1518 ui.debug(_('message:\n%s\n') % message)
1519
1519
1520 files = {}
1520 files = {}
1521 try:
1521 try:
1522 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1522 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1523 files=files)
1523 files=files)
1524 finally:
1524 finally:
1525 files = patch.updatedir(ui, repo, files, wlock=wlock)
1525 files = patch.updatedir(ui, repo, files, wlock=wlock)
1526 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1526 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1527 finally:
1527 finally:
1528 os.unlink(tmpname)
1528 os.unlink(tmpname)
1529
1529
1530 def incoming(ui, repo, source="default", **opts):
1530 def incoming(ui, repo, source="default", **opts):
1531 """show new changesets found in source
1531 """show new changesets found in source
1532
1532
1533 Show new changesets found in the specified path/URL or the default
1533 Show new changesets found in the specified path/URL or the default
1534 pull location. These are the changesets that would be pulled if a pull
1534 pull location. These are the changesets that would be pulled if a pull
1535 was requested.
1535 was requested.
1536
1536
1537 For remote repository, using --bundle avoids downloading the changesets
1537 For remote repository, using --bundle avoids downloading the changesets
1538 twice if the incoming is followed by a pull.
1538 twice if the incoming is followed by a pull.
1539
1539
1540 See pull for valid source format details.
1540 See pull for valid source format details.
1541 """
1541 """
1542 source = ui.expandpath(source)
1542 source = ui.expandpath(source)
1543 setremoteconfig(ui, opts)
1543 setremoteconfig(ui, opts)
1544
1544
1545 other = hg.repository(ui, source)
1545 other = hg.repository(ui, source)
1546 incoming = repo.findincoming(other, force=opts["force"])
1546 incoming = repo.findincoming(other, force=opts["force"])
1547 if not incoming:
1547 if not incoming:
1548 ui.status(_("no changes found\n"))
1548 ui.status(_("no changes found\n"))
1549 return
1549 return
1550
1550
1551 cleanup = None
1551 cleanup = None
1552 try:
1552 try:
1553 fname = opts["bundle"]
1553 fname = opts["bundle"]
1554 if fname or not other.local():
1554 if fname or not other.local():
1555 # create a bundle (uncompressed if other repo is not local)
1555 # create a bundle (uncompressed if other repo is not local)
1556 cg = other.changegroup(incoming, "incoming")
1556 cg = other.changegroup(incoming, "incoming")
1557 bundletype = other.local() and "HG10BZ" or "HG10UN"
1557 bundletype = other.local() and "HG10BZ" or "HG10UN"
1558 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1558 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1559 # keep written bundle?
1559 # keep written bundle?
1560 if opts["bundle"]:
1560 if opts["bundle"]:
1561 cleanup = None
1561 cleanup = None
1562 if not other.local():
1562 if not other.local():
1563 # use the created uncompressed bundlerepo
1563 # use the created uncompressed bundlerepo
1564 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1564 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1565
1565
1566 revs = None
1566 revs = None
1567 if opts['rev']:
1567 if opts['rev']:
1568 revs = [other.lookup(rev) for rev in opts['rev']]
1568 revs = [other.lookup(rev) for rev in opts['rev']]
1569 o = other.changelog.nodesbetween(incoming, revs)[0]
1569 o = other.changelog.nodesbetween(incoming, revs)[0]
1570 if opts['newest_first']:
1570 if opts['newest_first']:
1571 o.reverse()
1571 o.reverse()
1572 displayer = cmdutil.show_changeset(ui, other, opts)
1572 displayer = cmdutil.show_changeset(ui, other, opts)
1573 for n in o:
1573 for n in o:
1574 parents = [p for p in other.changelog.parents(n) if p != nullid]
1574 parents = [p for p in other.changelog.parents(n) if p != nullid]
1575 if opts['no_merges'] and len(parents) == 2:
1575 if opts['no_merges'] and len(parents) == 2:
1576 continue
1576 continue
1577 displayer.show(changenode=n)
1577 displayer.show(changenode=n)
1578 finally:
1578 finally:
1579 if hasattr(other, 'close'):
1579 if hasattr(other, 'close'):
1580 other.close()
1580 other.close()
1581 if cleanup:
1581 if cleanup:
1582 os.unlink(cleanup)
1582 os.unlink(cleanup)
1583
1583
1584 def init(ui, dest=".", **opts):
1584 def init(ui, dest=".", **opts):
1585 """create a new repository in the given directory
1585 """create a new repository in the given directory
1586
1586
1587 Initialize a new repository in the given directory. If the given
1587 Initialize a new repository in the given directory. If the given
1588 directory does not exist, it is created.
1588 directory does not exist, it is created.
1589
1589
1590 If no directory is given, the current directory is used.
1590 If no directory is given, the current directory is used.
1591
1591
1592 It is possible to specify an ssh:// URL as the destination.
1592 It is possible to specify an ssh:// URL as the destination.
1593 Look at the help text for the pull command for important details
1593 Look at the help text for the pull command for important details
1594 about ssh:// URLs.
1594 about ssh:// URLs.
1595 """
1595 """
1596 setremoteconfig(ui, opts)
1596 setremoteconfig(ui, opts)
1597 hg.repository(ui, dest, create=1)
1597 hg.repository(ui, dest, create=1)
1598
1598
1599 def locate(ui, repo, *pats, **opts):
1599 def locate(ui, repo, *pats, **opts):
1600 """locate files matching specific patterns
1600 """locate files matching specific patterns
1601
1601
1602 Print all files under Mercurial control whose names match the
1602 Print all files under Mercurial control whose names match the
1603 given patterns.
1603 given patterns.
1604
1604
1605 This command searches the current directory and its
1605 This command searches the current directory and its
1606 subdirectories. To search an entire repository, move to the root
1606 subdirectories. To search an entire repository, move to the root
1607 of the repository.
1607 of the repository.
1608
1608
1609 If no patterns are given to match, this command prints all file
1609 If no patterns are given to match, this command prints all file
1610 names.
1610 names.
1611
1611
1612 If you want to feed the output of this command into the "xargs"
1612 If you want to feed the output of this command into the "xargs"
1613 command, use the "-0" option to both this command and "xargs".
1613 command, use the "-0" option to both this command and "xargs".
1614 This will avoid the problem of "xargs" treating single filenames
1614 This will avoid the problem of "xargs" treating single filenames
1615 that contain white space as multiple filenames.
1615 that contain white space as multiple filenames.
1616 """
1616 """
1617 end = opts['print0'] and '\0' or '\n'
1617 end = opts['print0'] and '\0' or '\n'
1618 rev = opts['rev']
1618 rev = opts['rev']
1619 if rev:
1619 if rev:
1620 node = repo.lookup(rev)
1620 node = repo.lookup(rev)
1621 else:
1621 else:
1622 node = None
1622 node = None
1623
1623
1624 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1624 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1625 head='(?:.*/|)'):
1625 head='(?:.*/|)'):
1626 if not node and repo.dirstate.state(abs) == '?':
1626 if not node and repo.dirstate.state(abs) == '?':
1627 continue
1627 continue
1628 if opts['fullpath']:
1628 if opts['fullpath']:
1629 ui.write(os.path.join(repo.root, abs), end)
1629 ui.write(os.path.join(repo.root, abs), end)
1630 else:
1630 else:
1631 ui.write(((pats and rel) or abs), end)
1631 ui.write(((pats and rel) or abs), end)
1632
1632
1633 def log(ui, repo, *pats, **opts):
1633 def log(ui, repo, *pats, **opts):
1634 """show revision history of entire repository or files
1634 """show revision history of entire repository or files
1635
1635
1636 Print the revision history of the specified files or the entire
1636 Print the revision history of the specified files or the entire
1637 project.
1637 project.
1638
1638
1639 File history is shown without following rename or copy history of
1639 File history is shown without following rename or copy history of
1640 files. Use -f/--follow with a file name to follow history across
1640 files. Use -f/--follow with a file name to follow history across
1641 renames and copies. --follow without a file name will only show
1641 renames and copies. --follow without a file name will only show
1642 ancestors or descendants of the starting revision. --follow-first
1642 ancestors or descendants of the starting revision. --follow-first
1643 only follows the first parent of merge revisions.
1643 only follows the first parent of merge revisions.
1644
1644
1645 If no revision range is specified, the default is tip:0 unless
1645 If no revision range is specified, the default is tip:0 unless
1646 --follow is set, in which case the working directory parent is
1646 --follow is set, in which case the working directory parent is
1647 used as the starting revision.
1647 used as the starting revision.
1648
1648
1649 By default this command outputs: changeset id and hash, tags,
1649 By default this command outputs: changeset id and hash, tags,
1650 non-trivial parents, user, date and time, and a summary for each
1650 non-trivial parents, user, date and time, and a summary for each
1651 commit. When the -v/--verbose switch is used, the list of changed
1651 commit. When the -v/--verbose switch is used, the list of changed
1652 files and full commit message is shown.
1652 files and full commit message is shown.
1653
1653
1654 NOTE: log -p may generate unexpected diff output for merge
1654 NOTE: log -p may generate unexpected diff output for merge
1655 changesets, as it will compare the merge changeset against its
1655 changesets, as it will compare the merge changeset against its
1656 first parent only. Also, the files: list will only reflect files
1656 first parent only. Also, the files: list will only reflect files
1657 that are different from BOTH parents.
1657 that are different from BOTH parents.
1658
1658
1659 """
1659 """
1660
1660
1661 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1661 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1662 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1662 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1663
1663
1664 if opts['limit']:
1664 if opts['limit']:
1665 try:
1665 try:
1666 limit = int(opts['limit'])
1666 limit = int(opts['limit'])
1667 except ValueError:
1667 except ValueError:
1668 raise util.Abort(_('limit must be a positive integer'))
1668 raise util.Abort(_('limit must be a positive integer'))
1669 if limit <= 0: raise util.Abort(_('limit must be positive'))
1669 if limit <= 0: raise util.Abort(_('limit must be positive'))
1670 else:
1670 else:
1671 limit = sys.maxint
1671 limit = sys.maxint
1672 count = 0
1672 count = 0
1673
1673
1674 if opts['copies'] and opts['rev']:
1674 if opts['copies'] and opts['rev']:
1675 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1675 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1676 else:
1676 else:
1677 endrev = repo.changelog.count()
1677 endrev = repo.changelog.count()
1678 rcache = {}
1678 rcache = {}
1679 ncache = {}
1679 ncache = {}
1680 dcache = []
1680 dcache = []
1681 def getrenamed(fn, rev, man):
1681 def getrenamed(fn, rev, man):
1682 '''looks up all renames for a file (up to endrev) the first
1682 '''looks up all renames for a file (up to endrev) the first
1683 time the file is given. It indexes on the changerev and only
1683 time the file is given. It indexes on the changerev and only
1684 parses the manifest if linkrev != changerev.
1684 parses the manifest if linkrev != changerev.
1685 Returns rename info for fn at changerev rev.'''
1685 Returns rename info for fn at changerev rev.'''
1686 if fn not in rcache:
1686 if fn not in rcache:
1687 rcache[fn] = {}
1687 rcache[fn] = {}
1688 ncache[fn] = {}
1688 ncache[fn] = {}
1689 fl = repo.file(fn)
1689 fl = repo.file(fn)
1690 for i in xrange(fl.count()):
1690 for i in xrange(fl.count()):
1691 node = fl.node(i)
1691 node = fl.node(i)
1692 lr = fl.linkrev(node)
1692 lr = fl.linkrev(node)
1693 renamed = fl.renamed(node)
1693 renamed = fl.renamed(node)
1694 rcache[fn][lr] = renamed
1694 rcache[fn][lr] = renamed
1695 if renamed:
1695 if renamed:
1696 ncache[fn][node] = renamed
1696 ncache[fn][node] = renamed
1697 if lr >= endrev:
1697 if lr >= endrev:
1698 break
1698 break
1699 if rev in rcache[fn]:
1699 if rev in rcache[fn]:
1700 return rcache[fn][rev]
1700 return rcache[fn][rev]
1701 mr = repo.manifest.rev(man)
1701 mr = repo.manifest.rev(man)
1702 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1702 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1703 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1703 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1704 if not dcache or dcache[0] != man:
1704 if not dcache or dcache[0] != man:
1705 dcache[:] = [man, repo.manifest.readdelta(man)]
1705 dcache[:] = [man, repo.manifest.readdelta(man)]
1706 if fn in dcache[1]:
1706 if fn in dcache[1]:
1707 return ncache[fn].get(dcache[1][fn])
1707 return ncache[fn].get(dcache[1][fn])
1708 return None
1708 return None
1709
1709
1710 df = False
1710 df = False
1711 if opts["date"]:
1711 if opts["date"]:
1712 df = util.matchdate(opts["date"])
1712 df = util.matchdate(opts["date"])
1713
1713
1714
1714
1715 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1715 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1716 for st, rev, fns in changeiter:
1716 for st, rev, fns in changeiter:
1717 if st == 'add':
1717 if st == 'add':
1718 changenode = repo.changelog.node(rev)
1718 changenode = repo.changelog.node(rev)
1719 parents = [p for p in repo.changelog.parentrevs(rev)
1719 parents = [p for p in repo.changelog.parentrevs(rev)
1720 if p != nullrev]
1720 if p != nullrev]
1721 if opts['no_merges'] and len(parents) == 2:
1721 if opts['no_merges'] and len(parents) == 2:
1722 continue
1722 continue
1723 if opts['only_merges'] and len(parents) != 2:
1723 if opts['only_merges'] and len(parents) != 2:
1724 continue
1724 continue
1725
1725
1726 if df:
1726 if df:
1727 changes = get(rev)
1727 changes = get(rev)
1728 if not df(changes[2][0]):
1728 if not df(changes[2][0]):
1729 continue
1729 continue
1730
1730
1731 if opts['keyword']:
1731 if opts['keyword']:
1732 changes = get(rev)
1732 changes = get(rev)
1733 miss = 0
1733 miss = 0
1734 for k in [kw.lower() for kw in opts['keyword']]:
1734 for k in [kw.lower() for kw in opts['keyword']]:
1735 if not (k in changes[1].lower() or
1735 if not (k in changes[1].lower() or
1736 k in changes[4].lower() or
1736 k in changes[4].lower() or
1737 k in " ".join(changes[3][:20]).lower()):
1737 k in " ".join(changes[3][:20]).lower()):
1738 miss = 1
1738 miss = 1
1739 break
1739 break
1740 if miss:
1740 if miss:
1741 continue
1741 continue
1742
1742
1743 copies = []
1743 copies = []
1744 if opts.get('copies') and rev:
1744 if opts.get('copies') and rev:
1745 mf = get(rev)[0]
1745 mf = get(rev)[0]
1746 for fn in get(rev)[3]:
1746 for fn in get(rev)[3]:
1747 rename = getrenamed(fn, rev, mf)
1747 rename = getrenamed(fn, rev, mf)
1748 if rename:
1748 if rename:
1749 copies.append((fn, rename[0]))
1749 copies.append((fn, rename[0]))
1750 displayer.show(rev, changenode, copies=copies)
1750 displayer.show(rev, changenode, copies=copies)
1751 elif st == 'iter':
1751 elif st == 'iter':
1752 if count == limit: break
1752 if count == limit: break
1753 if displayer.flush(rev):
1753 if displayer.flush(rev):
1754 count += 1
1754 count += 1
1755
1755
1756 def manifest(ui, repo, rev=None):
1756 def manifest(ui, repo, rev=None):
1757 """output the current or given revision of the project manifest
1757 """output the current or given revision of the project manifest
1758
1758
1759 Print a list of version controlled files for the given revision.
1759 Print a list of version controlled files for the given revision.
1760 If no revision is given, the parent of the working directory is used,
1760 If no revision is given, the parent of the working directory is used,
1761 or tip if no revision is checked out.
1761 or tip if no revision is checked out.
1762
1762
1763 The manifest is the list of files being version controlled. If no revision
1763 The manifest is the list of files being version controlled. If no revision
1764 is given then the first parent of the working directory is used.
1764 is given then the first parent of the working directory is used.
1765
1765
1766 With -v flag, print file permissions. With --debug flag, print
1766 With -v flag, print file permissions. With --debug flag, print
1767 file revision hashes.
1767 file revision hashes.
1768 """
1768 """
1769
1769
1770 m = repo.changectx(rev).manifest()
1770 m = repo.changectx(rev).manifest()
1771 files = m.keys()
1771 files = m.keys()
1772 files.sort()
1772 files.sort()
1773
1773
1774 for f in files:
1774 for f in files:
1775 if ui.debugflag:
1775 if ui.debugflag:
1776 ui.write("%40s " % hex(m[f]))
1776 ui.write("%40s " % hex(m[f]))
1777 if ui.verbose:
1777 if ui.verbose:
1778 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1778 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1779 ui.write("%s\n" % f)
1779 ui.write("%s\n" % f)
1780
1780
1781 def merge(ui, repo, node=None, force=None, branch=None):
1781 def merge(ui, repo, node=None, force=None, branch=None):
1782 """merge working directory with another revision
1782 """merge working directory with another revision
1783
1783
1784 Merge the contents of the current working directory and the
1784 Merge the contents of the current working directory and the
1785 requested revision. Files that changed between either parent are
1785 requested revision. Files that changed between either parent are
1786 marked as changed for the next commit and a commit must be
1786 marked as changed for the next commit and a commit must be
1787 performed before any further updates are allowed.
1787 performed before any further updates are allowed.
1788
1788
1789 If no revision is specified, the working directory's parent is a
1789 If no revision is specified, the working directory's parent is a
1790 head revision, and the repository contains exactly one other head,
1790 head revision, and the repository contains exactly one other head,
1791 the other head is merged with by default. Otherwise, an explicit
1791 the other head is merged with by default. Otherwise, an explicit
1792 revision to merge with must be provided.
1792 revision to merge with must be provided.
1793 """
1793 """
1794
1794
1795 if node or branch:
1795 if node or branch:
1796 node = _lookup(repo, node, branch)
1796 node = _lookup(repo, node, branch)
1797 else:
1797 else:
1798 heads = repo.heads()
1798 heads = repo.heads()
1799 if len(heads) > 2:
1799 if len(heads) > 2:
1800 raise util.Abort(_('repo has %d heads - '
1800 raise util.Abort(_('repo has %d heads - '
1801 'please merge with an explicit rev') %
1801 'please merge with an explicit rev') %
1802 len(heads))
1802 len(heads))
1803 if len(heads) == 1:
1803 if len(heads) == 1:
1804 raise util.Abort(_('there is nothing to merge - '
1804 raise util.Abort(_('there is nothing to merge - '
1805 'use "hg update" instead'))
1805 'use "hg update" instead'))
1806 parent = repo.dirstate.parents()[0]
1806 parent = repo.dirstate.parents()[0]
1807 if parent not in heads:
1807 if parent not in heads:
1808 raise util.Abort(_('working dir not at a head rev - '
1808 raise util.Abort(_('working dir not at a head rev - '
1809 'use "hg update" or merge with an explicit rev'))
1809 'use "hg update" or merge with an explicit rev'))
1810 node = parent == heads[0] and heads[-1] or heads[0]
1810 node = parent == heads[0] and heads[-1] or heads[0]
1811 return hg.merge(repo, node, force=force)
1811 return hg.merge(repo, node, force=force)
1812
1812
1813 def outgoing(ui, repo, dest=None, **opts):
1813 def outgoing(ui, repo, dest=None, **opts):
1814 """show changesets not found in destination
1814 """show changesets not found in destination
1815
1815
1816 Show changesets not found in the specified destination repository or
1816 Show changesets not found in the specified destination repository or
1817 the default push location. These are the changesets that would be pushed
1817 the default push location. These are the changesets that would be pushed
1818 if a push was requested.
1818 if a push was requested.
1819
1819
1820 See pull for valid destination format details.
1820 See pull for valid destination format details.
1821 """
1821 """
1822 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1822 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1823 setremoteconfig(ui, opts)
1823 setremoteconfig(ui, opts)
1824 revs = None
1824 revs = None
1825 if opts['rev']:
1825 if opts['rev']:
1826 revs = [repo.lookup(rev) for rev in opts['rev']]
1826 revs = [repo.lookup(rev) for rev in opts['rev']]
1827
1827
1828 other = hg.repository(ui, dest)
1828 other = hg.repository(ui, dest)
1829 o = repo.findoutgoing(other, force=opts['force'])
1829 o = repo.findoutgoing(other, force=opts['force'])
1830 if not o:
1830 if not o:
1831 ui.status(_("no changes found\n"))
1831 ui.status(_("no changes found\n"))
1832 return
1832 return
1833 o = repo.changelog.nodesbetween(o, revs)[0]
1833 o = repo.changelog.nodesbetween(o, revs)[0]
1834 if opts['newest_first']:
1834 if opts['newest_first']:
1835 o.reverse()
1835 o.reverse()
1836 displayer = cmdutil.show_changeset(ui, repo, opts)
1836 displayer = cmdutil.show_changeset(ui, repo, opts)
1837 for n in o:
1837 for n in o:
1838 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1838 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1839 if opts['no_merges'] and len(parents) == 2:
1839 if opts['no_merges'] and len(parents) == 2:
1840 continue
1840 continue
1841 displayer.show(changenode=n)
1841 displayer.show(changenode=n)
1842
1842
1843 def parents(ui, repo, file_=None, **opts):
1843 def parents(ui, repo, file_=None, **opts):
1844 """show the parents of the working dir or revision
1844 """show the parents of the working dir or revision
1845
1845
1846 Print the working directory's parent revisions.
1846 Print the working directory's parent revisions.
1847 """
1847 """
1848 rev = opts.get('rev')
1848 rev = opts.get('rev')
1849 if rev:
1849 if rev:
1850 if file_:
1850 if file_:
1851 ctx = repo.filectx(file_, changeid=rev)
1851 ctx = repo.filectx(file_, changeid=rev)
1852 else:
1852 else:
1853 ctx = repo.changectx(rev)
1853 ctx = repo.changectx(rev)
1854 p = [cp.node() for cp in ctx.parents()]
1854 p = [cp.node() for cp in ctx.parents()]
1855 else:
1855 else:
1856 p = repo.dirstate.parents()
1856 p = repo.dirstate.parents()
1857
1857
1858 displayer = cmdutil.show_changeset(ui, repo, opts)
1858 displayer = cmdutil.show_changeset(ui, repo, opts)
1859 for n in p:
1859 for n in p:
1860 if n != nullid:
1860 if n != nullid:
1861 displayer.show(changenode=n)
1861 displayer.show(changenode=n)
1862
1862
1863 def paths(ui, repo, search=None):
1863 def paths(ui, repo, search=None):
1864 """show definition of symbolic path names
1864 """show definition of symbolic path names
1865
1865
1866 Show definition of symbolic path name NAME. If no name is given, show
1866 Show definition of symbolic path name NAME. If no name is given, show
1867 definition of available names.
1867 definition of available names.
1868
1868
1869 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1869 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1870 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1870 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1871 """
1871 """
1872 if search:
1872 if search:
1873 for name, path in ui.configitems("paths"):
1873 for name, path in ui.configitems("paths"):
1874 if name == search:
1874 if name == search:
1875 ui.write("%s\n" % path)
1875 ui.write("%s\n" % path)
1876 return
1876 return
1877 ui.warn(_("not found!\n"))
1877 ui.warn(_("not found!\n"))
1878 return 1
1878 return 1
1879 else:
1879 else:
1880 for name, path in ui.configitems("paths"):
1880 for name, path in ui.configitems("paths"):
1881 ui.write("%s = %s\n" % (name, path))
1881 ui.write("%s = %s\n" % (name, path))
1882
1882
1883 def postincoming(ui, repo, modheads, optupdate):
1883 def postincoming(ui, repo, modheads, optupdate):
1884 if modheads == 0:
1884 if modheads == 0:
1885 return
1885 return
1886 if optupdate:
1886 if optupdate:
1887 if modheads == 1:
1887 if modheads == 1:
1888 return hg.update(repo, repo.changelog.tip()) # update
1888 return hg.update(repo, repo.changelog.tip()) # update
1889 else:
1889 else:
1890 ui.status(_("not updating, since new heads added\n"))
1890 ui.status(_("not updating, since new heads added\n"))
1891 if modheads > 1:
1891 if modheads > 1:
1892 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1892 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1893 else:
1893 else:
1894 ui.status(_("(run 'hg update' to get a working copy)\n"))
1894 ui.status(_("(run 'hg update' to get a working copy)\n"))
1895
1895
1896 def pull(ui, repo, source="default", **opts):
1896 def pull(ui, repo, source="default", **opts):
1897 """pull changes from the specified source
1897 """pull changes from the specified source
1898
1898
1899 Pull changes from a remote repository to a local one.
1899 Pull changes from a remote repository to a local one.
1900
1900
1901 This finds all changes from the repository at the specified path
1901 This finds all changes from the repository at the specified path
1902 or URL and adds them to the local repository. By default, this
1902 or URL and adds them to the local repository. By default, this
1903 does not update the copy of the project in the working directory.
1903 does not update the copy of the project in the working directory.
1904
1904
1905 Valid URLs are of the form:
1905 Valid URLs are of the form:
1906
1906
1907 local/filesystem/path (or file://local/filesystem/path)
1907 local/filesystem/path (or file://local/filesystem/path)
1908 http://[user@]host[:port]/[path]
1908 http://[user@]host[:port]/[path]
1909 https://[user@]host[:port]/[path]
1909 https://[user@]host[:port]/[path]
1910 ssh://[user@]host[:port]/[path]
1910 ssh://[user@]host[:port]/[path]
1911 static-http://host[:port]/[path]
1911 static-http://host[:port]/[path]
1912
1912
1913 Paths in the local filesystem can either point to Mercurial
1913 Paths in the local filesystem can either point to Mercurial
1914 repositories or to bundle files (as created by 'hg bundle' or
1914 repositories or to bundle files (as created by 'hg bundle' or
1915 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1915 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1916 allows access to a Mercurial repository where you simply use a web
1916 allows access to a Mercurial repository where you simply use a web
1917 server to publish the .hg directory as static content.
1917 server to publish the .hg directory as static content.
1918
1918
1919 Some notes about using SSH with Mercurial:
1919 Some notes about using SSH with Mercurial:
1920 - SSH requires an accessible shell account on the destination machine
1920 - SSH requires an accessible shell account on the destination machine
1921 and a copy of hg in the remote path or specified with as remotecmd.
1921 and a copy of hg in the remote path or specified with as remotecmd.
1922 - path is relative to the remote user's home directory by default.
1922 - path is relative to the remote user's home directory by default.
1923 Use an extra slash at the start of a path to specify an absolute path:
1923 Use an extra slash at the start of a path to specify an absolute path:
1924 ssh://example.com//tmp/repository
1924 ssh://example.com//tmp/repository
1925 - Mercurial doesn't use its own compression via SSH; the right thing
1925 - Mercurial doesn't use its own compression via SSH; the right thing
1926 to do is to configure it in your ~/.ssh/config, e.g.:
1926 to do is to configure it in your ~/.ssh/config, e.g.:
1927 Host *.mylocalnetwork.example.com
1927 Host *.mylocalnetwork.example.com
1928 Compression no
1928 Compression no
1929 Host *
1929 Host *
1930 Compression yes
1930 Compression yes
1931 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1931 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1932 with the --ssh command line option.
1932 with the --ssh command line option.
1933 """
1933 """
1934 source = ui.expandpath(source)
1934 source = ui.expandpath(source)
1935 setremoteconfig(ui, opts)
1935 setremoteconfig(ui, opts)
1936
1936
1937 other = hg.repository(ui, source)
1937 other = hg.repository(ui, source)
1938 ui.status(_('pulling from %s\n') % (source))
1938 ui.status(_('pulling from %s\n') % (source))
1939 revs = None
1939 revs = None
1940 if opts['rev']:
1940 if opts['rev']:
1941 if 'lookup' in other.capabilities:
1941 if 'lookup' in other.capabilities:
1942 revs = [other.lookup(rev) for rev in opts['rev']]
1942 revs = [other.lookup(rev) for rev in opts['rev']]
1943 else:
1943 else:
1944 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1944 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1945 raise util.Abort(error)
1945 raise util.Abort(error)
1946 modheads = repo.pull(other, heads=revs, force=opts['force'])
1946 modheads = repo.pull(other, heads=revs, force=opts['force'])
1947 return postincoming(ui, repo, modheads, opts['update'])
1947 return postincoming(ui, repo, modheads, opts['update'])
1948
1948
1949 def push(ui, repo, dest=None, **opts):
1949 def push(ui, repo, dest=None, **opts):
1950 """push changes to the specified destination
1950 """push changes to the specified destination
1951
1951
1952 Push changes from the local repository to the given destination.
1952 Push changes from the local repository to the given destination.
1953
1953
1954 This is the symmetrical operation for pull. It helps to move
1954 This is the symmetrical operation for pull. It helps to move
1955 changes from the current repository to a different one. If the
1955 changes from the current repository to a different one. If the
1956 destination is local this is identical to a pull in that directory
1956 destination is local this is identical to a pull in that directory
1957 from the current one.
1957 from the current one.
1958
1958
1959 By default, push will refuse to run if it detects the result would
1959 By default, push will refuse to run if it detects the result would
1960 increase the number of remote heads. This generally indicates the
1960 increase the number of remote heads. This generally indicates the
1961 the client has forgotten to sync and merge before pushing.
1961 the client has forgotten to sync and merge before pushing.
1962
1962
1963 Valid URLs are of the form:
1963 Valid URLs are of the form:
1964
1964
1965 local/filesystem/path (or file://local/filesystem/path)
1965 local/filesystem/path (or file://local/filesystem/path)
1966 ssh://[user@]host[:port]/[path]
1966 ssh://[user@]host[:port]/[path]
1967 http://[user@]host[:port]/[path]
1967 http://[user@]host[:port]/[path]
1968 https://[user@]host[:port]/[path]
1968 https://[user@]host[:port]/[path]
1969
1969
1970 Look at the help text for the pull command for important details
1970 Look at the help text for the pull command for important details
1971 about ssh:// URLs.
1971 about ssh:// URLs.
1972
1972
1973 Pushing to http:// and https:// URLs is only possible, if this
1973 Pushing to http:// and https:// URLs is only possible, if this
1974 feature is explicitly enabled on the remote Mercurial server.
1974 feature is explicitly enabled on the remote Mercurial server.
1975 """
1975 """
1976 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1976 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1977 setremoteconfig(ui, opts)
1977 setremoteconfig(ui, opts)
1978
1978
1979 other = hg.repository(ui, dest)
1979 other = hg.repository(ui, dest)
1980 ui.status('pushing to %s\n' % (dest))
1980 ui.status('pushing to %s\n' % (dest))
1981 revs = None
1981 revs = None
1982 if opts['rev']:
1982 if opts['rev']:
1983 revs = [repo.lookup(rev) for rev in opts['rev']]
1983 revs = [repo.lookup(rev) for rev in opts['rev']]
1984 r = repo.push(other, opts['force'], revs=revs)
1984 r = repo.push(other, opts['force'], revs=revs)
1985 return r == 0
1985 return r == 0
1986
1986
1987 def rawcommit(ui, repo, *pats, **opts):
1987 def rawcommit(ui, repo, *pats, **opts):
1988 """raw commit interface (DEPRECATED)
1988 """raw commit interface (DEPRECATED)
1989
1989
1990 (DEPRECATED)
1990 (DEPRECATED)
1991 Lowlevel commit, for use in helper scripts.
1991 Lowlevel commit, for use in helper scripts.
1992
1992
1993 This command is not intended to be used by normal users, as it is
1993 This command is not intended to be used by normal users, as it is
1994 primarily useful for importing from other SCMs.
1994 primarily useful for importing from other SCMs.
1995
1995
1996 This command is now deprecated and will be removed in a future
1996 This command is now deprecated and will be removed in a future
1997 release, please use debugsetparents and commit instead.
1997 release, please use debugsetparents and commit instead.
1998 """
1998 """
1999
1999
2000 ui.warn(_("(the rawcommit command is deprecated)\n"))
2000 ui.warn(_("(the rawcommit command is deprecated)\n"))
2001
2001
2002 message = logmessage(opts)
2002 message = logmessage(opts)
2003
2003
2004 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2004 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2005 if opts['files']:
2005 if opts['files']:
2006 files += open(opts['files']).read().splitlines()
2006 files += open(opts['files']).read().splitlines()
2007
2007
2008 parents = [repo.lookup(p) for p in opts['parent']]
2008 parents = [repo.lookup(p) for p in opts['parent']]
2009
2009
2010 try:
2010 try:
2011 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2011 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2012 except ValueError, inst:
2012 except ValueError, inst:
2013 raise util.Abort(str(inst))
2013 raise util.Abort(str(inst))
2014
2014
2015 def recover(ui, repo):
2015 def recover(ui, repo):
2016 """roll back an interrupted transaction
2016 """roll back an interrupted transaction
2017
2017
2018 Recover from an interrupted commit or pull.
2018 Recover from an interrupted commit or pull.
2019
2019
2020 This command tries to fix the repository status after an interrupted
2020 This command tries to fix the repository status after an interrupted
2021 operation. It should only be necessary when Mercurial suggests it.
2021 operation. It should only be necessary when Mercurial suggests it.
2022 """
2022 """
2023 if repo.recover():
2023 if repo.recover():
2024 return hg.verify(repo)
2024 return hg.verify(repo)
2025 return 1
2025 return 1
2026
2026
2027 def remove(ui, repo, *pats, **opts):
2027 def remove(ui, repo, *pats, **opts):
2028 """remove the specified files on the next commit
2028 """remove the specified files on the next commit
2029
2029
2030 Schedule the indicated files for removal from the repository.
2030 Schedule the indicated files for removal from the repository.
2031
2031
2032 This only removes files from the current branch, not from the
2032 This only removes files from the current branch, not from the
2033 entire project history. If the files still exist in the working
2033 entire project history. If the files still exist in the working
2034 directory, they will be deleted from it. If invoked with --after,
2034 directory, they will be deleted from it. If invoked with --after,
2035 files that have been manually deleted are marked as removed.
2035 files that have been manually deleted are marked as removed.
2036
2036
2037 This command schedules the files to be removed at the next commit.
2037 This command schedules the files to be removed at the next commit.
2038 To undo a remove before that, see hg revert.
2038 To undo a remove before that, see hg revert.
2039
2039
2040 Modified files and added files are not removed by default. To
2040 Modified files and added files are not removed by default. To
2041 remove them, use the -f/--force option.
2041 remove them, use the -f/--force option.
2042 """
2042 """
2043 names = []
2043 names = []
2044 if not opts['after'] and not pats:
2044 if not opts['after'] and not pats:
2045 raise util.Abort(_('no files specified'))
2045 raise util.Abort(_('no files specified'))
2046 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2046 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2047 exact = dict.fromkeys(files)
2047 exact = dict.fromkeys(files)
2048 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2048 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2049 modified, added, removed, deleted, unknown = mardu
2049 modified, added, removed, deleted, unknown = mardu
2050 remove, forget = [], []
2050 remove, forget = [], []
2051 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2051 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2052 reason = None
2052 reason = None
2053 if abs not in deleted and opts['after']:
2053 if abs not in deleted and opts['after']:
2054 reason = _('is still present')
2054 reason = _('is still present')
2055 elif abs in modified and not opts['force']:
2055 elif abs in modified and not opts['force']:
2056 reason = _('is modified (use -f to force removal)')
2056 reason = _('is modified (use -f to force removal)')
2057 elif abs in added:
2057 elif abs in added:
2058 if opts['force']:
2058 if opts['force']:
2059 forget.append(abs)
2059 forget.append(abs)
2060 continue
2060 continue
2061 reason = _('has been marked for add (use -f to force removal)')
2061 reason = _('has been marked for add (use -f to force removal)')
2062 elif abs in unknown:
2062 elif abs in unknown:
2063 reason = _('is not managed')
2063 reason = _('is not managed')
2064 elif abs in removed:
2064 elif abs in removed:
2065 continue
2065 continue
2066 if reason:
2066 if reason:
2067 if exact:
2067 if exact:
2068 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2068 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2069 else:
2069 else:
2070 if ui.verbose or not exact:
2070 if ui.verbose or not exact:
2071 ui.status(_('removing %s\n') % rel)
2071 ui.status(_('removing %s\n') % rel)
2072 remove.append(abs)
2072 remove.append(abs)
2073 repo.forget(forget)
2073 repo.forget(forget)
2074 repo.remove(remove, unlink=not opts['after'])
2074 repo.remove(remove, unlink=not opts['after'])
2075
2075
2076 def rename(ui, repo, *pats, **opts):
2076 def rename(ui, repo, *pats, **opts):
2077 """rename files; equivalent of copy + remove
2077 """rename files; equivalent of copy + remove
2078
2078
2079 Mark dest as copies of sources; mark sources for deletion. If
2079 Mark dest as copies of sources; mark sources for deletion. If
2080 dest is a directory, copies are put in that directory. If dest is
2080 dest is a directory, copies are put in that directory. If dest is
2081 a file, there can only be one source.
2081 a file, there can only be one source.
2082
2082
2083 By default, this command copies the contents of files as they
2083 By default, this command copies the contents of files as they
2084 stand in the working directory. If invoked with --after, the
2084 stand in the working directory. If invoked with --after, the
2085 operation is recorded, but no copying is performed.
2085 operation is recorded, but no copying is performed.
2086
2086
2087 This command takes effect in the next commit. To undo a rename
2087 This command takes effect in the next commit. To undo a rename
2088 before that, see hg revert.
2088 before that, see hg revert.
2089 """
2089 """
2090 wlock = repo.wlock(0)
2090 wlock = repo.wlock(0)
2091 errs, copied = docopy(ui, repo, pats, opts, wlock)
2091 errs, copied = docopy(ui, repo, pats, opts, wlock)
2092 names = []
2092 names = []
2093 for abs, rel, exact in copied:
2093 for abs, rel, exact in copied:
2094 if ui.verbose or not exact:
2094 if ui.verbose or not exact:
2095 ui.status(_('removing %s\n') % rel)
2095 ui.status(_('removing %s\n') % rel)
2096 names.append(abs)
2096 names.append(abs)
2097 if not opts.get('dry_run'):
2097 if not opts.get('dry_run'):
2098 repo.remove(names, True, wlock)
2098 repo.remove(names, True, wlock)
2099 return errs
2099 return errs
2100
2100
2101 def revert(ui, repo, *pats, **opts):
2101 def revert(ui, repo, *pats, **opts):
2102 """revert files or dirs to their states as of some revision
2102 """revert files or dirs to their states as of some revision
2103
2103
2104 With no revision specified, revert the named files or directories
2104 With no revision specified, revert the named files or directories
2105 to the contents they had in the parent of the working directory.
2105 to the contents they had in the parent of the working directory.
2106 This restores the contents of the affected files to an unmodified
2106 This restores the contents of the affected files to an unmodified
2107 state and unschedules adds, removes, copies, and renames. If the
2107 state and unschedules adds, removes, copies, and renames. If the
2108 working directory has two parents, you must explicitly specify the
2108 working directory has two parents, you must explicitly specify the
2109 revision to revert to.
2109 revision to revert to.
2110
2110
2111 Modified files are saved with a .orig suffix before reverting.
2111 Modified files are saved with a .orig suffix before reverting.
2112 To disable these backups, use --no-backup.
2112 To disable these backups, use --no-backup.
2113
2113
2114 Using the -r option, revert the given files or directories to their
2114 Using the -r option, revert the given files or directories to their
2115 contents as of a specific revision. This can be helpful to "roll
2115 contents as of a specific revision. This can be helpful to "roll
2116 back" some or all of a change that should not have been committed.
2116 back" some or all of a change that should not have been committed.
2117
2117
2118 Revert modifies the working directory. It does not commit any
2118 Revert modifies the working directory. It does not commit any
2119 changes, or change the parent of the working directory. If you
2119 changes, or change the parent of the working directory. If you
2120 revert to a revision other than the parent of the working
2120 revert to a revision other than the parent of the working
2121 directory, the reverted files will thus appear modified
2121 directory, the reverted files will thus appear modified
2122 afterwards.
2122 afterwards.
2123
2123
2124 If a file has been deleted, it is recreated. If the executable
2124 If a file has been deleted, it is recreated. If the executable
2125 mode of a file was changed, it is reset.
2125 mode of a file was changed, it is reset.
2126
2126
2127 If names are given, all files matching the names are reverted.
2127 If names are given, all files matching the names are reverted.
2128
2128
2129 If no arguments are given, no files are reverted.
2129 If no arguments are given, no files are reverted.
2130 """
2130 """
2131
2131
2132 if opts["date"]:
2132 if opts["date"]:
2133 if opts["rev"]:
2133 if opts["rev"]:
2134 raise util.Abort(_("you can't specify a revision and a date"))
2134 raise util.Abort(_("you can't specify a revision and a date"))
2135 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2135 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2136
2136
2137 if not pats and not opts['all']:
2137 if not pats and not opts['all']:
2138 raise util.Abort(_('no files or directories specified; '
2138 raise util.Abort(_('no files or directories specified; '
2139 'use --all to revert the whole repo'))
2139 'use --all to revert the whole repo'))
2140
2140
2141 parent, p2 = repo.dirstate.parents()
2141 parent, p2 = repo.dirstate.parents()
2142 if not opts['rev'] and p2 != nullid:
2142 if not opts['rev'] and p2 != nullid:
2143 raise util.Abort(_('uncommitted merge - please provide a '
2143 raise util.Abort(_('uncommitted merge - please provide a '
2144 'specific revision'))
2144 'specific revision'))
2145 node = repo.changectx(opts['rev']).node()
2145 node = repo.changectx(opts['rev']).node()
2146 mf = repo.manifest.read(repo.changelog.read(node)[0])
2146 mf = repo.manifest.read(repo.changelog.read(node)[0])
2147 if node == parent:
2147 if node == parent:
2148 pmf = mf
2148 pmf = mf
2149 else:
2149 else:
2150 pmf = None
2150 pmf = None
2151
2151
2152 wlock = repo.wlock()
2152 wlock = repo.wlock()
2153
2153
2154 # need all matching names in dirstate and manifest of target rev,
2154 # need all matching names in dirstate and manifest of target rev,
2155 # so have to walk both. do not print errors if files exist in one
2155 # so have to walk both. do not print errors if files exist in one
2156 # but not other.
2156 # but not other.
2157
2157
2158 names = {}
2158 names = {}
2159 target_only = {}
2159 target_only = {}
2160
2160
2161 # walk dirstate.
2161 # walk dirstate.
2162
2162
2163 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2163 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2164 badmatch=mf.has_key):
2164 badmatch=mf.has_key):
2165 names[abs] = (rel, exact)
2165 names[abs] = (rel, exact)
2166 if src == 'b':
2166 if src == 'b':
2167 target_only[abs] = True
2167 target_only[abs] = True
2168
2168
2169 # walk target manifest.
2169 # walk target manifest.
2170
2170
2171 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2171 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2172 badmatch=names.has_key):
2172 badmatch=names.has_key):
2173 if abs in names: continue
2173 if abs in names: continue
2174 names[abs] = (rel, exact)
2174 names[abs] = (rel, exact)
2175 target_only[abs] = True
2175 target_only[abs] = True
2176
2176
2177 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2177 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2178 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2178 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2179
2179
2180 revert = ([], _('reverting %s\n'))
2180 revert = ([], _('reverting %s\n'))
2181 add = ([], _('adding %s\n'))
2181 add = ([], _('adding %s\n'))
2182 remove = ([], _('removing %s\n'))
2182 remove = ([], _('removing %s\n'))
2183 forget = ([], _('forgetting %s\n'))
2183 forget = ([], _('forgetting %s\n'))
2184 undelete = ([], _('undeleting %s\n'))
2184 undelete = ([], _('undeleting %s\n'))
2185 update = {}
2185 update = {}
2186
2186
2187 disptable = (
2187 disptable = (
2188 # dispatch table:
2188 # dispatch table:
2189 # file state
2189 # file state
2190 # action if in target manifest
2190 # action if in target manifest
2191 # action if not in target manifest
2191 # action if not in target manifest
2192 # make backup if in target manifest
2192 # make backup if in target manifest
2193 # make backup if not in target manifest
2193 # make backup if not in target manifest
2194 (modified, revert, remove, True, True),
2194 (modified, revert, remove, True, True),
2195 (added, revert, forget, True, False),
2195 (added, revert, forget, True, False),
2196 (removed, undelete, None, False, False),
2196 (removed, undelete, None, False, False),
2197 (deleted, revert, remove, False, False),
2197 (deleted, revert, remove, False, False),
2198 (unknown, add, None, True, False),
2198 (unknown, add, None, True, False),
2199 (target_only, add, None, False, False),
2199 (target_only, add, None, False, False),
2200 )
2200 )
2201
2201
2202 entries = names.items()
2202 entries = names.items()
2203 entries.sort()
2203 entries.sort()
2204
2204
2205 for abs, (rel, exact) in entries:
2205 for abs, (rel, exact) in entries:
2206 mfentry = mf.get(abs)
2206 mfentry = mf.get(abs)
2207 def handle(xlist, dobackup):
2207 def handle(xlist, dobackup):
2208 xlist[0].append(abs)
2208 xlist[0].append(abs)
2209 update[abs] = 1
2209 update[abs] = 1
2210 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2210 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2211 bakname = "%s.orig" % rel
2211 bakname = "%s.orig" % rel
2212 ui.note(_('saving current version of %s as %s\n') %
2212 ui.note(_('saving current version of %s as %s\n') %
2213 (rel, bakname))
2213 (rel, bakname))
2214 if not opts.get('dry_run'):
2214 if not opts.get('dry_run'):
2215 util.copyfile(rel, bakname)
2215 util.copyfile(rel, bakname)
2216 if ui.verbose or not exact:
2216 if ui.verbose or not exact:
2217 ui.status(xlist[1] % rel)
2217 ui.status(xlist[1] % rel)
2218 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2218 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2219 if abs not in table: continue
2219 if abs not in table: continue
2220 # file has changed in dirstate
2220 # file has changed in dirstate
2221 if mfentry:
2221 if mfentry:
2222 handle(hitlist, backuphit)
2222 handle(hitlist, backuphit)
2223 elif misslist is not None:
2223 elif misslist is not None:
2224 handle(misslist, backupmiss)
2224 handle(misslist, backupmiss)
2225 else:
2225 else:
2226 if exact: ui.warn(_('file not managed: %s\n') % rel)
2226 if exact: ui.warn(_('file not managed: %s\n') % rel)
2227 break
2227 break
2228 else:
2228 else:
2229 # file has not changed in dirstate
2229 # file has not changed in dirstate
2230 if node == parent:
2230 if node == parent:
2231 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2231 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2232 continue
2232 continue
2233 if pmf is None:
2233 if pmf is None:
2234 # only need parent manifest in this unlikely case,
2234 # only need parent manifest in this unlikely case,
2235 # so do not read by default
2235 # so do not read by default
2236 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2236 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2237 if abs in pmf:
2237 if abs in pmf:
2238 if mfentry:
2238 if mfentry:
2239 # if version of file is same in parent and target
2239 # if version of file is same in parent and target
2240 # manifests, do nothing
2240 # manifests, do nothing
2241 if pmf[abs] != mfentry:
2241 if pmf[abs] != mfentry:
2242 handle(revert, False)
2242 handle(revert, False)
2243 else:
2243 else:
2244 handle(remove, False)
2244 handle(remove, False)
2245
2245
2246 if not opts.get('dry_run'):
2246 if not opts.get('dry_run'):
2247 repo.dirstate.forget(forget[0])
2247 repo.dirstate.forget(forget[0])
2248 r = hg.revert(repo, node, update.has_key, wlock)
2248 r = hg.revert(repo, node, update.has_key, wlock)
2249 repo.dirstate.update(add[0], 'a')
2249 repo.dirstate.update(add[0], 'a')
2250 repo.dirstate.update(undelete[0], 'n')
2250 repo.dirstate.update(undelete[0], 'n')
2251 repo.dirstate.update(remove[0], 'r')
2251 repo.dirstate.update(remove[0], 'r')
2252 return r
2252 return r
2253
2253
2254 def rollback(ui, repo):
2254 def rollback(ui, repo):
2255 """roll back the last transaction in this repository
2255 """roll back the last transaction in this repository
2256
2256
2257 Roll back the last transaction in this repository, restoring the
2257 Roll back the last transaction in this repository, restoring the
2258 project to its state prior to the transaction.
2258 project to its state prior to the transaction.
2259
2259
2260 Transactions are used to encapsulate the effects of all commands
2260 Transactions are used to encapsulate the effects of all commands
2261 that create new changesets or propagate existing changesets into a
2261 that create new changesets or propagate existing changesets into a
2262 repository. For example, the following commands are transactional,
2262 repository. For example, the following commands are transactional,
2263 and their effects can be rolled back:
2263 and their effects can be rolled back:
2264
2264
2265 commit
2265 commit
2266 import
2266 import
2267 pull
2267 pull
2268 push (with this repository as destination)
2268 push (with this repository as destination)
2269 unbundle
2269 unbundle
2270
2270
2271 This command should be used with care. There is only one level of
2271 This command should be used with care. There is only one level of
2272 rollback, and there is no way to undo a rollback.
2272 rollback, and there is no way to undo a rollback.
2273
2273
2274 This command is not intended for use on public repositories. Once
2274 This command is not intended for use on public repositories. Once
2275 changes are visible for pull by other users, rolling a transaction
2275 changes are visible for pull by other users, rolling a transaction
2276 back locally is ineffective (someone else may already have pulled
2276 back locally is ineffective (someone else may already have pulled
2277 the changes). Furthermore, a race is possible with readers of the
2277 the changes). Furthermore, a race is possible with readers of the
2278 repository; for example an in-progress pull from the repository
2278 repository; for example an in-progress pull from the repository
2279 may fail if a rollback is performed.
2279 may fail if a rollback is performed.
2280 """
2280 """
2281 repo.rollback()
2281 repo.rollback()
2282
2282
2283 def root(ui, repo):
2283 def root(ui, repo):
2284 """print the root (top) of the current working dir
2284 """print the root (top) of the current working dir
2285
2285
2286 Print the root directory of the current repository.
2286 Print the root directory of the current repository.
2287 """
2287 """
2288 ui.write(repo.root + "\n")
2288 ui.write(repo.root + "\n")
2289
2289
2290 def serve(ui, repo, **opts):
2290 def serve(ui, repo, **opts):
2291 """export the repository via HTTP
2291 """export the repository via HTTP
2292
2292
2293 Start a local HTTP repository browser and pull server.
2293 Start a local HTTP repository browser and pull server.
2294
2294
2295 By default, the server logs accesses to stdout and errors to
2295 By default, the server logs accesses to stdout and errors to
2296 stderr. Use the "-A" and "-E" options to log to files.
2296 stderr. Use the "-A" and "-E" options to log to files.
2297 """
2297 """
2298
2298
2299 if opts["stdio"]:
2299 if opts["stdio"]:
2300 if repo is None:
2300 if repo is None:
2301 raise hg.RepoError(_("There is no Mercurial repository here"
2301 raise hg.RepoError(_("There is no Mercurial repository here"
2302 " (.hg not found)"))
2302 " (.hg not found)"))
2303 s = sshserver.sshserver(ui, repo)
2303 s = sshserver.sshserver(ui, repo)
2304 s.serve_forever()
2304 s.serve_forever()
2305
2305
2306 parentui = ui.parentui or ui
2306 parentui = ui.parentui or ui
2307 optlist = ("name templates style address port ipv6"
2307 optlist = ("name templates style address port ipv6"
2308 " accesslog errorlog webdir_conf")
2308 " accesslog errorlog webdir_conf")
2309 for o in optlist.split():
2309 for o in optlist.split():
2310 if opts[o]:
2310 if opts[o]:
2311 parentui.setconfig("web", o, str(opts[o]))
2311 parentui.setconfig("web", o, str(opts[o]))
2312
2312
2313 if repo is None and not ui.config("web", "webdir_conf"):
2313 if repo is None and not ui.config("web", "webdir_conf"):
2314 raise hg.RepoError(_("There is no Mercurial repository here"
2314 raise hg.RepoError(_("There is no Mercurial repository here"
2315 " (.hg not found)"))
2315 " (.hg not found)"))
2316
2316
2317 if opts['daemon'] and not opts['daemon_pipefds']:
2317 if opts['daemon'] and not opts['daemon_pipefds']:
2318 rfd, wfd = os.pipe()
2318 rfd, wfd = os.pipe()
2319 args = sys.argv[:]
2319 args = sys.argv[:]
2320 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2320 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2321 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2321 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2322 args[0], args)
2322 args[0], args)
2323 os.close(wfd)
2323 os.close(wfd)
2324 os.read(rfd, 1)
2324 os.read(rfd, 1)
2325 os._exit(0)
2325 os._exit(0)
2326
2326
2327 httpd = hgweb.server.create_server(parentui, repo)
2327 httpd = hgweb.server.create_server(parentui, repo)
2328
2328
2329 if ui.verbose:
2329 if ui.verbose:
2330 if httpd.port != 80:
2330 if httpd.port != 80:
2331 ui.status(_('listening at http://%s:%d/\n') %
2331 ui.status(_('listening at http://%s:%d/\n') %
2332 (httpd.addr, httpd.port))
2332 (httpd.addr, httpd.port))
2333 else:
2333 else:
2334 ui.status(_('listening at http://%s/\n') % httpd.addr)
2334 ui.status(_('listening at http://%s/\n') % httpd.addr)
2335
2335
2336 if opts['pid_file']:
2336 if opts['pid_file']:
2337 fp = open(opts['pid_file'], 'w')
2337 fp = open(opts['pid_file'], 'w')
2338 fp.write(str(os.getpid()) + '\n')
2338 fp.write(str(os.getpid()) + '\n')
2339 fp.close()
2339 fp.close()
2340
2340
2341 if opts['daemon_pipefds']:
2341 if opts['daemon_pipefds']:
2342 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2342 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2343 os.close(rfd)
2343 os.close(rfd)
2344 os.write(wfd, 'y')
2344 os.write(wfd, 'y')
2345 os.close(wfd)
2345 os.close(wfd)
2346 sys.stdout.flush()
2346 sys.stdout.flush()
2347 sys.stderr.flush()
2347 sys.stderr.flush()
2348 fd = os.open(util.nulldev, os.O_RDWR)
2348 fd = os.open(util.nulldev, os.O_RDWR)
2349 if fd != 0: os.dup2(fd, 0)
2349 if fd != 0: os.dup2(fd, 0)
2350 if fd != 1: os.dup2(fd, 1)
2350 if fd != 1: os.dup2(fd, 1)
2351 if fd != 2: os.dup2(fd, 2)
2351 if fd != 2: os.dup2(fd, 2)
2352 if fd not in (0, 1, 2): os.close(fd)
2352 if fd not in (0, 1, 2): os.close(fd)
2353
2353
2354 httpd.serve_forever()
2354 httpd.serve_forever()
2355
2355
2356 def status(ui, repo, *pats, **opts):
2356 def status(ui, repo, *pats, **opts):
2357 """show changed files in the working directory
2357 """show changed files in the working directory
2358
2358
2359 Show status of files in the repository. If names are given, only
2359 Show status of files in the repository. If names are given, only
2360 files that match are shown. Files that are clean or ignored, are
2360 files that match are shown. Files that are clean or ignored, are
2361 not listed unless -c (clean), -i (ignored) or -A is given.
2361 not listed unless -c (clean), -i (ignored) or -A is given.
2362
2362
2363 NOTE: status may appear to disagree with diff if permissions have
2363 NOTE: status may appear to disagree with diff if permissions have
2364 changed or a merge has occurred. The standard diff format does not
2364 changed or a merge has occurred. The standard diff format does not
2365 report permission changes and diff only reports changes relative
2365 report permission changes and diff only reports changes relative
2366 to one merge parent.
2366 to one merge parent.
2367
2367
2368 If one revision is given, it is used as the base revision.
2368 If one revision is given, it is used as the base revision.
2369 If two revisions are given, the difference between them is shown.
2369 If two revisions are given, the difference between them is shown.
2370
2370
2371 The codes used to show the status of files are:
2371 The codes used to show the status of files are:
2372 M = modified
2372 M = modified
2373 A = added
2373 A = added
2374 R = removed
2374 R = removed
2375 C = clean
2375 C = clean
2376 ! = deleted, but still tracked
2376 ! = deleted, but still tracked
2377 ? = not tracked
2377 ? = not tracked
2378 I = ignored (not shown by default)
2378 I = ignored (not shown by default)
2379 = the previous added file was copied from here
2379 = the previous added file was copied from here
2380 """
2380 """
2381
2381
2382 all = opts['all']
2382 all = opts['all']
2383 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2383 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2384
2384
2385 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2385 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2386 cwd = (pats and repo.getcwd()) or ''
2386 cwd = (pats and repo.getcwd()) or ''
2387 modified, added, removed, deleted, unknown, ignored, clean = [
2387 modified, added, removed, deleted, unknown, ignored, clean = [
2388 n for n in repo.status(node1=node1, node2=node2, files=files,
2388 n for n in repo.status(node1=node1, node2=node2, files=files,
2389 match=matchfn,
2389 match=matchfn,
2390 list_ignored=all or opts['ignored'],
2390 list_ignored=all or opts['ignored'],
2391 list_clean=all or opts['clean'])]
2391 list_clean=all or opts['clean'])]
2392
2392
2393 changetypes = (('modified', 'M', modified),
2393 changetypes = (('modified', 'M', modified),
2394 ('added', 'A', added),
2394 ('added', 'A', added),
2395 ('removed', 'R', removed),
2395 ('removed', 'R', removed),
2396 ('deleted', '!', deleted),
2396 ('deleted', '!', deleted),
2397 ('unknown', '?', unknown),
2397 ('unknown', '?', unknown),
2398 ('ignored', 'I', ignored))
2398 ('ignored', 'I', ignored))
2399
2399
2400 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2400 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2401
2401
2402 end = opts['print0'] and '\0' or '\n'
2402 end = opts['print0'] and '\0' or '\n'
2403
2403
2404 for opt, char, changes in ([ct for ct in explicit_changetypes
2404 for opt, char, changes in ([ct for ct in explicit_changetypes
2405 if all or opts[ct[0]]]
2405 if all or opts[ct[0]]]
2406 or changetypes):
2406 or changetypes):
2407 if opts['no_status']:
2407 if opts['no_status']:
2408 format = "%%s%s" % end
2408 format = "%%s%s" % end
2409 else:
2409 else:
2410 format = "%s %%s%s" % (char, end)
2410 format = "%s %%s%s" % (char, end)
2411
2411
2412 for f in changes:
2412 for f in changes:
2413 ui.write(format % util.pathto(cwd, f))
2413 ui.write(format % util.pathto(cwd, f))
2414 if ((all or opts.get('copies')) and not opts.get('no_status')):
2414 if ((all or opts.get('copies')) and not opts.get('no_status')):
2415 copied = repo.dirstate.copied(f)
2415 copied = repo.dirstate.copied(f)
2416 if copied:
2416 if copied:
2417 ui.write(' %s%s' % (util.pathto(cwd, copied), end))
2417 ui.write(' %s%s' % (util.pathto(cwd, copied), end))
2418
2418
2419 def tag(ui, repo, name, rev_=None, **opts):
2419 def tag(ui, repo, name, rev_=None, **opts):
2420 """add a tag for the current or given revision
2420 """add a tag for the current or given revision
2421
2421
2422 Name a particular revision using <name>.
2422 Name a particular revision using <name>.
2423
2423
2424 Tags are used to name particular revisions of the repository and are
2424 Tags are used to name particular revisions of the repository and are
2425 very useful to compare different revision, to go back to significant
2425 very useful to compare different revision, to go back to significant
2426 earlier versions or to mark branch points as releases, etc.
2426 earlier versions or to mark branch points as releases, etc.
2427
2427
2428 If no revision is given, the parent of the working directory is used,
2428 If no revision is given, the parent of the working directory is used,
2429 or tip if no revision is checked out.
2429 or tip if no revision is checked out.
2430
2430
2431 To facilitate version control, distribution, and merging of tags,
2431 To facilitate version control, distribution, and merging of tags,
2432 they are stored as a file named ".hgtags" which is managed
2432 they are stored as a file named ".hgtags" which is managed
2433 similarly to other project files and can be hand-edited if
2433 similarly to other project files and can be hand-edited if
2434 necessary. The file '.hg/localtags' is used for local tags (not
2434 necessary. The file '.hg/localtags' is used for local tags (not
2435 shared among repositories).
2435 shared among repositories).
2436 """
2436 """
2437 if name in ['tip', '.', 'null']:
2437 if name in ['tip', '.', 'null']:
2438 raise util.Abort(_("the name '%s' is reserved") % name)
2438 raise util.Abort(_("the name '%s' is reserved") % name)
2439 if rev_ is not None:
2439 if rev_ is not None:
2440 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2440 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2441 "please use 'hg tag [-r REV] NAME' instead\n"))
2441 "please use 'hg tag [-r REV] NAME' instead\n"))
2442 if opts['rev']:
2442 if opts['rev']:
2443 raise util.Abort(_("use only one form to specify the revision"))
2443 raise util.Abort(_("use only one form to specify the revision"))
2444 if opts['rev']:
2444 if opts['rev']:
2445 rev_ = opts['rev']
2445 rev_ = opts['rev']
2446 if not rev_ and repo.dirstate.parents()[1] != nullid:
2446 if not rev_ and repo.dirstate.parents()[1] != nullid:
2447 raise util.Abort(_('uncommitted merge - please provide a '
2447 raise util.Abort(_('uncommitted merge - please provide a '
2448 'specific revision'))
2448 'specific revision'))
2449 r = repo.changectx(rev_).node()
2449 r = repo.changectx(rev_).node()
2450
2450
2451 message = opts['message']
2451 message = opts['message']
2452 if not message:
2452 if not message:
2453 message = _('Added tag %s for changeset %s') % (name, short(r))
2453 message = _('Added tag %s for changeset %s') % (name, short(r))
2454
2454
2455 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2455 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2456
2456
2457 def tags(ui, repo):
2457 def tags(ui, repo):
2458 """list repository tags
2458 """list repository tags
2459
2459
2460 List the repository tags.
2460 List the repository tags.
2461
2461
2462 This lists both regular and local tags.
2462 This lists both regular and local tags.
2463 """
2463 """
2464
2464
2465 l = repo.tagslist()
2465 l = repo.tagslist()
2466 l.reverse()
2466 l.reverse()
2467 hexfunc = ui.debugflag and hex or short
2467 hexfunc = ui.debugflag and hex or short
2468 for t, n in l:
2468 for t, n in l:
2469 try:
2469 try:
2470 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2470 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2471 except KeyError:
2471 except KeyError:
2472 r = " ?:?"
2472 r = " ?:?"
2473 if ui.quiet:
2473 if ui.quiet:
2474 ui.write("%s\n" % t)
2474 ui.write("%s\n" % t)
2475 else:
2475 else:
2476 spaces = " " * (30 - util.locallen(t))
2476 spaces = " " * (30 - util.locallen(t))
2477 ui.write("%s%s %s\n" % (t, spaces, r))
2477 ui.write("%s%s %s\n" % (t, spaces, r))
2478
2478
2479 def tip(ui, repo, **opts):
2479 def tip(ui, repo, **opts):
2480 """show the tip revision
2480 """show the tip revision
2481
2481
2482 Show the tip revision.
2482 Show the tip revision.
2483 """
2483 """
2484 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2484 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2485
2485
2486 def unbundle(ui, repo, fname, **opts):
2486 def unbundle(ui, repo, fname, **opts):
2487 """apply a changegroup file
2487 """apply a changegroup file
2488
2488
2489 Apply a compressed changegroup file generated by the bundle
2489 Apply a compressed changegroup file generated by the bundle
2490 command.
2490 command.
2491 """
2491 """
2492 if os.path.exists(fname):
2492 if os.path.exists(fname):
2493 f = open(fname, "rb")
2493 f = open(fname, "rb")
2494 else:
2494 else:
2495 f = urllib.urlopen(fname)
2495 f = urllib.urlopen(fname)
2496 gen = changegroup.readbundle(f, fname)
2496 gen = changegroup.readbundle(f, fname)
2497 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2497 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2498 return postincoming(ui, repo, modheads, opts['update'])
2498 return postincoming(ui, repo, modheads, opts['update'])
2499
2499
2500 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2500 def update(ui, repo, node=None, clean=False, branch=None, date=None):
2501 """update working directory
2501 """update working directory
2502
2502
2503 Update the working directory to the specified revision.
2503 Update the working directory to the specified revision.
2504
2504
2505 If there are no outstanding changes in the working directory and
2505 If there are no outstanding changes in the working directory and
2506 there is a linear relationship between the current version and the
2506 there is a linear relationship between the current version and the
2507 requested version, the result is the requested version.
2507 requested version, the result is the requested version.
2508
2508
2509 To merge the working directory with another revision, use the
2509 To merge the working directory with another revision, use the
2510 merge command.
2510 merge command.
2511
2511
2512 By default, update will refuse to run if doing so would require
2512 By default, update will refuse to run if doing so would require
2513 discarding local changes.
2513 discarding local changes.
2514 """
2514 """
2515 if date:
2515 if date:
2516 if node:
2516 if node:
2517 raise util.Abort(_("you can't specify a revision and a date"))
2517 raise util.Abort(_("you can't specify a revision and a date"))
2518 node = cmdutil.finddate(ui, repo, date)
2518 node = cmdutil.finddate(ui, repo, date)
2519
2519
2520 node = _lookup(repo, node, branch)
2520 node = _lookup(repo, node, branch)
2521 if clean:
2521 if clean:
2522 return hg.clean(repo, node)
2522 return hg.clean(repo, node)
2523 else:
2523 else:
2524 return hg.update(repo, node)
2524 return hg.update(repo, node)
2525
2525
2526 def _lookup(repo, node, branch=None):
2526 def _lookup(repo, node, branch=None):
2527 if branch:
2527 if branch:
2528 repo.ui.warn(_("the --branch option is deprecated, "
2528 repo.ui.warn(_("the --branch option is deprecated, "
2529 "please use 'hg branch' instead\n"))
2529 "please use 'hg branch' instead\n"))
2530 br = repo.branchlookup(branch=branch)
2530 br = repo.branchlookup(branch=branch)
2531 found = []
2531 found = []
2532 for x in br:
2532 for x in br:
2533 if branch in br[x]:
2533 if branch in br[x]:
2534 found.append(x)
2534 found.append(x)
2535 if len(found) > 1:
2535 if len(found) > 1:
2536 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2536 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2537 for x in found:
2537 for x in found:
2538 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2538 cmdutil.show_changeset(ui, repo, {}).show(changenode=x)
2539 raise util.Abort("")
2539 raise util.Abort("")
2540 if len(found) == 1:
2540 if len(found) == 1:
2541 node = found[0]
2541 node = found[0]
2542 repo.ui.warn(_("Using head %s for branch %s\n")
2542 repo.ui.warn(_("Using head %s for branch %s\n")
2543 % (short(node), branch))
2543 % (short(node), branch))
2544 else:
2544 else:
2545 raise util.Abort(_("branch %s not found") % branch)
2545 raise util.Abort(_("branch %s not found") % branch)
2546 else:
2546 else:
2547 if node:
2547 if node:
2548 node = repo.lookup(node)
2548 node = repo.lookup(node)
2549 else:
2549 else:
2550 wc = repo.workingctx()
2550 wc = repo.workingctx()
2551 node = repo.branchtags()[wc.branch()]
2551 node = repo.branchtags()[wc.branch()]
2552 return node
2552 return node
2553
2553
2554 def verify(ui, repo):
2554 def verify(ui, repo):
2555 """verify the integrity of the repository
2555 """verify the integrity of the repository
2556
2556
2557 Verify the integrity of the current repository.
2557 Verify the integrity of the current repository.
2558
2558
2559 This will perform an extensive check of the repository's
2559 This will perform an extensive check of the repository's
2560 integrity, validating the hashes and checksums of each entry in
2560 integrity, validating the hashes and checksums of each entry in
2561 the changelog, manifest, and tracked files, as well as the
2561 the changelog, manifest, and tracked files, as well as the
2562 integrity of their crosslinks and indices.
2562 integrity of their crosslinks and indices.
2563 """
2563 """
2564 return hg.verify(repo)
2564 return hg.verify(repo)
2565
2565
2566 def version_(ui):
2566 def version_(ui):
2567 """output version and copyright information"""
2567 """output version and copyright information"""
2568 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2568 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2569 % version.get_version())
2569 % version.get_version())
2570 ui.status(_(
2570 ui.status(_(
2571 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2571 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2572 "This is free software; see the source for copying conditions. "
2572 "This is free software; see the source for copying conditions. "
2573 "There is NO\nwarranty; "
2573 "There is NO\nwarranty; "
2574 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2574 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2575 ))
2575 ))
2576
2576
2577 # Command options and aliases are listed here, alphabetically
2577 # Command options and aliases are listed here, alphabetically
2578
2578
2579 globalopts = [
2579 globalopts = [
2580 ('R', 'repository', '',
2580 ('R', 'repository', '',
2581 _('repository root directory or symbolic path name')),
2581 _('repository root directory or symbolic path name')),
2582 ('', 'cwd', '', _('change working directory')),
2582 ('', 'cwd', '', _('change working directory')),
2583 ('y', 'noninteractive', None,
2583 ('y', 'noninteractive', None,
2584 _('do not prompt, assume \'yes\' for any required answers')),
2584 _('do not prompt, assume \'yes\' for any required answers')),
2585 ('q', 'quiet', None, _('suppress output')),
2585 ('q', 'quiet', None, _('suppress output')),
2586 ('v', 'verbose', None, _('enable additional output')),
2586 ('v', 'verbose', None, _('enable additional output')),
2587 ('', 'config', [], _('set/override config option')),
2587 ('', 'config', [], _('set/override config option')),
2588 ('', 'debug', None, _('enable debugging output')),
2588 ('', 'debug', None, _('enable debugging output')),
2589 ('', 'debugger', None, _('start debugger')),
2589 ('', 'debugger', None, _('start debugger')),
2590 ('', 'encoding', util._encoding, _('set the charset encoding')),
2590 ('', 'encoding', util._encoding, _('set the charset encoding')),
2591 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2591 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2592 ('', 'lsprof', None, _('print improved command execution profile')),
2592 ('', 'lsprof', None, _('print improved command execution profile')),
2593 ('', 'traceback', None, _('print traceback on exception')),
2593 ('', 'traceback', None, _('print traceback on exception')),
2594 ('', 'time', None, _('time how long the command takes')),
2594 ('', 'time', None, _('time how long the command takes')),
2595 ('', 'profile', None, _('print command execution profile')),
2595 ('', 'profile', None, _('print command execution profile')),
2596 ('', 'version', None, _('output version information and exit')),
2596 ('', 'version', None, _('output version information and exit')),
2597 ('h', 'help', None, _('display help and exit')),
2597 ('h', 'help', None, _('display help and exit')),
2598 ]
2598 ]
2599
2599
2600 dryrunopts = [('n', 'dry-run', None,
2600 dryrunopts = [('n', 'dry-run', None,
2601 _('do not perform actions, just print output'))]
2601 _('do not perform actions, just print output'))]
2602
2602
2603 remoteopts = [
2603 remoteopts = [
2604 ('e', 'ssh', '', _('specify ssh command to use')),
2604 ('e', 'ssh', '', _('specify ssh command to use')),
2605 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2605 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2606 ]
2606 ]
2607
2607
2608 walkopts = [
2608 walkopts = [
2609 ('I', 'include', [], _('include names matching the given patterns')),
2609 ('I', 'include', [], _('include names matching the given patterns')),
2610 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2610 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2611 ]
2611 ]
2612
2612
2613 commitopts = [
2613 commitopts = [
2614 ('m', 'message', '', _('use <text> as commit message')),
2614 ('m', 'message', '', _('use <text> as commit message')),
2615 ('l', 'logfile', '', _('read commit message from <file>')),
2615 ('l', 'logfile', '', _('read commit message from <file>')),
2616 ]
2616 ]
2617
2617
2618 table = {
2618 table = {
2619 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2619 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2620 "addremove":
2620 "addremove":
2621 (addremove,
2621 (addremove,
2622 [('s', 'similarity', '',
2622 [('s', 'similarity', '',
2623 _('guess renamed files by similarity (0<=s<=100)')),
2623 _('guess renamed files by similarity (0<=s<=100)')),
2624 ] + walkopts + dryrunopts,
2624 ] + walkopts + dryrunopts,
2625 _('hg addremove [OPTION]... [FILE]...')),
2625 _('hg addremove [OPTION]... [FILE]...')),
2626 "^annotate":
2626 "^annotate":
2627 (annotate,
2627 (annotate,
2628 [('r', 'rev', '', _('annotate the specified revision')),
2628 [('r', 'rev', '', _('annotate the specified revision')),
2629 ('f', 'follow', None, _('follow file copies and renames')),
2629 ('f', 'follow', None, _('follow file copies and renames')),
2630 ('a', 'text', None, _('treat all files as text')),
2630 ('a', 'text', None, _('treat all files as text')),
2631 ('u', 'user', None, _('list the author')),
2631 ('u', 'user', None, _('list the author')),
2632 ('d', 'date', None, _('list the date')),
2632 ('d', 'date', None, _('list the date')),
2633 ('n', 'number', None, _('list the revision number (default)')),
2633 ('n', 'number', None, _('list the revision number (default)')),
2634 ('c', 'changeset', None, _('list the changeset')),
2634 ('c', 'changeset', None, _('list the changeset')),
2635 ] + walkopts,
2635 ] + walkopts,
2636 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2636 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2637 "archive":
2637 "archive":
2638 (archive,
2638 (archive,
2639 [('', 'no-decode', None, _('do not pass files through decoders')),
2639 [('', 'no-decode', None, _('do not pass files through decoders')),
2640 ('p', 'prefix', '', _('directory prefix for files in archive')),
2640 ('p', 'prefix', '', _('directory prefix for files in archive')),
2641 ('r', 'rev', '', _('revision to distribute')),
2641 ('r', 'rev', '', _('revision to distribute')),
2642 ('t', 'type', '', _('type of distribution to create')),
2642 ('t', 'type', '', _('type of distribution to create')),
2643 ] + walkopts,
2643 ] + walkopts,
2644 _('hg archive [OPTION]... DEST')),
2644 _('hg archive [OPTION]... DEST')),
2645 "backout":
2645 "backout":
2646 (backout,
2646 (backout,
2647 [('', 'merge', None,
2647 [('', 'merge', None,
2648 _('merge with old dirstate parent after backout')),
2648 _('merge with old dirstate parent after backout')),
2649 ('d', 'date', '', _('record datecode as commit date')),
2649 ('d', 'date', '', _('record datecode as commit date')),
2650 ('', 'parent', '', _('parent to choose when backing out merge')),
2650 ('', 'parent', '', _('parent to choose when backing out merge')),
2651 ('u', 'user', '', _('record user as committer')),
2651 ('u', 'user', '', _('record user as committer')),
2652 ] + walkopts + commitopts,
2652 ] + walkopts + commitopts,
2653 _('hg backout [OPTION]... REV')),
2653 _('hg backout [OPTION]... REV')),
2654 "branch": (branch, [], _('hg branch [NAME]')),
2654 "branch": (branch, [], _('hg branch [NAME]')),
2655 "branches": (branches, [], _('hg branches')),
2655 "branches": (branches, [], _('hg branches')),
2656 "bundle":
2656 "bundle":
2657 (bundle,
2657 (bundle,
2658 [('f', 'force', None,
2658 [('f', 'force', None,
2659 _('run even when remote repository is unrelated')),
2659 _('run even when remote repository is unrelated')),
2660 ('r', 'rev', [],
2660 ('r', 'rev', [],
2661 _('a changeset you would like to bundle')),
2661 _('a changeset you would like to bundle')),
2662 ('', 'base', [],
2662 ('', 'base', [],
2663 _('a base changeset to specify instead of a destination')),
2663 _('a base changeset to specify instead of a destination')),
2664 ] + remoteopts,
2664 ] + remoteopts,
2665 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2665 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2666 "cat":
2666 "cat":
2667 (cat,
2667 (cat,
2668 [('o', 'output', '', _('print output to file with formatted name')),
2668 [('o', 'output', '', _('print output to file with formatted name')),
2669 ('r', 'rev', '', _('print the given revision')),
2669 ('r', 'rev', '', _('print the given revision')),
2670 ] + walkopts,
2670 ] + walkopts,
2671 _('hg cat [OPTION]... FILE...')),
2671 _('hg cat [OPTION]... FILE...')),
2672 "^clone":
2672 "^clone":
2673 (clone,
2673 (clone,
2674 [('U', 'noupdate', None, _('do not update the new working directory')),
2674 [('U', 'noupdate', None, _('do not update the new working directory')),
2675 ('r', 'rev', [],
2675 ('r', 'rev', [],
2676 _('a changeset you would like to have after cloning')),
2676 _('a changeset you would like to have after cloning')),
2677 ('', 'pull', None, _('use pull protocol to copy metadata')),
2677 ('', 'pull', None, _('use pull protocol to copy metadata')),
2678 ('', 'uncompressed', None,
2678 ('', 'uncompressed', None,
2679 _('use uncompressed transfer (fast over LAN)')),
2679 _('use uncompressed transfer (fast over LAN)')),
2680 ] + remoteopts,
2680 ] + remoteopts,
2681 _('hg clone [OPTION]... SOURCE [DEST]')),
2681 _('hg clone [OPTION]... SOURCE [DEST]')),
2682 "^commit|ci":
2682 "^commit|ci":
2683 (commit,
2683 (commit,
2684 [('A', 'addremove', None,
2684 [('A', 'addremove', None,
2685 _('mark new/missing files as added/removed before committing')),
2685 _('mark new/missing files as added/removed before committing')),
2686 ('d', 'date', '', _('record datecode as commit date')),
2686 ('d', 'date', '', _('record datecode as commit date')),
2687 ('u', 'user', '', _('record user as commiter')),
2687 ('u', 'user', '', _('record user as commiter')),
2688 ] + walkopts + commitopts,
2688 ] + walkopts + commitopts,
2689 _('hg commit [OPTION]... [FILE]...')),
2689 _('hg commit [OPTION]... [FILE]...')),
2690 "copy|cp":
2690 "copy|cp":
2691 (copy,
2691 (copy,
2692 [('A', 'after', None, _('record a copy that has already occurred')),
2692 [('A', 'after', None, _('record a copy that has already occurred')),
2693 ('f', 'force', None,
2693 ('f', 'force', None,
2694 _('forcibly copy over an existing managed file')),
2694 _('forcibly copy over an existing managed file')),
2695 ] + walkopts + dryrunopts,
2695 ] + walkopts + dryrunopts,
2696 _('hg copy [OPTION]... [SOURCE]... DEST')),
2696 _('hg copy [OPTION]... [SOURCE]... DEST')),
2697 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2697 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2698 "debugcomplete":
2698 "debugcomplete":
2699 (debugcomplete,
2699 (debugcomplete,
2700 [('o', 'options', None, _('show the command options'))],
2700 [('o', 'options', None, _('show the command options'))],
2701 _('debugcomplete [-o] CMD')),
2701 _('debugcomplete [-o] CMD')),
2702 "debuginstall": (debuginstall, [], _('debuginstall')),
2702 "debuginstall": (debuginstall, [], _('debuginstall')),
2703 "debugrebuildstate":
2703 "debugrebuildstate":
2704 (debugrebuildstate,
2704 (debugrebuildstate,
2705 [('r', 'rev', '', _('revision to rebuild to'))],
2705 [('r', 'rev', '', _('revision to rebuild to'))],
2706 _('debugrebuildstate [-r REV] [REV]')),
2706 _('debugrebuildstate [-r REV] [REV]')),
2707 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2707 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2708 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2708 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2709 "debugstate": (debugstate, [], _('debugstate')),
2709 "debugstate": (debugstate, [], _('debugstate')),
2710 "debugdate":
2710 "debugdate":
2711 (debugdate,
2711 (debugdate,
2712 [('e', 'extended', None, _('try extended date formats'))],
2712 [('e', 'extended', None, _('try extended date formats'))],
2713 _('debugdate [-e] DATE [RANGE]')),
2713 _('debugdate [-e] DATE [RANGE]')),
2714 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2714 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2715 "debugindex": (debugindex, [], _('debugindex FILE')),
2715 "debugindex": (debugindex, [], _('debugindex FILE')),
2716 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2716 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2717 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2717 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2718 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2718 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2719 "^diff":
2719 "^diff":
2720 (diff,
2720 (diff,
2721 [('r', 'rev', [], _('revision')),
2721 [('r', 'rev', [], _('revision')),
2722 ('a', 'text', None, _('treat all files as text')),
2722 ('a', 'text', None, _('treat all files as text')),
2723 ('p', 'show-function', None,
2723 ('p', 'show-function', None,
2724 _('show which function each change is in')),
2724 _('show which function each change is in')),
2725 ('g', 'git', None, _('use git extended diff format')),
2725 ('g', 'git', None, _('use git extended diff format')),
2726 ('', 'nodates', None, _("don't include dates in diff headers")),
2726 ('', 'nodates', None, _("don't include dates in diff headers")),
2727 ('w', 'ignore-all-space', None,
2727 ('w', 'ignore-all-space', None,
2728 _('ignore white space when comparing lines')),
2728 _('ignore white space when comparing lines')),
2729 ('b', 'ignore-space-change', None,
2729 ('b', 'ignore-space-change', None,
2730 _('ignore changes in the amount of white space')),
2730 _('ignore changes in the amount of white space')),
2731 ('B', 'ignore-blank-lines', None,
2731 ('B', 'ignore-blank-lines', None,
2732 _('ignore changes whose lines are all blank')),
2732 _('ignore changes whose lines are all blank')),
2733 ] + walkopts,
2733 ] + walkopts,
2734 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2734 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2735 "^export":
2735 "^export":
2736 (export,
2736 (export,
2737 [('o', 'output', '', _('print output to file with formatted name')),
2737 [('o', 'output', '', _('print output to file with formatted name')),
2738 ('a', 'text', None, _('treat all files as text')),
2738 ('a', 'text', None, _('treat all files as text')),
2739 ('g', 'git', None, _('use git extended diff format')),
2739 ('g', 'git', None, _('use git extended diff format')),
2740 ('', 'nodates', None, _("don't include dates in diff headers")),
2740 ('', 'nodates', None, _("don't include dates in diff headers")),
2741 ('', 'switch-parent', None, _('diff against the second parent'))],
2741 ('', 'switch-parent', None, _('diff against the second parent'))],
2742 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2742 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2743 "grep":
2743 "grep":
2744 (grep,
2744 (grep,
2745 [('0', 'print0', None, _('end fields with NUL')),
2745 [('0', 'print0', None, _('end fields with NUL')),
2746 ('', 'all', None, _('print all revisions that match')),
2746 ('', 'all', None, _('print all revisions that match')),
2747 ('f', 'follow', None,
2747 ('f', 'follow', None,
2748 _('follow changeset history, or file history across copies and renames')),
2748 _('follow changeset history, or file history across copies and renames')),
2749 ('i', 'ignore-case', None, _('ignore case when matching')),
2749 ('i', 'ignore-case', None, _('ignore case when matching')),
2750 ('l', 'files-with-matches', None,
2750 ('l', 'files-with-matches', None,
2751 _('print only filenames and revs that match')),
2751 _('print only filenames and revs that match')),
2752 ('n', 'line-number', None, _('print matching line numbers')),
2752 ('n', 'line-number', None, _('print matching line numbers')),
2753 ('r', 'rev', [], _('search in given revision range')),
2753 ('r', 'rev', [], _('search in given revision range')),
2754 ('u', 'user', None, _('print user who committed change')),
2754 ('u', 'user', None, _('print user who committed change')),
2755 ] + walkopts,
2755 ] + walkopts,
2756 _('hg grep [OPTION]... PATTERN [FILE]...')),
2756 _('hg grep [OPTION]... PATTERN [FILE]...')),
2757 "heads":
2757 "heads":
2758 (heads,
2758 (heads,
2759 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2759 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2760 ('', 'style', '', _('display using template map file')),
2760 ('', 'style', '', _('display using template map file')),
2761 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2761 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2762 ('', 'template', '', _('display with template'))],
2762 ('', 'template', '', _('display with template'))],
2763 _('hg heads [-r REV]')),
2763 _('hg heads [-r REV]')),
2764 "help": (help_, [], _('hg help [COMMAND]')),
2764 "help": (help_, [], _('hg help [COMMAND]')),
2765 "identify|id": (identify, [], _('hg identify')),
2765 "identify|id": (identify, [], _('hg identify')),
2766 "import|patch":
2766 "import|patch":
2767 (import_,
2767 (import_,
2768 [('p', 'strip', 1,
2768 [('p', 'strip', 1,
2769 _('directory strip option for patch. This has the same\n'
2769 _('directory strip option for patch. This has the same\n'
2770 'meaning as the corresponding patch option')),
2770 'meaning as the corresponding patch option')),
2771 ('b', 'base', '', _('base path (DEPRECATED)')),
2771 ('b', 'base', '', _('base path (DEPRECATED)')),
2772 ('f', 'force', None,
2772 ('f', 'force', None,
2773 _('skip check for outstanding uncommitted changes'))] + commitopts,
2773 _('skip check for outstanding uncommitted changes'))] + commitopts,
2774 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2774 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2775 "incoming|in": (incoming,
2775 "incoming|in": (incoming,
2776 [('M', 'no-merges', None, _('do not show merges')),
2776 [('M', 'no-merges', None, _('do not show merges')),
2777 ('f', 'force', None,
2777 ('f', 'force', None,
2778 _('run even when remote repository is unrelated')),
2778 _('run even when remote repository is unrelated')),
2779 ('', 'style', '', _('display using template map file')),
2779 ('', 'style', '', _('display using template map file')),
2780 ('n', 'newest-first', None, _('show newest record first')),
2780 ('n', 'newest-first', None, _('show newest record first')),
2781 ('', 'bundle', '', _('file to store the bundles into')),
2781 ('', 'bundle', '', _('file to store the bundles into')),
2782 ('p', 'patch', None, _('show patch')),
2782 ('p', 'patch', None, _('show patch')),
2783 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2783 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2784 ('', 'template', '', _('display with template')),
2784 ('', 'template', '', _('display with template')),
2785 ] + remoteopts,
2785 ] + remoteopts,
2786 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2786 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2787 ' [--bundle FILENAME] [SOURCE]')),
2787 ' [--bundle FILENAME] [SOURCE]')),
2788 "^init":
2788 "^init":
2789 (init,
2789 (init,
2790 remoteopts,
2790 remoteopts,
2791 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2791 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2792 "locate":
2792 "locate":
2793 (locate,
2793 (locate,
2794 [('r', 'rev', '', _('search the repository as it stood at rev')),
2794 [('r', 'rev', '', _('search the repository as it stood at rev')),
2795 ('0', 'print0', None,
2795 ('0', 'print0', None,
2796 _('end filenames with NUL, for use with xargs')),
2796 _('end filenames with NUL, for use with xargs')),
2797 ('f', 'fullpath', None,
2797 ('f', 'fullpath', None,
2798 _('print complete paths from the filesystem root')),
2798 _('print complete paths from the filesystem root')),
2799 ] + walkopts,
2799 ] + walkopts,
2800 _('hg locate [OPTION]... [PATTERN]...')),
2800 _('hg locate [OPTION]... [PATTERN]...')),
2801 "^log|history":
2801 "^log|history":
2802 (log,
2802 (log,
2803 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2803 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2804 ('f', 'follow', None,
2804 ('f', 'follow', None,
2805 _('follow changeset history, or file history across copies and renames')),
2805 _('follow changeset history, or file history across copies and renames')),
2806 ('', 'follow-first', None,
2806 ('', 'follow-first', None,
2807 _('only follow the first parent of merge changesets')),
2807 _('only follow the first parent of merge changesets')),
2808 ('d', 'date', '', _('show revs matching date spec')),
2808 ('d', 'date', '', _('show revs matching date spec')),
2809 ('C', 'copies', None, _('show copied files')),
2809 ('C', 'copies', None, _('show copied files')),
2810 ('k', 'keyword', [], _('search for a keyword')),
2810 ('k', 'keyword', [], _('search for a keyword')),
2811 ('l', 'limit', '', _('limit number of changes displayed')),
2811 ('l', 'limit', '', _('limit number of changes displayed')),
2812 ('r', 'rev', [], _('show the specified revision or range')),
2812 ('r', 'rev', [], _('show the specified revision or range')),
2813 ('', 'removed', None, _('include revs where files were removed')),
2813 ('', 'removed', None, _('include revs where files were removed')),
2814 ('M', 'no-merges', None, _('do not show merges')),
2814 ('M', 'no-merges', None, _('do not show merges')),
2815 ('', 'style', '', _('display using template map file')),
2815 ('', 'style', '', _('display using template map file')),
2816 ('m', 'only-merges', None, _('show only merges')),
2816 ('m', 'only-merges', None, _('show only merges')),
2817 ('p', 'patch', None, _('show patch')),
2817 ('p', 'patch', None, _('show patch')),
2818 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2818 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2819 ('', 'template', '', _('display with template')),
2819 ('', 'template', '', _('display with template')),
2820 ] + walkopts,
2820 ] + walkopts,
2821 _('hg log [OPTION]... [FILE]')),
2821 _('hg log [OPTION]... [FILE]')),
2822 "manifest": (manifest, [], _('hg manifest [REV]')),
2822 "manifest": (manifest, [], _('hg manifest [REV]')),
2823 "^merge":
2823 "^merge":
2824 (merge,
2824 (merge,
2825 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2825 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
2826 ('f', 'force', None, _('force a merge with outstanding changes'))],
2826 ('f', 'force', None, _('force a merge with outstanding changes'))],
2827 _('hg merge [-f] [REV]')),
2827 _('hg merge [-f] [REV]')),
2828 "outgoing|out": (outgoing,
2828 "outgoing|out": (outgoing,
2829 [('M', 'no-merges', None, _('do not show merges')),
2829 [('M', 'no-merges', None, _('do not show merges')),
2830 ('f', 'force', None,
2830 ('f', 'force', None,
2831 _('run even when remote repository is unrelated')),
2831 _('run even when remote repository is unrelated')),
2832 ('p', 'patch', None, _('show patch')),
2832 ('p', 'patch', None, _('show patch')),
2833 ('', 'style', '', _('display using template map file')),
2833 ('', 'style', '', _('display using template map file')),
2834 ('r', 'rev', [], _('a specific revision you would like to push')),
2834 ('r', 'rev', [], _('a specific revision you would like to push')),
2835 ('n', 'newest-first', None, _('show newest record first')),
2835 ('n', 'newest-first', None, _('show newest record first')),
2836 ('', 'template', '', _('display with template')),
2836 ('', 'template', '', _('display with template')),
2837 ] + remoteopts,
2837 ] + remoteopts,
2838 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2838 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2839 "^parents":
2839 "^parents":
2840 (parents,
2840 (parents,
2841 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2841 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2842 ('r', 'rev', '', _('show parents from the specified rev')),
2842 ('r', 'rev', '', _('show parents from the specified rev')),
2843 ('', 'style', '', _('display using template map file')),
2843 ('', 'style', '', _('display using template map file')),
2844 ('', 'template', '', _('display with template'))],
2844 ('', 'template', '', _('display with template'))],
2845 _('hg parents [-r REV] [FILE]')),
2845 _('hg parents [-r REV] [FILE]')),
2846 "paths": (paths, [], _('hg paths [NAME]')),
2846 "paths": (paths, [], _('hg paths [NAME]')),
2847 "^pull":
2847 "^pull":
2848 (pull,
2848 (pull,
2849 [('u', 'update', None,
2849 [('u', 'update', None,
2850 _('update to new tip if changesets were pulled')),
2850 _('update to new tip if changesets were pulled')),
2851 ('f', 'force', None,
2851 ('f', 'force', None,
2852 _('run even when remote repository is unrelated')),
2852 _('run even when remote repository is unrelated')),
2853 ('r', 'rev', [],
2853 ('r', 'rev', [],
2854 _('a specific revision up to which you would like to pull')),
2854 _('a specific revision up to which you would like to pull')),
2855 ] + remoteopts,
2855 ] + remoteopts,
2856 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2856 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2857 "^push":
2857 "^push":
2858 (push,
2858 (push,
2859 [('f', 'force', None, _('force push')),
2859 [('f', 'force', None, _('force push')),
2860 ('r', 'rev', [], _('a specific revision you would like to push')),
2860 ('r', 'rev', [], _('a specific revision you would like to push')),
2861 ] + remoteopts,
2861 ] + remoteopts,
2862 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2862 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2863 "debugrawcommit|rawcommit":
2863 "debugrawcommit|rawcommit":
2864 (rawcommit,
2864 (rawcommit,
2865 [('p', 'parent', [], _('parent')),
2865 [('p', 'parent', [], _('parent')),
2866 ('d', 'date', '', _('date code')),
2866 ('d', 'date', '', _('date code')),
2867 ('u', 'user', '', _('user')),
2867 ('u', 'user', '', _('user')),
2868 ('F', 'files', '', _('file list'))
2868 ('F', 'files', '', _('file list'))
2869 ] + commitopts,
2869 ] + commitopts,
2870 _('hg debugrawcommit [OPTION]... [FILE]...')),
2870 _('hg debugrawcommit [OPTION]... [FILE]...')),
2871 "recover": (recover, [], _('hg recover')),
2871 "recover": (recover, [], _('hg recover')),
2872 "^remove|rm":
2872 "^remove|rm":
2873 (remove,
2873 (remove,
2874 [('A', 'after', None, _('record remove that has already occurred')),
2874 [('A', 'after', None, _('record remove that has already occurred')),
2875 ('f', 'force', None, _('remove file even if modified')),
2875 ('f', 'force', None, _('remove file even if modified')),
2876 ] + walkopts,
2876 ] + walkopts,
2877 _('hg remove [OPTION]... FILE...')),
2877 _('hg remove [OPTION]... FILE...')),
2878 "rename|mv":
2878 "rename|mv":
2879 (rename,
2879 (rename,
2880 [('A', 'after', None, _('record a rename that has already occurred')),
2880 [('A', 'after', None, _('record a rename that has already occurred')),
2881 ('f', 'force', None,
2881 ('f', 'force', None,
2882 _('forcibly copy over an existing managed file')),
2882 _('forcibly copy over an existing managed file')),
2883 ] + walkopts + dryrunopts,
2883 ] + walkopts + dryrunopts,
2884 _('hg rename [OPTION]... SOURCE... DEST')),
2884 _('hg rename [OPTION]... SOURCE... DEST')),
2885 "^revert":
2885 "^revert":
2886 (revert,
2886 (revert,
2887 [('a', 'all', None, _('revert all changes when no arguments given')),
2887 [('a', 'all', None, _('revert all changes when no arguments given')),
2888 ('d', 'date', '', _('tipmost revision matching date')),
2888 ('d', 'date', '', _('tipmost revision matching date')),
2889 ('r', 'rev', '', _('revision to revert to')),
2889 ('r', 'rev', '', _('revision to revert to')),
2890 ('', 'no-backup', None, _('do not save backup copies of files')),
2890 ('', 'no-backup', None, _('do not save backup copies of files')),
2891 ] + walkopts + dryrunopts,
2891 ] + walkopts + dryrunopts,
2892 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2892 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2893 "rollback": (rollback, [], _('hg rollback')),
2893 "rollback": (rollback, [], _('hg rollback')),
2894 "root": (root, [], _('hg root')),
2894 "root": (root, [], _('hg root')),
2895 "showconfig|debugconfig":
2895 "showconfig|debugconfig":
2896 (showconfig,
2896 (showconfig,
2897 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2897 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2898 _('showconfig [-u] [NAME]...')),
2898 _('showconfig [-u] [NAME]...')),
2899 "^serve":
2899 "^serve":
2900 (serve,
2900 (serve,
2901 [('A', 'accesslog', '', _('name of access log file to write to')),
2901 [('A', 'accesslog', '', _('name of access log file to write to')),
2902 ('d', 'daemon', None, _('run server in background')),
2902 ('d', 'daemon', None, _('run server in background')),
2903 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2903 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2904 ('E', 'errorlog', '', _('name of error log file to write to')),
2904 ('E', 'errorlog', '', _('name of error log file to write to')),
2905 ('p', 'port', 0, _('port to use (default: 8000)')),
2905 ('p', 'port', 0, _('port to use (default: 8000)')),
2906 ('a', 'address', '', _('address to use')),
2906 ('a', 'address', '', _('address to use')),
2907 ('n', 'name', '',
2907 ('n', 'name', '',
2908 _('name to show in web pages (default: working dir)')),
2908 _('name to show in web pages (default: working dir)')),
2909 ('', 'webdir-conf', '', _('name of the webdir config file'
2909 ('', 'webdir-conf', '', _('name of the webdir config file'
2910 ' (serve more than one repo)')),
2910 ' (serve more than one repo)')),
2911 ('', 'pid-file', '', _('name of file to write process ID to')),
2911 ('', 'pid-file', '', _('name of file to write process ID to')),
2912 ('', 'stdio', None, _('for remote clients')),
2912 ('', 'stdio', None, _('for remote clients')),
2913 ('t', 'templates', '', _('web templates to use')),
2913 ('t', 'templates', '', _('web templates to use')),
2914 ('', 'style', '', _('template style to use')),
2914 ('', 'style', '', _('template style to use')),
2915 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2915 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2916 _('hg serve [OPTION]...')),
2916 _('hg serve [OPTION]...')),
2917 "^status|st":
2917 "^status|st":
2918 (status,
2918 (status,
2919 [('A', 'all', None, _('show status of all files')),
2919 [('A', 'all', None, _('show status of all files')),
2920 ('m', 'modified', None, _('show only modified files')),
2920 ('m', 'modified', None, _('show only modified files')),
2921 ('a', 'added', None, _('show only added files')),
2921 ('a', 'added', None, _('show only added files')),
2922 ('r', 'removed', None, _('show only removed files')),
2922 ('r', 'removed', None, _('show only removed files')),
2923 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2923 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2924 ('c', 'clean', None, _('show only files without changes')),
2924 ('c', 'clean', None, _('show only files without changes')),
2925 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2925 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2926 ('i', 'ignored', None, _('show only ignored files')),
2926 ('i', 'ignored', None, _('show only ignored files')),
2927 ('n', 'no-status', None, _('hide status prefix')),
2927 ('n', 'no-status', None, _('hide status prefix')),
2928 ('C', 'copies', None, _('show source of copied files')),
2928 ('C', 'copies', None, _('show source of copied files')),
2929 ('0', 'print0', None,
2929 ('0', 'print0', None,
2930 _('end filenames with NUL, for use with xargs')),
2930 _('end filenames with NUL, for use with xargs')),
2931 ('', 'rev', [], _('show difference from revision')),
2931 ('', 'rev', [], _('show difference from revision')),
2932 ] + walkopts,
2932 ] + walkopts,
2933 _('hg status [OPTION]... [FILE]...')),
2933 _('hg status [OPTION]... [FILE]...')),
2934 "tag":
2934 "tag":
2935 (tag,
2935 (tag,
2936 [('l', 'local', None, _('make the tag local')),
2936 [('l', 'local', None, _('make the tag local')),
2937 ('m', 'message', '', _('message for tag commit log entry')),
2937 ('m', 'message', '', _('message for tag commit log entry')),
2938 ('d', 'date', '', _('record datecode as commit date')),
2938 ('d', 'date', '', _('record datecode as commit date')),
2939 ('u', 'user', '', _('record user as commiter')),
2939 ('u', 'user', '', _('record user as commiter')),
2940 ('r', 'rev', '', _('revision to tag'))],
2940 ('r', 'rev', '', _('revision to tag'))],
2941 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2941 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2942 "tags": (tags, [], _('hg tags')),
2942 "tags": (tags, [], _('hg tags')),
2943 "tip":
2943 "tip":
2944 (tip,
2944 (tip,
2945 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2945 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2946 ('', 'style', '', _('display using template map file')),
2946 ('', 'style', '', _('display using template map file')),
2947 ('p', 'patch', None, _('show patch')),
2947 ('p', 'patch', None, _('show patch')),
2948 ('', 'template', '', _('display with template'))],
2948 ('', 'template', '', _('display with template'))],
2949 _('hg tip [-p]')),
2949 _('hg tip [-p]')),
2950 "unbundle":
2950 "unbundle":
2951 (unbundle,
2951 (unbundle,
2952 [('u', 'update', None,
2952 [('u', 'update', None,
2953 _('update to new tip if changesets were unbundled'))],
2953 _('update to new tip if changesets were unbundled'))],
2954 _('hg unbundle [-u] FILE')),
2954 _('hg unbundle [-u] FILE')),
2955 "^update|up|checkout|co":
2955 "^update|up|checkout|co":
2956 (update,
2956 (update,
2957 [('b', 'branch', '',
2957 [('b', 'branch', '',
2958 _('checkout the head of a specific branch (DEPRECATED)')),
2958 _('checkout the head of a specific branch (DEPRECATED)')),
2959 ('C', 'clean', None, _('overwrite locally modified files')),
2959 ('C', 'clean', None, _('overwrite locally modified files')),
2960 ('d', 'date', '', _('tipmost revision matching date'))],
2960 ('d', 'date', '', _('tipmost revision matching date'))],
2961 _('hg update [-C] [-d DATE] [REV]')),
2961 _('hg update [-C] [-d DATE] [REV]')),
2962 "verify": (verify, [], _('hg verify')),
2962 "verify": (verify, [], _('hg verify')),
2963 "version": (version_, [], _('hg version')),
2963 "version": (version_, [], _('hg version')),
2964 }
2964 }
2965
2965
2966 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2966 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2967 " debugindex debugindexdot debugdate debuginstall")
2967 " debugindex debugindexdot debugdate debuginstall")
2968 optionalrepo = ("paths serve showconfig")
2968 optionalrepo = ("paths serve showconfig")
2969
2969
2970 def findpossible(ui, cmd):
2970 def findpossible(ui, cmd):
2971 """
2971 """
2972 Return cmd -> (aliases, command table entry)
2972 Return cmd -> (aliases, command table entry)
2973 for each matching command.
2973 for each matching command.
2974 Return debug commands (or their aliases) only if no normal command matches.
2974 Return debug commands (or their aliases) only if no normal command matches.
2975 """
2975 """
2976 choice = {}
2976 choice = {}
2977 debugchoice = {}
2977 debugchoice = {}
2978 for e in table.keys():
2978 for e in table.keys():
2979 aliases = e.lstrip("^").split("|")
2979 aliases = e.lstrip("^").split("|")
2980 found = None
2980 found = None
2981 if cmd in aliases:
2981 if cmd in aliases:
2982 found = cmd
2982 found = cmd
2983 elif not ui.config("ui", "strict"):
2983 elif not ui.config("ui", "strict"):
2984 for a in aliases:
2984 for a in aliases:
2985 if a.startswith(cmd):
2985 if a.startswith(cmd):
2986 found = a
2986 found = a
2987 break
2987 break
2988 if found is not None:
2988 if found is not None:
2989 if aliases[0].startswith("debug") or found.startswith("debug"):
2989 if aliases[0].startswith("debug") or found.startswith("debug"):
2990 debugchoice[found] = (aliases, table[e])
2990 debugchoice[found] = (aliases, table[e])
2991 else:
2991 else:
2992 choice[found] = (aliases, table[e])
2992 choice[found] = (aliases, table[e])
2993
2993
2994 if not choice and debugchoice:
2994 if not choice and debugchoice:
2995 choice = debugchoice
2995 choice = debugchoice
2996
2996
2997 return choice
2997 return choice
2998
2998
2999 def findcmd(ui, cmd):
2999 def findcmd(ui, cmd):
3000 """Return (aliases, command table entry) for command string."""
3000 """Return (aliases, command table entry) for command string."""
3001 choice = findpossible(ui, cmd)
3001 choice = findpossible(ui, cmd)
3002
3002
3003 if choice.has_key(cmd):
3003 if choice.has_key(cmd):
3004 return choice[cmd]
3004 return choice[cmd]
3005
3005
3006 if len(choice) > 1:
3006 if len(choice) > 1:
3007 clist = choice.keys()
3007 clist = choice.keys()
3008 clist.sort()
3008 clist.sort()
3009 raise AmbiguousCommand(cmd, clist)
3009 raise AmbiguousCommand(cmd, clist)
3010
3010
3011 if choice:
3011 if choice:
3012 return choice.values()[0]
3012 return choice.values()[0]
3013
3013
3014 raise UnknownCommand(cmd)
3014 raise UnknownCommand(cmd)
3015
3015
3016 def catchterm(*args):
3016 def catchterm(*args):
3017 raise util.SignalInterrupt
3017 raise util.SignalInterrupt
3018
3018
3019 def run():
3019 def run():
3020 sys.exit(dispatch(sys.argv[1:]))
3020 sys.exit(dispatch(sys.argv[1:]))
3021
3021
3022 class ParseError(Exception):
3022 class ParseError(Exception):
3023 """Exception raised on errors in parsing the command line."""
3023 """Exception raised on errors in parsing the command line."""
3024
3024
3025 def parse(ui, args):
3025 def parse(ui, args):
3026 options = {}
3026 options = {}
3027 cmdoptions = {}
3027 cmdoptions = {}
3028
3028
3029 try:
3029 try:
3030 args = fancyopts.fancyopts(args, globalopts, options)
3030 args = fancyopts.fancyopts(args, globalopts, options)
3031 except fancyopts.getopt.GetoptError, inst:
3031 except fancyopts.getopt.GetoptError, inst:
3032 raise ParseError(None, inst)
3032 raise ParseError(None, inst)
3033
3033
3034 if args:
3034 if args:
3035 cmd, args = args[0], args[1:]
3035 cmd, args = args[0], args[1:]
3036 aliases, i = findcmd(ui, cmd)
3036 aliases, i = findcmd(ui, cmd)
3037 cmd = aliases[0]
3037 cmd = aliases[0]
3038 defaults = ui.config("defaults", cmd)
3038 defaults = ui.config("defaults", cmd)
3039 if defaults:
3039 if defaults:
3040 args = shlex.split(defaults) + args
3040 args = shlex.split(defaults) + args
3041 c = list(i[1])
3041 c = list(i[1])
3042 else:
3042 else:
3043 cmd = None
3043 cmd = None
3044 c = []
3044 c = []
3045
3045
3046 # combine global options into local
3046 # combine global options into local
3047 for o in globalopts:
3047 for o in globalopts:
3048 c.append((o[0], o[1], options[o[1]], o[3]))
3048 c.append((o[0], o[1], options[o[1]], o[3]))
3049
3049
3050 try:
3050 try:
3051 args = fancyopts.fancyopts(args, c, cmdoptions)
3051 args = fancyopts.fancyopts(args, c, cmdoptions)
3052 except fancyopts.getopt.GetoptError, inst:
3052 except fancyopts.getopt.GetoptError, inst:
3053 raise ParseError(cmd, inst)
3053 raise ParseError(cmd, inst)
3054
3054
3055 # separate global options back out
3055 # separate global options back out
3056 for o in globalopts:
3056 for o in globalopts:
3057 n = o[1]
3057 n = o[1]
3058 options[n] = cmdoptions[n]
3058 options[n] = cmdoptions[n]
3059 del cmdoptions[n]
3059 del cmdoptions[n]
3060
3060
3061 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3061 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3062
3062
3063 external = {}
3063 external = {}
3064
3064
3065 def findext(name):
3065 def findext(name):
3066 '''return module with given extension name'''
3066 '''return module with given extension name'''
3067 try:
3067 try:
3068 return sys.modules[external[name]]
3068 return sys.modules[external[name]]
3069 except KeyError:
3069 except KeyError:
3070 for k, v in external.iteritems():
3070 for k, v in external.iteritems():
3071 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3071 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3072 return sys.modules[v]
3072 return sys.modules[v]
3073 raise KeyError(name)
3073 raise KeyError(name)
3074
3074
3075 def load_extensions(ui):
3075 def load_extensions(ui):
3076 added = []
3076 added = []
3077 for ext_name, load_from_name in ui.extensions():
3077 for ext_name, load_from_name in ui.extensions():
3078 if ext_name in external:
3078 if ext_name in external:
3079 continue
3079 continue
3080 try:
3080 try:
3081 if load_from_name:
3081 if load_from_name:
3082 # the module will be loaded in sys.modules
3082 # the module will be loaded in sys.modules
3083 # choose an unique name so that it doesn't
3083 # choose an unique name so that it doesn't
3084 # conflicts with other modules
3084 # conflicts with other modules
3085 module_name = "hgext_%s" % ext_name.replace('.', '_')
3085 module_name = "hgext_%s" % ext_name.replace('.', '_')
3086 mod = imp.load_source(module_name, load_from_name)
3086 mod = imp.load_source(module_name, load_from_name)
3087 else:
3087 else:
3088 def importh(name):
3088 def importh(name):
3089 mod = __import__(name)
3089 mod = __import__(name)
3090 components = name.split('.')
3090 components = name.split('.')
3091 for comp in components[1:]:
3091 for comp in components[1:]:
3092 mod = getattr(mod, comp)
3092 mod = getattr(mod, comp)
3093 return mod
3093 return mod
3094 try:
3094 try:
3095 mod = importh("hgext.%s" % ext_name)
3095 mod = importh("hgext.%s" % ext_name)
3096 except ImportError:
3096 except ImportError:
3097 mod = importh(ext_name)
3097 mod = importh(ext_name)
3098 external[ext_name] = mod.__name__
3098 external[ext_name] = mod.__name__
3099 added.append((mod, ext_name))
3099 added.append((mod, ext_name))
3100 except (util.SignalInterrupt, KeyboardInterrupt):
3100 except (util.SignalInterrupt, KeyboardInterrupt):
3101 raise
3101 raise
3102 except Exception, inst:
3102 except Exception, inst:
3103 ui.warn(_("*** failed to import extension %s: %s\n") %
3103 ui.warn(_("*** failed to import extension %s: %s\n") %
3104 (ext_name, inst))
3104 (ext_name, inst))
3105 if ui.print_exc():
3105 if ui.print_exc():
3106 return 1
3106 return 1
3107
3107
3108 for mod, name in added:
3108 for mod, name in added:
3109 uisetup = getattr(mod, 'uisetup', None)
3109 uisetup = getattr(mod, 'uisetup', None)
3110 if uisetup:
3110 if uisetup:
3111 uisetup(ui)
3111 uisetup(ui)
3112 reposetup = getattr(mod, 'reposetup', None)
3112 reposetup = getattr(mod, 'reposetup', None)
3113 if reposetup:
3113 if reposetup:
3114 hg.repo_setup_hooks.append(reposetup)
3114 hg.repo_setup_hooks.append(reposetup)
3115 cmdtable = getattr(mod, 'cmdtable', {})
3115 cmdtable = getattr(mod, 'cmdtable', {})
3116 for t in cmdtable:
3116 for t in cmdtable:
3117 if t in table:
3117 if t in table:
3118 ui.warn(_("module %s overrides %s\n") % (name, t))
3118 ui.warn(_("module %s overrides %s\n") % (name, t))
3119 table.update(cmdtable)
3119 table.update(cmdtable)
3120
3120
3121 def parseconfig(config):
3121 def parseconfig(config):
3122 """parse the --config options from the command line"""
3122 """parse the --config options from the command line"""
3123 parsed = []
3123 parsed = []
3124 for cfg in config:
3124 for cfg in config:
3125 try:
3125 try:
3126 name, value = cfg.split('=', 1)
3126 name, value = cfg.split('=', 1)
3127 section, name = name.split('.', 1)
3127 section, name = name.split('.', 1)
3128 if not section or not name:
3128 if not section or not name:
3129 raise IndexError
3129 raise IndexError
3130 parsed.append((section, name, value))
3130 parsed.append((section, name, value))
3131 except (IndexError, ValueError):
3131 except (IndexError, ValueError):
3132 raise util.Abort(_('malformed --config option: %s') % cfg)
3132 raise util.Abort(_('malformed --config option: %s') % cfg)
3133 return parsed
3133 return parsed
3134
3134
3135 def dispatch(args):
3135 def dispatch(args):
3136 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3136 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3137 num = getattr(signal, name, None)
3137 num = getattr(signal, name, None)
3138 if num: signal.signal(num, catchterm)
3138 if num: signal.signal(num, catchterm)
3139
3139
3140 try:
3140 try:
3141 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3141 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3142 except util.Abort, inst:
3142 except util.Abort, inst:
3143 sys.stderr.write(_("abort: %s\n") % inst)
3143 sys.stderr.write(_("abort: %s\n") % inst)
3144 return -1
3144 return -1
3145
3145
3146 load_extensions(u)
3146 load_extensions(u)
3147 u.addreadhook(load_extensions)
3147 u.addreadhook(load_extensions)
3148
3148
3149 try:
3149 try:
3150 cmd, func, args, options, cmdoptions = parse(u, args)
3150 cmd, func, args, options, cmdoptions = parse(u, args)
3151 if options["encoding"]:
3151 if options["encoding"]:
3152 util._encoding = options["encoding"]
3152 util._encoding = options["encoding"]
3153 if options["encodingmode"]:
3153 if options["encodingmode"]:
3154 util._encodingmode = options["encodingmode"]
3154 util._encodingmode = options["encodingmode"]
3155 if options["time"]:
3155 if options["time"]:
3156 def get_times():
3156 def get_times():
3157 t = os.times()
3157 t = os.times()
3158 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3158 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3159 t = (t[0], t[1], t[2], t[3], time.clock())
3159 t = (t[0], t[1], t[2], t[3], time.clock())
3160 return t
3160 return t
3161 s = get_times()
3161 s = get_times()
3162 def print_time():
3162 def print_time():
3163 t = get_times()
3163 t = get_times()
3164 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3164 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3165 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3165 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3166 atexit.register(print_time)
3166 atexit.register(print_time)
3167
3167
3168 # enter the debugger before command execution
3168 # enter the debugger before command execution
3169 if options['debugger']:
3169 if options['debugger']:
3170 pdb.set_trace()
3170 pdb.set_trace()
3171
3171
3172 try:
3172 try:
3173 if options['cwd']:
3173 if options['cwd']:
3174 os.chdir(options['cwd'])
3174 os.chdir(options['cwd'])
3175
3175
3176 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3176 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3177 not options["noninteractive"], options["traceback"],
3177 not options["noninteractive"], options["traceback"],
3178 parseconfig(options["config"]))
3178 parseconfig(options["config"]))
3179
3179
3180 path = u.expandpath(options["repository"]) or ""
3180 path = u.expandpath(options["repository"]) or ""
3181 repo = path and hg.repository(u, path=path) or None
3181 repo = path and hg.repository(u, path=path) or None
3182 if repo and not repo.local():
3182 if repo and not repo.local():
3183 raise util.Abort(_("repository '%s' is not local") % path)
3183 raise util.Abort(_("repository '%s' is not local") % path)
3184
3184
3185 if options['help']:
3185 if options['help']:
3186 return help_(u, cmd, options['version'])
3186 return help_(u, cmd, options['version'])
3187 elif options['version']:
3187 elif options['version']:
3188 return version_(u)
3188 return version_(u)
3189 elif not cmd:
3189 elif not cmd:
3190 return help_(u, 'shortlist')
3190 return help_(u, 'shortlist')
3191
3191
3192 if cmd not in norepo.split():
3192 if cmd not in norepo.split():
3193 try:
3193 try:
3194 if not repo:
3194 if not repo:
3195 repo = hg.repository(u, path=path)
3195 repo = hg.repository(u, path=path)
3196 u = repo.ui
3196 u = repo.ui
3197 except hg.RepoError:
3197 except hg.RepoError:
3198 if cmd not in optionalrepo.split():
3198 if cmd not in optionalrepo.split():
3199 raise
3199 raise
3200 d = lambda: func(u, repo, *args, **cmdoptions)
3200 d = lambda: func(u, repo, *args, **cmdoptions)
3201 else:
3201 else:
3202 d = lambda: func(u, *args, **cmdoptions)
3202 d = lambda: func(u, *args, **cmdoptions)
3203
3203
3204 try:
3204 try:
3205 if options['profile']:
3205 if options['profile']:
3206 import hotshot, hotshot.stats
3206 import hotshot, hotshot.stats
3207 prof = hotshot.Profile("hg.prof")
3207 prof = hotshot.Profile("hg.prof")
3208 try:
3208 try:
3209 try:
3209 try:
3210 return prof.runcall(d)
3210 return prof.runcall(d)
3211 except:
3211 except:
3212 try:
3212 try:
3213 u.warn(_('exception raised - generating '
3213 u.warn(_('exception raised - generating '
3214 'profile anyway\n'))
3214 'profile anyway\n'))
3215 except:
3215 except:
3216 pass
3216 pass
3217 raise
3217 raise
3218 finally:
3218 finally:
3219 prof.close()
3219 prof.close()
3220 stats = hotshot.stats.load("hg.prof")
3220 stats = hotshot.stats.load("hg.prof")
3221 stats.strip_dirs()
3221 stats.strip_dirs()
3222 stats.sort_stats('time', 'calls')
3222 stats.sort_stats('time', 'calls')
3223 stats.print_stats(40)
3223 stats.print_stats(40)
3224 elif options['lsprof']:
3224 elif options['lsprof']:
3225 try:
3225 try:
3226 from mercurial import lsprof
3226 from mercurial import lsprof
3227 except ImportError:
3227 except ImportError:
3228 raise util.Abort(_(
3228 raise util.Abort(_(
3229 'lsprof not available - install from '
3229 'lsprof not available - install from '
3230 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3230 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3231 p = lsprof.Profiler()
3231 p = lsprof.Profiler()
3232 p.enable(subcalls=True)
3232 p.enable(subcalls=True)
3233 try:
3233 try:
3234 return d()
3234 return d()
3235 finally:
3235 finally:
3236 p.disable()
3236 p.disable()
3237 stats = lsprof.Stats(p.getstats())
3237 stats = lsprof.Stats(p.getstats())
3238 stats.sort()
3238 stats.sort()
3239 stats.pprint(top=10, file=sys.stderr, climit=5)
3239 stats.pprint(top=10, file=sys.stderr, climit=5)
3240 else:
3240 else:
3241 return d()
3241 return d()
3242 finally:
3242 finally:
3243 u.flush()
3243 u.flush()
3244 except:
3244 except:
3245 # enter the debugger when we hit an exception
3245 # enter the debugger when we hit an exception
3246 if options['debugger']:
3246 if options['debugger']:
3247 pdb.post_mortem(sys.exc_info()[2])
3247 pdb.post_mortem(sys.exc_info()[2])
3248 u.print_exc()
3248 u.print_exc()
3249 raise
3249 raise
3250 except ParseError, inst:
3250 except ParseError, inst:
3251 if inst.args[0]:
3251 if inst.args[0]:
3252 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3252 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3253 help_(u, inst.args[0])
3253 help_(u, inst.args[0])
3254 else:
3254 else:
3255 u.warn(_("hg: %s\n") % inst.args[1])
3255 u.warn(_("hg: %s\n") % inst.args[1])
3256 help_(u, 'shortlist')
3256 help_(u, 'shortlist')
3257 except AmbiguousCommand, inst:
3257 except AmbiguousCommand, inst:
3258 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3258 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3259 (inst.args[0], " ".join(inst.args[1])))
3259 (inst.args[0], " ".join(inst.args[1])))
3260 except UnknownCommand, inst:
3260 except UnknownCommand, inst:
3261 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3261 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3262 help_(u, 'shortlist')
3262 help_(u, 'shortlist')
3263 except hg.RepoError, inst:
3263 except hg.RepoError, inst:
3264 u.warn(_("abort: %s!\n") % inst)
3264 u.warn(_("abort: %s!\n") % inst)
3265 except lock.LockHeld, inst:
3265 except lock.LockHeld, inst:
3266 if inst.errno == errno.ETIMEDOUT:
3266 if inst.errno == errno.ETIMEDOUT:
3267 reason = _('timed out waiting for lock held by %s') % inst.locker
3267 reason = _('timed out waiting for lock held by %s') % inst.locker
3268 else:
3268 else:
3269 reason = _('lock held by %s') % inst.locker
3269 reason = _('lock held by %s') % inst.locker
3270 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3270 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3271 except lock.LockUnavailable, inst:
3271 except lock.LockUnavailable, inst:
3272 u.warn(_("abort: could not lock %s: %s\n") %
3272 u.warn(_("abort: could not lock %s: %s\n") %
3273 (inst.desc or inst.filename, inst.strerror))
3273 (inst.desc or inst.filename, inst.strerror))
3274 except revlog.RevlogError, inst:
3274 except revlog.RevlogError, inst:
3275 u.warn(_("abort: %s!\n") % inst)
3275 u.warn(_("abort: %s!\n") % inst)
3276 except util.SignalInterrupt:
3276 except util.SignalInterrupt:
3277 u.warn(_("killed!\n"))
3277 u.warn(_("killed!\n"))
3278 except KeyboardInterrupt:
3278 except KeyboardInterrupt:
3279 try:
3279 try:
3280 u.warn(_("interrupted!\n"))
3280 u.warn(_("interrupted!\n"))
3281 except IOError, inst:
3281 except IOError, inst:
3282 if inst.errno == errno.EPIPE:
3282 if inst.errno == errno.EPIPE:
3283 if u.debugflag:
3283 if u.debugflag:
3284 u.warn(_("\nbroken pipe\n"))
3284 u.warn(_("\nbroken pipe\n"))
3285 else:
3285 else:
3286 raise
3286 raise
3287 except socket.error, inst:
3287 except socket.error, inst:
3288 u.warn(_("abort: %s\n") % inst[1])
3288 u.warn(_("abort: %s\n") % inst[1])
3289 except IOError, inst:
3289 except IOError, inst:
3290 if hasattr(inst, "code"):
3290 if hasattr(inst, "code"):
3291 u.warn(_("abort: %s\n") % inst)
3291 u.warn(_("abort: %s\n") % inst)
3292 elif hasattr(inst, "reason"):
3292 elif hasattr(inst, "reason"):
3293 try: # usually it is in the form (errno, strerror)
3293 try: # usually it is in the form (errno, strerror)
3294 reason = inst.reason.args[1]
3294 reason = inst.reason.args[1]
3295 except: # it might be anything, for example a string
3295 except: # it might be anything, for example a string
3296 reason = inst.reason
3296 reason = inst.reason
3297 u.warn(_("abort: error: %s\n") % reason)
3297 u.warn(_("abort: error: %s\n") % reason)
3298 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3298 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3299 if u.debugflag:
3299 if u.debugflag:
3300 u.warn(_("broken pipe\n"))
3300 u.warn(_("broken pipe\n"))
3301 elif getattr(inst, "strerror", None):
3301 elif getattr(inst, "strerror", None):
3302 if getattr(inst, "filename", None):
3302 if getattr(inst, "filename", None):
3303 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3303 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3304 else:
3304 else:
3305 u.warn(_("abort: %s\n") % inst.strerror)
3305 u.warn(_("abort: %s\n") % inst.strerror)
3306 else:
3306 else:
3307 raise
3307 raise
3308 except OSError, inst:
3308 except OSError, inst:
3309 if getattr(inst, "filename", None):
3309 if getattr(inst, "filename", None):
3310 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3310 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3311 else:
3311 else:
3312 u.warn(_("abort: %s\n") % inst.strerror)
3312 u.warn(_("abort: %s\n") % inst.strerror)
3313 except util.UnexpectedOutput, inst:
3313 except util.UnexpectedOutput, inst:
3314 u.warn(_("abort: %s") % inst[0])
3314 u.warn(_("abort: %s") % inst[0])
3315 if not isinstance(inst[1], basestring):
3315 if not isinstance(inst[1], basestring):
3316 u.warn(" %r\n" % (inst[1],))
3316 u.warn(" %r\n" % (inst[1],))
3317 elif not inst[1]:
3317 elif not inst[1]:
3318 u.warn(_(" empty string\n"))
3318 u.warn(_(" empty string\n"))
3319 else:
3319 else:
3320 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3320 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3321 except util.Abort, inst:
3321 except util.Abort, inst:
3322 u.warn(_("abort: %s\n") % inst)
3322 u.warn(_("abort: %s\n") % inst)
3323 except TypeError, inst:
3323 except TypeError, inst:
3324 # was this an argument error?
3324 # was this an argument error?
3325 tb = traceback.extract_tb(sys.exc_info()[2])
3325 tb = traceback.extract_tb(sys.exc_info()[2])
3326 if len(tb) > 2: # no
3326 if len(tb) > 2: # no
3327 raise
3327 raise
3328 u.debug(inst, "\n")
3328 u.debug(inst, "\n")
3329 u.warn(_("%s: invalid arguments\n") % cmd)
3329 u.warn(_("%s: invalid arguments\n") % cmd)
3330 help_(u, cmd)
3330 help_(u, cmd)
3331 except SystemExit, inst:
3331 except SystemExit, inst:
3332 # Commands shouldn't sys.exit directly, but give a return code.
3332 # Commands shouldn't sys.exit directly, but give a return code.
3333 # Just in case catch this and and pass exit code to caller.
3333 # Just in case catch this and and pass exit code to caller.
3334 return inst.code
3334 return inst.code
3335 except:
3335 except:
3336 u.warn(_("** unknown exception encountered, details follow\n"))
3336 u.warn(_("** unknown exception encountered, details follow\n"))
3337 u.warn(_("** report bug details to "
3337 u.warn(_("** report bug details to "
3338 "http://www.selenic.com/mercurial/bts\n"))
3338 "http://www.selenic.com/mercurial/bts\n"))
3339 u.warn(_("** or mercurial@selenic.com\n"))
3339 u.warn(_("** or mercurial@selenic.com\n"))
3340 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3340 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3341 % version.get_version())
3341 % version.get_version())
3342 raise
3342 raise
3343
3343
3344 return -1
3344 return -1
@@ -1,509 +1,509 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
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 from node import *
8 from node import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import demandload
10 from demandload import demandload
11 demandload(globals(), "ancestor bdiff repo revlog util os")
11 demandload(globals(), "ancestor bdiff repo revlog util os")
12
12
13 class changectx(object):
13 class changectx(object):
14 """A changecontext object makes access to data related to a particular
14 """A changecontext object makes access to data related to a particular
15 changeset convenient."""
15 changeset convenient."""
16 def __init__(self, repo, changeid=None):
16 def __init__(self, repo, changeid=None):
17 """changeid is a revision number, node, or tag"""
17 """changeid is a revision number, node, or tag"""
18 self._repo = repo
18 self._repo = repo
19
19
20 if not changeid and changeid != 0:
20 if not changeid and changeid != 0:
21 p1, p2 = self._repo.dirstate.parents()
21 p1, p2 = self._repo.dirstate.parents()
22 self._rev = self._repo.changelog.rev(p1)
22 self._rev = self._repo.changelog.rev(p1)
23 if self._rev == -1:
23 if self._rev == -1:
24 changeid = 'tip'
24 changeid = 'tip'
25 else:
25 else:
26 self._node = p1
26 self._node = p1
27 return
27 return
28
28
29 self._node = self._repo.lookup(changeid)
29 self._node = self._repo.lookup(changeid)
30 self._rev = self._repo.changelog.rev(self._node)
30 self._rev = self._repo.changelog.rev(self._node)
31
31
32 def __str__(self):
32 def __str__(self):
33 return short(self.node())
33 return short(self.node())
34
34
35 def __repr__(self):
35 def __repr__(self):
36 return "<changectx %s>" % str(self)
36 return "<changectx %s>" % str(self)
37
37
38 def __eq__(self, other):
38 def __eq__(self, other):
39 try:
39 try:
40 return self._rev == other._rev
40 return self._rev == other._rev
41 except AttributeError:
41 except AttributeError:
42 return False
42 return False
43
43
44 def __nonzero__(self):
44 def __nonzero__(self):
45 return self._rev != nullrev
45 return self._rev != nullrev
46
46
47 def __getattr__(self, name):
47 def __getattr__(self, name):
48 if name == '_changeset':
48 if name == '_changeset':
49 self._changeset = self._repo.changelog.read(self.node())
49 self._changeset = self._repo.changelog.read(self.node())
50 return self._changeset
50 return self._changeset
51 elif name == '_manifest':
51 elif name == '_manifest':
52 self._manifest = self._repo.manifest.read(self._changeset[0])
52 self._manifest = self._repo.manifest.read(self._changeset[0])
53 return self._manifest
53 return self._manifest
54 elif name == '_manifestdelta':
54 elif name == '_manifestdelta':
55 md = self._repo.manifest.readdelta(self._changeset[0])
55 md = self._repo.manifest.readdelta(self._changeset[0])
56 self._manifestdelta = md
56 self._manifestdelta = md
57 return self._manifestdelta
57 return self._manifestdelta
58 else:
58 else:
59 raise AttributeError, name
59 raise AttributeError, name
60
60
61 def changeset(self): return self._changeset
61 def changeset(self): return self._changeset
62 def manifest(self): return self._manifest
62 def manifest(self): return self._manifest
63
63
64 def rev(self): return self._rev
64 def rev(self): return self._rev
65 def node(self): return self._node
65 def node(self): return self._node
66 def user(self): return self._changeset[1]
66 def user(self): return self._changeset[1]
67 def date(self): return self._changeset[2]
67 def date(self): return self._changeset[2]
68 def files(self): return self._changeset[3]
68 def files(self): return self._changeset[3]
69 def description(self): return self._changeset[4]
69 def description(self): return self._changeset[4]
70 def branch(self): return self._changeset[5].get("branch", "")
70 def branch(self): return self._changeset[5].get("branch", "")
71
71
72 def parents(self):
72 def parents(self):
73 """return contexts for each parent changeset"""
73 """return contexts for each parent changeset"""
74 p = self._repo.changelog.parents(self._node)
74 p = self._repo.changelog.parents(self._node)
75 return [changectx(self._repo, x) for x in p]
75 return [changectx(self._repo, x) for x in p]
76
76
77 def children(self):
77 def children(self):
78 """return contexts for each child changeset"""
78 """return contexts for each child changeset"""
79 c = self._repo.changelog.children(self._node)
79 c = self._repo.changelog.children(self._node)
80 return [changectx(self._repo, x) for x in c]
80 return [changectx(self._repo, x) for x in c]
81
81
82 def filenode(self, path):
82 def filenode(self, path):
83 if '_manifest' in self.__dict__:
83 if '_manifest' in self.__dict__:
84 try:
84 try:
85 return self._manifest[path]
85 return self._manifest[path]
86 except KeyError:
86 except KeyError:
87 raise repo.LookupError(_("'%s' not found in manifest") % path)
87 raise repo.LookupError(_("'%s' not found in manifest") % path)
88 if '_manifestdelta' in self.__dict__ or path in self.files():
88 if '_manifestdelta' in self.__dict__ or path in self.files():
89 if path in self._manifestdelta:
89 if path in self._manifestdelta:
90 return self._manifestdelta[path]
90 return self._manifestdelta[path]
91 node, flag = self._repo.manifest.find(self._changeset[0], path)
91 node, flag = self._repo.manifest.find(self._changeset[0], path)
92 if not node:
92 if not node:
93 raise repo.LookupError(_("'%s' not found in manifest") % path)
93 raise repo.LookupError(_("'%s' not found in manifest") % path)
94
94
95 return node
95 return node
96
96
97 def filectx(self, path, fileid=None):
97 def filectx(self, path, fileid=None):
98 """get a file context from this changeset"""
98 """get a file context from this changeset"""
99 if fileid is None:
99 if fileid is None:
100 fileid = self.filenode(path)
100 fileid = self.filenode(path)
101 return filectx(self._repo, path, fileid=fileid, changectx=self)
101 return filectx(self._repo, path, fileid=fileid, changectx=self)
102
102
103 def filectxs(self):
103 def filectxs(self):
104 """generate a file context for each file in this changeset's
104 """generate a file context for each file in this changeset's
105 manifest"""
105 manifest"""
106 mf = self.manifest()
106 mf = self.manifest()
107 m = mf.keys()
107 m = mf.keys()
108 m.sort()
108 m.sort()
109 for f in m:
109 for f in m:
110 yield self.filectx(f, fileid=mf[f])
110 yield self.filectx(f, fileid=mf[f])
111
111
112 def ancestor(self, c2):
112 def ancestor(self, c2):
113 """
113 """
114 return the ancestor context of self and c2
114 return the ancestor context of self and c2
115 """
115 """
116 n = self._repo.changelog.ancestor(self._node, c2._node)
116 n = self._repo.changelog.ancestor(self._node, c2._node)
117 return changectx(self._repo, n)
117 return changectx(self._repo, n)
118
118
119 class filectx(object):
119 class filectx(object):
120 """A filecontext object makes access to data related to a particular
120 """A filecontext object makes access to data related to a particular
121 filerevision convenient."""
121 filerevision convenient."""
122 def __init__(self, repo, path, changeid=None, fileid=None,
122 def __init__(self, repo, path, changeid=None, fileid=None,
123 filelog=None, changectx=None):
123 filelog=None, changectx=None):
124 """changeid can be a changeset revision, node, or tag.
124 """changeid can be a changeset revision, node, or tag.
125 fileid can be a file revision or node."""
125 fileid can be a file revision or node."""
126 self._repo = repo
126 self._repo = repo
127 self._path = path
127 self._path = path
128
128
129 assert changeid is not None or fileid is not None
129 assert changeid is not None or fileid is not None
130
130
131 if filelog:
131 if filelog:
132 self._filelog = filelog
132 self._filelog = filelog
133 if changectx:
133 if changectx:
134 self._changectx = changectx
134 self._changectx = changectx
135 self._changeid = changectx.node()
135 self._changeid = changectx.node()
136
136
137 if fileid is None:
137 if fileid is None:
138 self._changeid = changeid
138 self._changeid = changeid
139 else:
139 else:
140 self._fileid = fileid
140 self._fileid = fileid
141
141
142 def __getattr__(self, name):
142 def __getattr__(self, name):
143 if name == '_changectx':
143 if name == '_changectx':
144 self._changectx = changectx(self._repo, self._changeid)
144 self._changectx = changectx(self._repo, self._changeid)
145 return self._changectx
145 return self._changectx
146 elif name == '_filelog':
146 elif name == '_filelog':
147 self._filelog = self._repo.file(self._path)
147 self._filelog = self._repo.file(self._path)
148 return self._filelog
148 return self._filelog
149 elif name == '_changeid':
149 elif name == '_changeid':
150 self._changeid = self._filelog.linkrev(self._filenode)
150 self._changeid = self._filelog.linkrev(self._filenode)
151 return self._changeid
151 return self._changeid
152 elif name == '_filenode':
152 elif name == '_filenode':
153 try:
153 try:
154 if '_fileid' in self.__dict__:
154 if '_fileid' in self.__dict__:
155 self._filenode = self._filelog.lookup(self._fileid)
155 self._filenode = self._filelog.lookup(self._fileid)
156 else:
156 else:
157 self._filenode = self._changectx.filenode(self._path)
157 self._filenode = self._changectx.filenode(self._path)
158 except revlog.RevlogError, inst:
158 except revlog.RevlogError, inst:
159 raise repo.LookupError(str(inst))
159 raise repo.LookupError(str(inst))
160 return self._filenode
160 return self._filenode
161 elif name == '_filerev':
161 elif name == '_filerev':
162 self._filerev = self._filelog.rev(self._filenode)
162 self._filerev = self._filelog.rev(self._filenode)
163 return self._filerev
163 return self._filerev
164 else:
164 else:
165 raise AttributeError, name
165 raise AttributeError, name
166
166
167 def __nonzero__(self):
167 def __nonzero__(self):
168 try:
168 try:
169 n = self._filenode
169 n = self._filenode
170 return True
170 return True
171 except repo.LookupError:
171 except repo.LookupError:
172 # file is missing
172 # file is missing
173 return False
173 return False
174
174
175 def __str__(self):
175 def __str__(self):
176 return "%s@%s" % (self.path(), short(self.node()))
176 return "%s@%s" % (self.path(), short(self.node()))
177
177
178 def __repr__(self):
178 def __repr__(self):
179 return "<filectx %s>" % str(self)
179 return "<filectx %s>" % str(self)
180
180
181 def __eq__(self, other):
181 def __eq__(self, other):
182 try:
182 try:
183 return (self._path == other._path
183 return (self._path == other._path
184 and self._changeid == other._changeid)
184 and self._changeid == other._changeid)
185 except AttributeError:
185 except AttributeError:
186 return False
186 return False
187
187
188 def filectx(self, fileid):
188 def filectx(self, fileid):
189 '''opens an arbitrary revision of the file without
189 '''opens an arbitrary revision of the file without
190 opening a new filelog'''
190 opening a new filelog'''
191 return filectx(self._repo, self._path, fileid=fileid,
191 return filectx(self._repo, self._path, fileid=fileid,
192 filelog=self._filelog)
192 filelog=self._filelog)
193
193
194 def filerev(self): return self._filerev
194 def filerev(self): return self._filerev
195 def filenode(self): return self._filenode
195 def filenode(self): return self._filenode
196 def filelog(self): return self._filelog
196 def filelog(self): return self._filelog
197
197
198 def rev(self):
198 def rev(self):
199 if '_changectx' in self.__dict__:
199 if '_changectx' in self.__dict__:
200 return self._changectx.rev()
200 return self._changectx.rev()
201 return self._filelog.linkrev(self._filenode)
201 return self._filelog.linkrev(self._filenode)
202
202
203 def node(self): return self._changectx.node()
203 def node(self): return self._changectx.node()
204 def user(self): return self._changectx.user()
204 def user(self): return self._changectx.user()
205 def date(self): return self._changectx.date()
205 def date(self): return self._changectx.date()
206 def files(self): return self._changectx.files()
206 def files(self): return self._changectx.files()
207 def description(self): return self._changectx.description()
207 def description(self): return self._changectx.description()
208 def branch(self): return self._changectx.branch()
208 def branch(self): return self._changectx.branch()
209 def manifest(self): return self._changectx.manifest()
209 def manifest(self): return self._changectx.manifest()
210 def changectx(self): return self._changectx
210 def changectx(self): return self._changectx
211
211
212 def data(self): return self._filelog.read(self._filenode)
212 def data(self): return self._filelog.read(self._filenode)
213 def renamed(self): return self._filelog.renamed(self._filenode)
213 def renamed(self): return self._filelog.renamed(self._filenode)
214 def path(self): return self._path
214 def path(self): return self._path
215 def size(self): return self._filelog.size(self._filerev)
215 def size(self): return self._filelog.size(self._filerev)
216
216
217 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
217 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
218
218
219 def parents(self):
219 def parents(self):
220 p = self._path
220 p = self._path
221 fl = self._filelog
221 fl = self._filelog
222 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
222 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
223
223
224 r = self.renamed()
224 r = self.renamed()
225 if r:
225 if r:
226 pl[0] = (r[0], r[1], None)
226 pl[0] = (r[0], r[1], None)
227
227
228 return [filectx(self._repo, p, fileid=n, filelog=l)
228 return [filectx(self._repo, p, fileid=n, filelog=l)
229 for p,n,l in pl if n != nullid]
229 for p,n,l in pl if n != nullid]
230
230
231 def children(self):
231 def children(self):
232 # hard for renames
232 # hard for renames
233 c = self._filelog.children(self._filenode)
233 c = self._filelog.children(self._filenode)
234 return [filectx(self._repo, self._path, fileid=x,
234 return [filectx(self._repo, self._path, fileid=x,
235 filelog=self._filelog) for x in c]
235 filelog=self._filelog) for x in c]
236
236
237 def annotate(self, follow=False):
237 def annotate(self, follow=False):
238 '''returns a list of tuples of (ctx, line) for each line
238 '''returns a list of tuples of (ctx, line) for each line
239 in the file, where ctx is the filectx of the node where
239 in the file, where ctx is the filectx of the node where
240 that line was last changed'''
240 that line was last changed'''
241
241
242 def decorate(text, rev):
242 def decorate(text, rev):
243 return ([rev] * len(text.splitlines()), text)
243 return ([rev] * len(text.splitlines()), text)
244
244
245 def pair(parent, child):
245 def pair(parent, child):
246 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
246 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
247 child[0][b1:b2] = parent[0][a1:a2]
247 child[0][b1:b2] = parent[0][a1:a2]
248 return child
248 return child
249
249
250 getlog = util.cachefunc(lambda x: self._repo.file(x))
250 getlog = util.cachefunc(lambda x: self._repo.file(x))
251 def getctx(path, fileid):
251 def getctx(path, fileid):
252 log = path == self._path and self._filelog or getlog(path)
252 log = path == self._path and self._filelog or getlog(path)
253 return filectx(self._repo, path, fileid=fileid, filelog=log)
253 return filectx(self._repo, path, fileid=fileid, filelog=log)
254 getctx = util.cachefunc(getctx)
254 getctx = util.cachefunc(getctx)
255
255
256 def parents(f):
256 def parents(f):
257 # we want to reuse filectx objects as much as possible
257 # we want to reuse filectx objects as much as possible
258 p = f._path
258 p = f._path
259 if f._filerev is None: # working dir
259 if f._filerev is None: # working dir
260 pl = [(n.path(), n.filerev()) for n in f.parents()]
260 pl = [(n.path(), n.filerev()) for n in f.parents()]
261 else:
261 else:
262 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
262 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
263
263
264 if follow:
264 if follow:
265 r = f.renamed()
265 r = f.renamed()
266 if r:
266 if r:
267 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
267 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
268
268
269 return [getctx(p, n) for p, n in pl if n != nullrev]
269 return [getctx(p, n) for p, n in pl if n != nullrev]
270
270
271 # use linkrev to find the first changeset where self appeared
271 # use linkrev to find the first changeset where self appeared
272 if self.rev() != self._filelog.linkrev(self._filenode):
272 if self.rev() != self._filelog.linkrev(self._filenode):
273 base = self.filectx(self.filerev())
273 base = self.filectx(self.filerev())
274 else:
274 else:
275 base = self
275 base = self
276
276
277 # find all ancestors
277 # find all ancestors
278 needed = {base: 1}
278 needed = {base: 1}
279 visit = [base]
279 visit = [base]
280 files = [base._path]
280 files = [base._path]
281 while visit:
281 while visit:
282 f = visit.pop(0)
282 f = visit.pop(0)
283 for p in parents(f):
283 for p in parents(f):
284 if p not in needed:
284 if p not in needed:
285 needed[p] = 1
285 needed[p] = 1
286 visit.append(p)
286 visit.append(p)
287 if p._path not in files:
287 if p._path not in files:
288 files.append(p._path)
288 files.append(p._path)
289 else:
289 else:
290 # count how many times we'll use this
290 # count how many times we'll use this
291 needed[p] += 1
291 needed[p] += 1
292
292
293 # sort by revision (per file) which is a topological order
293 # sort by revision (per file) which is a topological order
294 visit = []
294 visit = []
295 files.reverse()
295 files.reverse()
296 for f in files:
296 for f in files:
297 fn = [(n._filerev, n) for n in needed.keys() if n._path == f]
297 fn = [(n._filerev, n) for n in needed.keys() if n._path == f]
298 fn.sort()
298 fn.sort()
299 visit.extend(fn)
299 visit.extend(fn)
300 hist = {}
300 hist = {}
301
301
302 for r, f in visit:
302 for r, f in visit:
303 curr = decorate(f.data(), f)
303 curr = decorate(f.data(), f)
304 for p in parents(f):
304 for p in parents(f):
305 if p != nullid:
305 if p != nullid:
306 curr = pair(hist[p], curr)
306 curr = pair(hist[p], curr)
307 # trim the history of unneeded revs
307 # trim the history of unneeded revs
308 needed[p] -= 1
308 needed[p] -= 1
309 if not needed[p]:
309 if not needed[p]:
310 del hist[p]
310 del hist[p]
311 hist[f] = curr
311 hist[f] = curr
312
312
313 return zip(hist[f][0], hist[f][1].splitlines(1))
313 return zip(hist[f][0], hist[f][1].splitlines(1))
314
314
315 def ancestor(self, fc2):
315 def ancestor(self, fc2):
316 """
316 """
317 find the common ancestor file context, if any, of self, and fc2
317 find the common ancestor file context, if any, of self, and fc2
318 """
318 """
319
319
320 acache = {}
320 acache = {}
321
321
322 # prime the ancestor cache for the working directory
322 # prime the ancestor cache for the working directory
323 for c in (self, fc2):
323 for c in (self, fc2):
324 if c._filerev == None:
324 if c._filerev == None:
325 pl = [(n.path(), n.filenode()) for n in c.parents()]
325 pl = [(n.path(), n.filenode()) for n in c.parents()]
326 acache[(c._path, None)] = pl
326 acache[(c._path, None)] = pl
327
327
328 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
328 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
329 def parents(vertex):
329 def parents(vertex):
330 if vertex in acache:
330 if vertex in acache:
331 return acache[vertex]
331 return acache[vertex]
332 f, n = vertex
332 f, n = vertex
333 if f not in flcache:
333 if f not in flcache:
334 flcache[f] = self._repo.file(f)
334 flcache[f] = self._repo.file(f)
335 fl = flcache[f]
335 fl = flcache[f]
336 pl = [(f, p) for p in fl.parents(n) if p != nullid]
336 pl = [(f, p) for p in fl.parents(n) if p != nullid]
337 re = fl.renamed(n)
337 re = fl.renamed(n)
338 if re:
338 if re:
339 pl.append(re)
339 pl.append(re)
340 acache[vertex] = pl
340 acache[vertex] = pl
341 return pl
341 return pl
342
342
343 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
343 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
344 v = ancestor.ancestor(a, b, parents)
344 v = ancestor.ancestor(a, b, parents)
345 if v:
345 if v:
346 f, n = v
346 f, n = v
347 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
347 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
348
348
349 return None
349 return None
350
350
351 class workingctx(changectx):
351 class workingctx(changectx):
352 """A workingctx object makes access to data related to
352 """A workingctx object makes access to data related to
353 the current working directory convenient."""
353 the current working directory convenient."""
354 def __init__(self, repo):
354 def __init__(self, repo):
355 self._repo = repo
355 self._repo = repo
356 self._rev = None
356 self._rev = None
357 self._node = None
357 self._node = None
358
358
359 def __str__(self):
359 def __str__(self):
360 return str(self._parents[0]) + "+"
360 return str(self._parents[0]) + "+"
361
361
362 def __nonzero__(self):
362 def __nonzero__(self):
363 return True
363 return True
364
364
365 def __getattr__(self, name):
365 def __getattr__(self, name):
366 if name == '_parents':
366 if name == '_parents':
367 self._parents = self._repo.parents()
367 self._parents = self._repo.parents()
368 return self._parents
368 return self._parents
369 if name == '_status':
369 if name == '_status':
370 self._status = self._repo.status()
370 self._status = self._repo.status()
371 return self._status
371 return self._status
372 if name == '_manifest':
372 if name == '_manifest':
373 self._buildmanifest()
373 self._buildmanifest()
374 return self._manifest
374 return self._manifest
375 else:
375 else:
376 raise AttributeError, name
376 raise AttributeError, name
377
377
378 def _buildmanifest(self):
378 def _buildmanifest(self):
379 """generate a manifest corresponding to the working directory"""
379 """generate a manifest corresponding to the working directory"""
380
380
381 man = self._parents[0].manifest().copy()
381 man = self._parents[0].manifest().copy()
382 copied = self._repo.dirstate.copies()
382 copied = self._repo.dirstate.copies()
383 modified, added, removed, deleted, unknown = self._status[:5]
383 modified, added, removed, deleted, unknown = self._status[:5]
384 for i, l in (("a", added), ("m", modified), ("u", unknown)):
384 for i, l in (("a", added), ("m", modified), ("u", unknown)):
385 for f in l:
385 for f in l:
386 man[f] = man.get(copied.get(f, f), nullid) + i
386 man[f] = man.get(copied.get(f, f), nullid) + i
387 try:
387 try:
388 man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f)))
388 man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f)))
389 except OSError:
389 except OSError:
390 pass
390 pass
391
391
392 for f in deleted + removed:
392 for f in deleted + removed:
393 if f in man:
393 if f in man:
394 del man[f]
394 del man[f]
395
395
396 self._manifest = man
396 self._manifest = man
397
397
398 def manifest(self): return self._manifest
398 def manifest(self): return self._manifest
399
399
400 def user(self): return self._repo.ui.username()
400 def user(self): return self._repo.ui.username()
401 def date(self): return util.makedate()
401 def date(self): return util.makedate()
402 def description(self): return ""
402 def description(self): return ""
403 def files(self):
403 def files(self):
404 f = self.modified() + self.added() + self.removed()
404 f = self.modified() + self.added() + self.removed()
405 f.sort()
405 f.sort()
406 return f
406 return f
407
407
408 def modified(self): return self._status[0]
408 def modified(self): return self._status[0]
409 def added(self): return self._status[1]
409 def added(self): return self._status[1]
410 def removed(self): return self._status[2]
410 def removed(self): return self._status[2]
411 def deleted(self): return self._status[3]
411 def deleted(self): return self._status[3]
412 def unknown(self): return self._status[4]
412 def unknown(self): return self._status[4]
413 def clean(self): return self._status[5]
413 def clean(self): return self._status[5]
414 def branch(self):
414 def branch(self):
415 try:
415 try:
416 return self._repo.opener("branch").read().strip()
416 return self._repo.opener("branch").read().strip() or "default"
417 except IOError:
417 except IOError:
418 return ""
418 return "default"
419
419
420 def parents(self):
420 def parents(self):
421 """return contexts for each parent changeset"""
421 """return contexts for each parent changeset"""
422 return self._parents
422 return self._parents
423
423
424 def children(self):
424 def children(self):
425 return []
425 return []
426
426
427 def filectx(self, path):
427 def filectx(self, path):
428 """get a file context from the working directory"""
428 """get a file context from the working directory"""
429 return workingfilectx(self._repo, path, workingctx=self)
429 return workingfilectx(self._repo, path, workingctx=self)
430
430
431 def ancestor(self, c2):
431 def ancestor(self, c2):
432 """return the ancestor context of self and c2"""
432 """return the ancestor context of self and c2"""
433 return self._parents[0].ancestor(c2) # punt on two parents for now
433 return self._parents[0].ancestor(c2) # punt on two parents for now
434
434
435 class workingfilectx(filectx):
435 class workingfilectx(filectx):
436 """A workingfilectx object makes access to data related to a particular
436 """A workingfilectx object makes access to data related to a particular
437 file in the working directory convenient."""
437 file in the working directory convenient."""
438 def __init__(self, repo, path, filelog=None, workingctx=None):
438 def __init__(self, repo, path, filelog=None, workingctx=None):
439 """changeid can be a changeset revision, node, or tag.
439 """changeid can be a changeset revision, node, or tag.
440 fileid can be a file revision or node."""
440 fileid can be a file revision or node."""
441 self._repo = repo
441 self._repo = repo
442 self._path = path
442 self._path = path
443 self._changeid = None
443 self._changeid = None
444 self._filerev = self._filenode = None
444 self._filerev = self._filenode = None
445
445
446 if filelog:
446 if filelog:
447 self._filelog = filelog
447 self._filelog = filelog
448 if workingctx:
448 if workingctx:
449 self._changectx = workingctx
449 self._changectx = workingctx
450
450
451 def __getattr__(self, name):
451 def __getattr__(self, name):
452 if name == '_changectx':
452 if name == '_changectx':
453 self._changectx = workingctx(repo)
453 self._changectx = workingctx(repo)
454 return self._changectx
454 return self._changectx
455 elif name == '_repopath':
455 elif name == '_repopath':
456 self._repopath = (self._repo.dirstate.copied(self._path)
456 self._repopath = (self._repo.dirstate.copied(self._path)
457 or self._path)
457 or self._path)
458 return self._repopath
458 return self._repopath
459 elif name == '_filelog':
459 elif name == '_filelog':
460 self._filelog = self._repo.file(self._repopath)
460 self._filelog = self._repo.file(self._repopath)
461 return self._filelog
461 return self._filelog
462 else:
462 else:
463 raise AttributeError, name
463 raise AttributeError, name
464
464
465 def __nonzero__(self):
465 def __nonzero__(self):
466 return True
466 return True
467
467
468 def __str__(self):
468 def __str__(self):
469 return "%s@%s" % (self.path(), self._changectx)
469 return "%s@%s" % (self.path(), self._changectx)
470
470
471 def filectx(self, fileid):
471 def filectx(self, fileid):
472 '''opens an arbitrary revision of the file without
472 '''opens an arbitrary revision of the file without
473 opening a new filelog'''
473 opening a new filelog'''
474 return filectx(self._repo, self._repopath, fileid=fileid,
474 return filectx(self._repo, self._repopath, fileid=fileid,
475 filelog=self._filelog)
475 filelog=self._filelog)
476
476
477 def rev(self):
477 def rev(self):
478 if '_changectx' in self.__dict__:
478 if '_changectx' in self.__dict__:
479 return self._changectx.rev()
479 return self._changectx.rev()
480 return self._filelog.linkrev(self._filenode)
480 return self._filelog.linkrev(self._filenode)
481
481
482 def data(self): return self._repo.wread(self._path)
482 def data(self): return self._repo.wread(self._path)
483 def renamed(self):
483 def renamed(self):
484 rp = self._repopath
484 rp = self._repopath
485 if rp == self._path:
485 if rp == self._path:
486 return None
486 return None
487 return rp, self._workingctx._parents._manifest.get(rp, nullid)
487 return rp, self._workingctx._parents._manifest.get(rp, nullid)
488
488
489 def parents(self):
489 def parents(self):
490 '''return parent filectxs, following copies if necessary'''
490 '''return parent filectxs, following copies if necessary'''
491 p = self._path
491 p = self._path
492 rp = self._repopath
492 rp = self._repopath
493 pcl = self._changectx._parents
493 pcl = self._changectx._parents
494 fl = self._filelog
494 fl = self._filelog
495 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
495 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
496 if len(pcl) > 1:
496 if len(pcl) > 1:
497 if rp != p:
497 if rp != p:
498 fl = None
498 fl = None
499 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
499 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
500
500
501 return [filectx(self._repo, p, fileid=n, filelog=l)
501 return [filectx(self._repo, p, fileid=n, filelog=l)
502 for p,n,l in pl if n != nullid]
502 for p,n,l in pl if n != nullid]
503
503
504 def children(self):
504 def children(self):
505 return []
505 return []
506
506
507 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
507 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
508
508
509 def cmp(self, text): return self._repo.wread(self._path) == text
509 def cmp(self, text): return self._repo.wread(self._path) == text
@@ -1,2008 +1,2008 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
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 from node import *
8 from node import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 import repo
11 import repo
12 demandload(globals(), "appendfile changegroup")
12 demandload(globals(), "appendfile changegroup")
13 demandload(globals(), "changelog dirstate filelog manifest context")
13 demandload(globals(), "changelog dirstate filelog manifest context")
14 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
14 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
15 demandload(globals(), "os revlog time util")
15 demandload(globals(), "os revlog time util")
16
16
17 class localrepository(repo.repository):
17 class localrepository(repo.repository):
18 capabilities = ('lookup', 'changegroupsubset')
18 capabilities = ('lookup', 'changegroupsubset')
19 supported = ('revlogv1', 'store')
19 supported = ('revlogv1', 'store')
20 branchcache_features = ('unnamed',)
20 branchcache_features = ('default',)
21
21
22 def __del__(self):
22 def __del__(self):
23 self.transhandle = None
23 self.transhandle = None
24 def __init__(self, parentui, path=None, create=0):
24 def __init__(self, parentui, path=None, create=0):
25 repo.repository.__init__(self)
25 repo.repository.__init__(self)
26 if not path:
26 if not path:
27 p = os.getcwd()
27 p = os.getcwd()
28 while not os.path.isdir(os.path.join(p, ".hg")):
28 while not os.path.isdir(os.path.join(p, ".hg")):
29 oldp = p
29 oldp = p
30 p = os.path.dirname(p)
30 p = os.path.dirname(p)
31 if p == oldp:
31 if p == oldp:
32 raise repo.RepoError(_("There is no Mercurial repository"
32 raise repo.RepoError(_("There is no Mercurial repository"
33 " here (.hg not found)"))
33 " here (.hg not found)"))
34 path = p
34 path = p
35
35
36 self.root = os.path.realpath(path)
36 self.root = os.path.realpath(path)
37 self.path = os.path.join(self.root, ".hg")
37 self.path = os.path.join(self.root, ".hg")
38 self.origroot = path
38 self.origroot = path
39 self.opener = util.opener(self.path)
39 self.opener = util.opener(self.path)
40 self.wopener = util.opener(self.root)
40 self.wopener = util.opener(self.root)
41
41
42 if not os.path.isdir(self.path):
42 if not os.path.isdir(self.path):
43 if create:
43 if create:
44 if not os.path.exists(path):
44 if not os.path.exists(path):
45 os.mkdir(path)
45 os.mkdir(path)
46 os.mkdir(self.path)
46 os.mkdir(self.path)
47 os.mkdir(os.path.join(self.path, "store"))
47 os.mkdir(os.path.join(self.path, "store"))
48 requirements = ("revlogv1", "store")
48 requirements = ("revlogv1", "store")
49 reqfile = self.opener("requires", "w")
49 reqfile = self.opener("requires", "w")
50 for r in requirements:
50 for r in requirements:
51 reqfile.write("%s\n" % r)
51 reqfile.write("%s\n" % r)
52 reqfile.close()
52 reqfile.close()
53 # create an invalid changelog
53 # create an invalid changelog
54 self.opener("00changelog.i", "a").write(
54 self.opener("00changelog.i", "a").write(
55 '\0\0\0\2' # represents revlogv2
55 '\0\0\0\2' # represents revlogv2
56 ' dummy changelog to prevent using the old repo layout'
56 ' dummy changelog to prevent using the old repo layout'
57 )
57 )
58 else:
58 else:
59 raise repo.RepoError(_("repository %s not found") % path)
59 raise repo.RepoError(_("repository %s not found") % path)
60 elif create:
60 elif create:
61 raise repo.RepoError(_("repository %s already exists") % path)
61 raise repo.RepoError(_("repository %s already exists") % path)
62 else:
62 else:
63 # find requirements
63 # find requirements
64 try:
64 try:
65 requirements = self.opener("requires").read().splitlines()
65 requirements = self.opener("requires").read().splitlines()
66 except IOError, inst:
66 except IOError, inst:
67 if inst.errno != errno.ENOENT:
67 if inst.errno != errno.ENOENT:
68 raise
68 raise
69 requirements = []
69 requirements = []
70 # check them
70 # check them
71 for r in requirements:
71 for r in requirements:
72 if r not in self.supported:
72 if r not in self.supported:
73 raise repo.RepoError(_("requirement '%s' not supported") % r)
73 raise repo.RepoError(_("requirement '%s' not supported") % r)
74
74
75 # setup store
75 # setup store
76 if "store" in requirements:
76 if "store" in requirements:
77 self.encodefn = util.encodefilename
77 self.encodefn = util.encodefilename
78 self.decodefn = util.decodefilename
78 self.decodefn = util.decodefilename
79 self.spath = os.path.join(self.path, "store")
79 self.spath = os.path.join(self.path, "store")
80 else:
80 else:
81 self.encodefn = lambda x: x
81 self.encodefn = lambda x: x
82 self.decodefn = lambda x: x
82 self.decodefn = lambda x: x
83 self.spath = self.path
83 self.spath = self.path
84 self.sopener = util.encodedopener(util.opener(self.spath), self.encodefn)
84 self.sopener = util.encodedopener(util.opener(self.spath), self.encodefn)
85
85
86 self.ui = ui.ui(parentui=parentui)
86 self.ui = ui.ui(parentui=parentui)
87 try:
87 try:
88 self.ui.readconfig(self.join("hgrc"), self.root)
88 self.ui.readconfig(self.join("hgrc"), self.root)
89 except IOError:
89 except IOError:
90 pass
90 pass
91
91
92 v = self.ui.configrevlog()
92 v = self.ui.configrevlog()
93 self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
93 self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
94 self.revlogv1 = self.revlogversion != revlog.REVLOGV0
94 self.revlogv1 = self.revlogversion != revlog.REVLOGV0
95 fl = v.get('flags', None)
95 fl = v.get('flags', None)
96 flags = 0
96 flags = 0
97 if fl != None:
97 if fl != None:
98 for x in fl.split():
98 for x in fl.split():
99 flags |= revlog.flagstr(x)
99 flags |= revlog.flagstr(x)
100 elif self.revlogv1:
100 elif self.revlogv1:
101 flags = revlog.REVLOG_DEFAULT_FLAGS
101 flags = revlog.REVLOG_DEFAULT_FLAGS
102
102
103 v = self.revlogversion | flags
103 v = self.revlogversion | flags
104 self.manifest = manifest.manifest(self.sopener, v)
104 self.manifest = manifest.manifest(self.sopener, v)
105 self.changelog = changelog.changelog(self.sopener, v)
105 self.changelog = changelog.changelog(self.sopener, v)
106
106
107 fallback = self.ui.config('ui', 'fallbackencoding')
107 fallback = self.ui.config('ui', 'fallbackencoding')
108 if fallback:
108 if fallback:
109 util._fallbackencoding = fallback
109 util._fallbackencoding = fallback
110
110
111 # the changelog might not have the inline index flag
111 # the changelog might not have the inline index flag
112 # on. If the format of the changelog is the same as found in
112 # on. If the format of the changelog is the same as found in
113 # .hgrc, apply any flags found in the .hgrc as well.
113 # .hgrc, apply any flags found in the .hgrc as well.
114 # Otherwise, just version from the changelog
114 # Otherwise, just version from the changelog
115 v = self.changelog.version
115 v = self.changelog.version
116 if v == self.revlogversion:
116 if v == self.revlogversion:
117 v |= flags
117 v |= flags
118 self.revlogversion = v
118 self.revlogversion = v
119
119
120 self.tagscache = None
120 self.tagscache = None
121 self.branchcache = None
121 self.branchcache = None
122 self.nodetagscache = None
122 self.nodetagscache = None
123 self.encodepats = None
123 self.encodepats = None
124 self.decodepats = None
124 self.decodepats = None
125 self.transhandle = None
125 self.transhandle = None
126
126
127 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
127 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
128
128
129 def url(self):
129 def url(self):
130 return 'file:' + self.root
130 return 'file:' + self.root
131
131
132 def hook(self, name, throw=False, **args):
132 def hook(self, name, throw=False, **args):
133 def callhook(hname, funcname):
133 def callhook(hname, funcname):
134 '''call python hook. hook is callable object, looked up as
134 '''call python hook. hook is callable object, looked up as
135 name in python module. if callable returns "true", hook
135 name in python module. if callable returns "true", hook
136 fails, else passes. if hook raises exception, treated as
136 fails, else passes. if hook raises exception, treated as
137 hook failure. exception propagates if throw is "true".
137 hook failure. exception propagates if throw is "true".
138
138
139 reason for "true" meaning "hook failed" is so that
139 reason for "true" meaning "hook failed" is so that
140 unmodified commands (e.g. mercurial.commands.update) can
140 unmodified commands (e.g. mercurial.commands.update) can
141 be run as hooks without wrappers to convert return values.'''
141 be run as hooks without wrappers to convert return values.'''
142
142
143 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
143 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
144 d = funcname.rfind('.')
144 d = funcname.rfind('.')
145 if d == -1:
145 if d == -1:
146 raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
146 raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
147 % (hname, funcname))
147 % (hname, funcname))
148 modname = funcname[:d]
148 modname = funcname[:d]
149 try:
149 try:
150 obj = __import__(modname)
150 obj = __import__(modname)
151 except ImportError:
151 except ImportError:
152 try:
152 try:
153 # extensions are loaded with hgext_ prefix
153 # extensions are loaded with hgext_ prefix
154 obj = __import__("hgext_%s" % modname)
154 obj = __import__("hgext_%s" % modname)
155 except ImportError:
155 except ImportError:
156 raise util.Abort(_('%s hook is invalid '
156 raise util.Abort(_('%s hook is invalid '
157 '(import of "%s" failed)') %
157 '(import of "%s" failed)') %
158 (hname, modname))
158 (hname, modname))
159 try:
159 try:
160 for p in funcname.split('.')[1:]:
160 for p in funcname.split('.')[1:]:
161 obj = getattr(obj, p)
161 obj = getattr(obj, p)
162 except AttributeError, err:
162 except AttributeError, err:
163 raise util.Abort(_('%s hook is invalid '
163 raise util.Abort(_('%s hook is invalid '
164 '("%s" is not defined)') %
164 '("%s" is not defined)') %
165 (hname, funcname))
165 (hname, funcname))
166 if not callable(obj):
166 if not callable(obj):
167 raise util.Abort(_('%s hook is invalid '
167 raise util.Abort(_('%s hook is invalid '
168 '("%s" is not callable)') %
168 '("%s" is not callable)') %
169 (hname, funcname))
169 (hname, funcname))
170 try:
170 try:
171 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
171 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
172 except (KeyboardInterrupt, util.SignalInterrupt):
172 except (KeyboardInterrupt, util.SignalInterrupt):
173 raise
173 raise
174 except Exception, exc:
174 except Exception, exc:
175 if isinstance(exc, util.Abort):
175 if isinstance(exc, util.Abort):
176 self.ui.warn(_('error: %s hook failed: %s\n') %
176 self.ui.warn(_('error: %s hook failed: %s\n') %
177 (hname, exc.args[0]))
177 (hname, exc.args[0]))
178 else:
178 else:
179 self.ui.warn(_('error: %s hook raised an exception: '
179 self.ui.warn(_('error: %s hook raised an exception: '
180 '%s\n') % (hname, exc))
180 '%s\n') % (hname, exc))
181 if throw:
181 if throw:
182 raise
182 raise
183 self.ui.print_exc()
183 self.ui.print_exc()
184 return True
184 return True
185 if r:
185 if r:
186 if throw:
186 if throw:
187 raise util.Abort(_('%s hook failed') % hname)
187 raise util.Abort(_('%s hook failed') % hname)
188 self.ui.warn(_('warning: %s hook failed\n') % hname)
188 self.ui.warn(_('warning: %s hook failed\n') % hname)
189 return r
189 return r
190
190
191 def runhook(name, cmd):
191 def runhook(name, cmd):
192 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
192 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
193 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
193 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
194 r = util.system(cmd, environ=env, cwd=self.root)
194 r = util.system(cmd, environ=env, cwd=self.root)
195 if r:
195 if r:
196 desc, r = util.explain_exit(r)
196 desc, r = util.explain_exit(r)
197 if throw:
197 if throw:
198 raise util.Abort(_('%s hook %s') % (name, desc))
198 raise util.Abort(_('%s hook %s') % (name, desc))
199 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
199 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
200 return r
200 return r
201
201
202 r = False
202 r = False
203 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
203 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
204 if hname.split(".", 1)[0] == name and cmd]
204 if hname.split(".", 1)[0] == name and cmd]
205 hooks.sort()
205 hooks.sort()
206 for hname, cmd in hooks:
206 for hname, cmd in hooks:
207 if cmd.startswith('python:'):
207 if cmd.startswith('python:'):
208 r = callhook(hname, cmd[7:].strip()) or r
208 r = callhook(hname, cmd[7:].strip()) or r
209 else:
209 else:
210 r = runhook(hname, cmd) or r
210 r = runhook(hname, cmd) or r
211 return r
211 return r
212
212
213 tag_disallowed = ':\r\n'
213 tag_disallowed = ':\r\n'
214
214
215 def tag(self, name, node, message, local, user, date):
215 def tag(self, name, node, message, local, user, date):
216 '''tag a revision with a symbolic name.
216 '''tag a revision with a symbolic name.
217
217
218 if local is True, the tag is stored in a per-repository file.
218 if local is True, the tag is stored in a per-repository file.
219 otherwise, it is stored in the .hgtags file, and a new
219 otherwise, it is stored in the .hgtags file, and a new
220 changeset is committed with the change.
220 changeset is committed with the change.
221
221
222 keyword arguments:
222 keyword arguments:
223
223
224 local: whether to store tag in non-version-controlled file
224 local: whether to store tag in non-version-controlled file
225 (default False)
225 (default False)
226
226
227 message: commit message to use if committing
227 message: commit message to use if committing
228
228
229 user: name of user to use if committing
229 user: name of user to use if committing
230
230
231 date: date tuple to use if committing'''
231 date: date tuple to use if committing'''
232
232
233 for c in self.tag_disallowed:
233 for c in self.tag_disallowed:
234 if c in name:
234 if c in name:
235 raise util.Abort(_('%r cannot be used in a tag name') % c)
235 raise util.Abort(_('%r cannot be used in a tag name') % c)
236
236
237 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
237 self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
238
238
239 if local:
239 if local:
240 # local tags are stored in the current charset
240 # local tags are stored in the current charset
241 self.opener('localtags', 'a').write('%s %s\n' % (hex(node), name))
241 self.opener('localtags', 'a').write('%s %s\n' % (hex(node), name))
242 self.hook('tag', node=hex(node), tag=name, local=local)
242 self.hook('tag', node=hex(node), tag=name, local=local)
243 return
243 return
244
244
245 for x in self.status()[:5]:
245 for x in self.status()[:5]:
246 if '.hgtags' in x:
246 if '.hgtags' in x:
247 raise util.Abort(_('working copy of .hgtags is changed '
247 raise util.Abort(_('working copy of .hgtags is changed '
248 '(please commit .hgtags manually)'))
248 '(please commit .hgtags manually)'))
249
249
250 # committed tags are stored in UTF-8
250 # committed tags are stored in UTF-8
251 line = '%s %s\n' % (hex(node), util.fromlocal(name))
251 line = '%s %s\n' % (hex(node), util.fromlocal(name))
252 self.wfile('.hgtags', 'ab').write(line)
252 self.wfile('.hgtags', 'ab').write(line)
253 if self.dirstate.state('.hgtags') == '?':
253 if self.dirstate.state('.hgtags') == '?':
254 self.add(['.hgtags'])
254 self.add(['.hgtags'])
255
255
256 self.commit(['.hgtags'], message, user, date)
256 self.commit(['.hgtags'], message, user, date)
257 self.hook('tag', node=hex(node), tag=name, local=local)
257 self.hook('tag', node=hex(node), tag=name, local=local)
258
258
259 def tags(self):
259 def tags(self):
260 '''return a mapping of tag to node'''
260 '''return a mapping of tag to node'''
261 if not self.tagscache:
261 if not self.tagscache:
262 self.tagscache = {}
262 self.tagscache = {}
263
263
264 def parsetag(line, context):
264 def parsetag(line, context):
265 if not line:
265 if not line:
266 return
266 return
267 s = l.split(" ", 1)
267 s = l.split(" ", 1)
268 if len(s) != 2:
268 if len(s) != 2:
269 self.ui.warn(_("%s: cannot parse entry\n") % context)
269 self.ui.warn(_("%s: cannot parse entry\n") % context)
270 return
270 return
271 node, key = s
271 node, key = s
272 key = util.tolocal(key.strip()) # stored in UTF-8
272 key = util.tolocal(key.strip()) # stored in UTF-8
273 try:
273 try:
274 bin_n = bin(node)
274 bin_n = bin(node)
275 except TypeError:
275 except TypeError:
276 self.ui.warn(_("%s: node '%s' is not well formed\n") %
276 self.ui.warn(_("%s: node '%s' is not well formed\n") %
277 (context, node))
277 (context, node))
278 return
278 return
279 if bin_n not in self.changelog.nodemap:
279 if bin_n not in self.changelog.nodemap:
280 self.ui.warn(_("%s: tag '%s' refers to unknown node\n") %
280 self.ui.warn(_("%s: tag '%s' refers to unknown node\n") %
281 (context, key))
281 (context, key))
282 return
282 return
283 self.tagscache[key] = bin_n
283 self.tagscache[key] = bin_n
284
284
285 # read the tags file from each head, ending with the tip,
285 # read the tags file from each head, ending with the tip,
286 # and add each tag found to the map, with "newer" ones
286 # and add each tag found to the map, with "newer" ones
287 # taking precedence
287 # taking precedence
288 f = None
288 f = None
289 for rev, node, fnode in self._hgtagsnodes():
289 for rev, node, fnode in self._hgtagsnodes():
290 f = (f and f.filectx(fnode) or
290 f = (f and f.filectx(fnode) or
291 self.filectx('.hgtags', fileid=fnode))
291 self.filectx('.hgtags', fileid=fnode))
292 count = 0
292 count = 0
293 for l in f.data().splitlines():
293 for l in f.data().splitlines():
294 count += 1
294 count += 1
295 parsetag(l, _("%s, line %d") % (str(f), count))
295 parsetag(l, _("%s, line %d") % (str(f), count))
296
296
297 try:
297 try:
298 f = self.opener("localtags")
298 f = self.opener("localtags")
299 count = 0
299 count = 0
300 for l in f:
300 for l in f:
301 # localtags are stored in the local character set
301 # localtags are stored in the local character set
302 # while the internal tag table is stored in UTF-8
302 # while the internal tag table is stored in UTF-8
303 l = util.fromlocal(l)
303 l = util.fromlocal(l)
304 count += 1
304 count += 1
305 parsetag(l, _("localtags, line %d") % count)
305 parsetag(l, _("localtags, line %d") % count)
306 except IOError:
306 except IOError:
307 pass
307 pass
308
308
309 self.tagscache['tip'] = self.changelog.tip()
309 self.tagscache['tip'] = self.changelog.tip()
310
310
311 return self.tagscache
311 return self.tagscache
312
312
313 def _hgtagsnodes(self):
313 def _hgtagsnodes(self):
314 heads = self.heads()
314 heads = self.heads()
315 heads.reverse()
315 heads.reverse()
316 last = {}
316 last = {}
317 ret = []
317 ret = []
318 for node in heads:
318 for node in heads:
319 c = self.changectx(node)
319 c = self.changectx(node)
320 rev = c.rev()
320 rev = c.rev()
321 try:
321 try:
322 fnode = c.filenode('.hgtags')
322 fnode = c.filenode('.hgtags')
323 except repo.LookupError:
323 except repo.LookupError:
324 continue
324 continue
325 ret.append((rev, node, fnode))
325 ret.append((rev, node, fnode))
326 if fnode in last:
326 if fnode in last:
327 ret[last[fnode]] = None
327 ret[last[fnode]] = None
328 last[fnode] = len(ret) - 1
328 last[fnode] = len(ret) - 1
329 return [item for item in ret if item]
329 return [item for item in ret if item]
330
330
331 def tagslist(self):
331 def tagslist(self):
332 '''return a list of tags ordered by revision'''
332 '''return a list of tags ordered by revision'''
333 l = []
333 l = []
334 for t, n in self.tags().items():
334 for t, n in self.tags().items():
335 try:
335 try:
336 r = self.changelog.rev(n)
336 r = self.changelog.rev(n)
337 except:
337 except:
338 r = -2 # sort to the beginning of the list if unknown
338 r = -2 # sort to the beginning of the list if unknown
339 l.append((r, t, n))
339 l.append((r, t, n))
340 l.sort()
340 l.sort()
341 return [(t, n) for r, t, n in l]
341 return [(t, n) for r, t, n in l]
342
342
343 def nodetags(self, node):
343 def nodetags(self, node):
344 '''return the tags associated with a node'''
344 '''return the tags associated with a node'''
345 if not self.nodetagscache:
345 if not self.nodetagscache:
346 self.nodetagscache = {}
346 self.nodetagscache = {}
347 for t, n in self.tags().items():
347 for t, n in self.tags().items():
348 self.nodetagscache.setdefault(n, []).append(t)
348 self.nodetagscache.setdefault(n, []).append(t)
349 return self.nodetagscache.get(node, [])
349 return self.nodetagscache.get(node, [])
350
350
351 def _branchtags(self):
351 def _branchtags(self):
352 partial, last, lrev = self._readbranchcache()
352 partial, last, lrev = self._readbranchcache()
353
353
354 tiprev = self.changelog.count() - 1
354 tiprev = self.changelog.count() - 1
355 if lrev != tiprev:
355 if lrev != tiprev:
356 self._updatebranchcache(partial, lrev+1, tiprev+1)
356 self._updatebranchcache(partial, lrev+1, tiprev+1)
357 self._writebranchcache(partial, self.changelog.tip(), tiprev)
357 self._writebranchcache(partial, self.changelog.tip(), tiprev)
358
358
359 return partial
359 return partial
360
360
361 def branchtags(self):
361 def branchtags(self):
362 if self.branchcache is not None:
362 if self.branchcache is not None:
363 return self.branchcache
363 return self.branchcache
364
364
365 self.branchcache = {} # avoid recursion in changectx
365 self.branchcache = {} # avoid recursion in changectx
366 partial = self._branchtags()
366 partial = self._branchtags()
367
367
368 # the branch cache is stored on disk as UTF-8, but in the local
368 # the branch cache is stored on disk as UTF-8, but in the local
369 # charset internally
369 # charset internally
370 for k, v in partial.items():
370 for k, v in partial.items():
371 self.branchcache[util.tolocal(k)] = v
371 self.branchcache[util.tolocal(k)] = v
372 return self.branchcache
372 return self.branchcache
373
373
374 def _readbranchcache(self):
374 def _readbranchcache(self):
375 partial = {}
375 partial = {}
376 try:
376 try:
377 f = self.opener("branches.cache")
377 f = self.opener("branches.cache")
378 lines = f.read().split('\n')
378 lines = f.read().split('\n')
379 f.close()
379 f.close()
380 features = lines.pop(0).strip()
380 features = lines.pop(0).strip()
381 if not features.startswith('features: '):
381 if not features.startswith('features: '):
382 raise ValueError(_('branch cache: no features specified'))
382 raise ValueError(_('branch cache: no features specified'))
383 features = features.split(' ', 1)[1].split()
383 features = features.split(' ', 1)[1].split()
384 missing_features = []
384 missing_features = []
385 for feature in self.branchcache_features:
385 for feature in self.branchcache_features:
386 try:
386 try:
387 features.remove(feature)
387 features.remove(feature)
388 except ValueError, inst:
388 except ValueError, inst:
389 missing_features.append(feature)
389 missing_features.append(feature)
390 if missing_features:
390 if missing_features:
391 raise ValueError(_('branch cache: missing features: %s')
391 raise ValueError(_('branch cache: missing features: %s')
392 % ', '.join(missing_features))
392 % ', '.join(missing_features))
393 if features:
393 if features:
394 raise ValueError(_('branch cache: unknown features: %s')
394 raise ValueError(_('branch cache: unknown features: %s')
395 % ', '.join(features))
395 % ', '.join(features))
396 last, lrev = lines.pop(0).split(" ", 1)
396 last, lrev = lines.pop(0).split(" ", 1)
397 last, lrev = bin(last), int(lrev)
397 last, lrev = bin(last), int(lrev)
398 if not (lrev < self.changelog.count() and
398 if not (lrev < self.changelog.count() and
399 self.changelog.node(lrev) == last): # sanity check
399 self.changelog.node(lrev) == last): # sanity check
400 # invalidate the cache
400 # invalidate the cache
401 raise ValueError('Invalid branch cache: unknown tip')
401 raise ValueError('Invalid branch cache: unknown tip')
402 for l in lines:
402 for l in lines:
403 if not l: continue
403 if not l: continue
404 node, label = l.split(" ", 1)
404 node, label = l.split(" ", 1)
405 partial[label.strip()] = bin(node)
405 partial[label.strip()] = bin(node)
406 except (KeyboardInterrupt, util.SignalInterrupt):
406 except (KeyboardInterrupt, util.SignalInterrupt):
407 raise
407 raise
408 except Exception, inst:
408 except Exception, inst:
409 if self.ui.debugflag:
409 if self.ui.debugflag:
410 self.ui.warn(str(inst), '\n')
410 self.ui.warn(str(inst), '\n')
411 partial, last, lrev = {}, nullid, nullrev
411 partial, last, lrev = {}, nullid, nullrev
412 return partial, last, lrev
412 return partial, last, lrev
413
413
414 def _writebranchcache(self, branches, tip, tiprev):
414 def _writebranchcache(self, branches, tip, tiprev):
415 try:
415 try:
416 f = self.opener("branches.cache", "w")
416 f = self.opener("branches.cache", "w")
417 f.write(" features: %s\n" % ' '.join(self.branchcache_features))
417 f.write(" features: %s\n" % ' '.join(self.branchcache_features))
418 f.write("%s %s\n" % (hex(tip), tiprev))
418 f.write("%s %s\n" % (hex(tip), tiprev))
419 for label, node in branches.iteritems():
419 for label, node in branches.iteritems():
420 f.write("%s %s\n" % (hex(node), label))
420 f.write("%s %s\n" % (hex(node), label))
421 except IOError:
421 except IOError:
422 pass
422 pass
423
423
424 def _updatebranchcache(self, partial, start, end):
424 def _updatebranchcache(self, partial, start, end):
425 for r in xrange(start, end):
425 for r in xrange(start, end):
426 c = self.changectx(r)
426 c = self.changectx(r)
427 b = c.branch()
427 b = c.branch()
428 partial[b] = c.node()
428 partial[b] = c.node()
429
429
430 def lookup(self, key):
430 def lookup(self, key):
431 if key == '.':
431 if key == '.':
432 key = self.dirstate.parents()[0]
432 key = self.dirstate.parents()[0]
433 if key == nullid:
433 if key == nullid:
434 raise repo.RepoError(_("no revision checked out"))
434 raise repo.RepoError(_("no revision checked out"))
435 elif key == 'null':
435 elif key == 'null':
436 return nullid
436 return nullid
437 n = self.changelog._match(key)
437 n = self.changelog._match(key)
438 if n:
438 if n:
439 return n
439 return n
440 if key in self.tags():
440 if key in self.tags():
441 return self.tags()[key]
441 return self.tags()[key]
442 if key in self.branchtags():
442 if key in self.branchtags():
443 return self.branchtags()[key]
443 return self.branchtags()[key]
444 n = self.changelog._partialmatch(key)
444 n = self.changelog._partialmatch(key)
445 if n:
445 if n:
446 return n
446 return n
447 raise repo.RepoError(_("unknown revision '%s'") % key)
447 raise repo.RepoError(_("unknown revision '%s'") % key)
448
448
449 def dev(self):
449 def dev(self):
450 return os.lstat(self.path).st_dev
450 return os.lstat(self.path).st_dev
451
451
452 def local(self):
452 def local(self):
453 return True
453 return True
454
454
455 def join(self, f):
455 def join(self, f):
456 return os.path.join(self.path, f)
456 return os.path.join(self.path, f)
457
457
458 def sjoin(self, f):
458 def sjoin(self, f):
459 f = self.encodefn(f)
459 f = self.encodefn(f)
460 return os.path.join(self.spath, f)
460 return os.path.join(self.spath, f)
461
461
462 def wjoin(self, f):
462 def wjoin(self, f):
463 return os.path.join(self.root, f)
463 return os.path.join(self.root, f)
464
464
465 def file(self, f):
465 def file(self, f):
466 if f[0] == '/':
466 if f[0] == '/':
467 f = f[1:]
467 f = f[1:]
468 return filelog.filelog(self.sopener, f, self.revlogversion)
468 return filelog.filelog(self.sopener, f, self.revlogversion)
469
469
470 def changectx(self, changeid=None):
470 def changectx(self, changeid=None):
471 return context.changectx(self, changeid)
471 return context.changectx(self, changeid)
472
472
473 def workingctx(self):
473 def workingctx(self):
474 return context.workingctx(self)
474 return context.workingctx(self)
475
475
476 def parents(self, changeid=None):
476 def parents(self, changeid=None):
477 '''
477 '''
478 get list of changectxs for parents of changeid or working directory
478 get list of changectxs for parents of changeid or working directory
479 '''
479 '''
480 if changeid is None:
480 if changeid is None:
481 pl = self.dirstate.parents()
481 pl = self.dirstate.parents()
482 else:
482 else:
483 n = self.changelog.lookup(changeid)
483 n = self.changelog.lookup(changeid)
484 pl = self.changelog.parents(n)
484 pl = self.changelog.parents(n)
485 if pl[1] == nullid:
485 if pl[1] == nullid:
486 return [self.changectx(pl[0])]
486 return [self.changectx(pl[0])]
487 return [self.changectx(pl[0]), self.changectx(pl[1])]
487 return [self.changectx(pl[0]), self.changectx(pl[1])]
488
488
489 def filectx(self, path, changeid=None, fileid=None):
489 def filectx(self, path, changeid=None, fileid=None):
490 """changeid can be a changeset revision, node, or tag.
490 """changeid can be a changeset revision, node, or tag.
491 fileid can be a file revision or node."""
491 fileid can be a file revision or node."""
492 return context.filectx(self, path, changeid, fileid)
492 return context.filectx(self, path, changeid, fileid)
493
493
494 def getcwd(self):
494 def getcwd(self):
495 return self.dirstate.getcwd()
495 return self.dirstate.getcwd()
496
496
497 def wfile(self, f, mode='r'):
497 def wfile(self, f, mode='r'):
498 return self.wopener(f, mode)
498 return self.wopener(f, mode)
499
499
500 def wread(self, filename):
500 def wread(self, filename):
501 if self.encodepats == None:
501 if self.encodepats == None:
502 l = []
502 l = []
503 for pat, cmd in self.ui.configitems("encode"):
503 for pat, cmd in self.ui.configitems("encode"):
504 mf = util.matcher(self.root, "", [pat], [], [])[1]
504 mf = util.matcher(self.root, "", [pat], [], [])[1]
505 l.append((mf, cmd))
505 l.append((mf, cmd))
506 self.encodepats = l
506 self.encodepats = l
507
507
508 data = self.wopener(filename, 'r').read()
508 data = self.wopener(filename, 'r').read()
509
509
510 for mf, cmd in self.encodepats:
510 for mf, cmd in self.encodepats:
511 if mf(filename):
511 if mf(filename):
512 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
512 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
513 data = util.filter(data, cmd)
513 data = util.filter(data, cmd)
514 break
514 break
515
515
516 return data
516 return data
517
517
518 def wwrite(self, filename, data, fd=None):
518 def wwrite(self, filename, data, fd=None):
519 if self.decodepats == None:
519 if self.decodepats == None:
520 l = []
520 l = []
521 for pat, cmd in self.ui.configitems("decode"):
521 for pat, cmd in self.ui.configitems("decode"):
522 mf = util.matcher(self.root, "", [pat], [], [])[1]
522 mf = util.matcher(self.root, "", [pat], [], [])[1]
523 l.append((mf, cmd))
523 l.append((mf, cmd))
524 self.decodepats = l
524 self.decodepats = l
525
525
526 for mf, cmd in self.decodepats:
526 for mf, cmd in self.decodepats:
527 if mf(filename):
527 if mf(filename):
528 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
528 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
529 data = util.filter(data, cmd)
529 data = util.filter(data, cmd)
530 break
530 break
531
531
532 if fd:
532 if fd:
533 return fd.write(data)
533 return fd.write(data)
534 return self.wopener(filename, 'w').write(data)
534 return self.wopener(filename, 'w').write(data)
535
535
536 def transaction(self):
536 def transaction(self):
537 tr = self.transhandle
537 tr = self.transhandle
538 if tr != None and tr.running():
538 if tr != None and tr.running():
539 return tr.nest()
539 return tr.nest()
540
540
541 # save dirstate for rollback
541 # save dirstate for rollback
542 try:
542 try:
543 ds = self.opener("dirstate").read()
543 ds = self.opener("dirstate").read()
544 except IOError:
544 except IOError:
545 ds = ""
545 ds = ""
546 self.opener("journal.dirstate", "w").write(ds)
546 self.opener("journal.dirstate", "w").write(ds)
547
547
548 renames = [(self.sjoin("journal"), self.sjoin("undo")),
548 renames = [(self.sjoin("journal"), self.sjoin("undo")),
549 (self.join("journal.dirstate"), self.join("undo.dirstate"))]
549 (self.join("journal.dirstate"), self.join("undo.dirstate"))]
550 tr = transaction.transaction(self.ui.warn, self.sopener,
550 tr = transaction.transaction(self.ui.warn, self.sopener,
551 self.sjoin("journal"),
551 self.sjoin("journal"),
552 aftertrans(renames))
552 aftertrans(renames))
553 self.transhandle = tr
553 self.transhandle = tr
554 return tr
554 return tr
555
555
556 def recover(self):
556 def recover(self):
557 l = self.lock()
557 l = self.lock()
558 if os.path.exists(self.sjoin("journal")):
558 if os.path.exists(self.sjoin("journal")):
559 self.ui.status(_("rolling back interrupted transaction\n"))
559 self.ui.status(_("rolling back interrupted transaction\n"))
560 transaction.rollback(self.sopener, self.sjoin("journal"))
560 transaction.rollback(self.sopener, self.sjoin("journal"))
561 self.reload()
561 self.reload()
562 return True
562 return True
563 else:
563 else:
564 self.ui.warn(_("no interrupted transaction available\n"))
564 self.ui.warn(_("no interrupted transaction available\n"))
565 return False
565 return False
566
566
567 def rollback(self, wlock=None):
567 def rollback(self, wlock=None):
568 if not wlock:
568 if not wlock:
569 wlock = self.wlock()
569 wlock = self.wlock()
570 l = self.lock()
570 l = self.lock()
571 if os.path.exists(self.sjoin("undo")):
571 if os.path.exists(self.sjoin("undo")):
572 self.ui.status(_("rolling back last transaction\n"))
572 self.ui.status(_("rolling back last transaction\n"))
573 transaction.rollback(self.sopener, self.sjoin("undo"))
573 transaction.rollback(self.sopener, self.sjoin("undo"))
574 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
574 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
575 self.reload()
575 self.reload()
576 self.wreload()
576 self.wreload()
577 else:
577 else:
578 self.ui.warn(_("no rollback information available\n"))
578 self.ui.warn(_("no rollback information available\n"))
579
579
580 def wreload(self):
580 def wreload(self):
581 self.dirstate.read()
581 self.dirstate.read()
582
582
583 def reload(self):
583 def reload(self):
584 self.changelog.load()
584 self.changelog.load()
585 self.manifest.load()
585 self.manifest.load()
586 self.tagscache = None
586 self.tagscache = None
587 self.nodetagscache = None
587 self.nodetagscache = None
588
588
589 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
589 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
590 desc=None):
590 desc=None):
591 try:
591 try:
592 l = lock.lock(lockname, 0, releasefn, desc=desc)
592 l = lock.lock(lockname, 0, releasefn, desc=desc)
593 except lock.LockHeld, inst:
593 except lock.LockHeld, inst:
594 if not wait:
594 if not wait:
595 raise
595 raise
596 self.ui.warn(_("waiting for lock on %s held by %r\n") %
596 self.ui.warn(_("waiting for lock on %s held by %r\n") %
597 (desc, inst.locker))
597 (desc, inst.locker))
598 # default to 600 seconds timeout
598 # default to 600 seconds timeout
599 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
599 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
600 releasefn, desc=desc)
600 releasefn, desc=desc)
601 if acquirefn:
601 if acquirefn:
602 acquirefn()
602 acquirefn()
603 return l
603 return l
604
604
605 def lock(self, wait=1):
605 def lock(self, wait=1):
606 return self.do_lock(self.sjoin("lock"), wait, acquirefn=self.reload,
606 return self.do_lock(self.sjoin("lock"), wait, acquirefn=self.reload,
607 desc=_('repository %s') % self.origroot)
607 desc=_('repository %s') % self.origroot)
608
608
609 def wlock(self, wait=1):
609 def wlock(self, wait=1):
610 return self.do_lock(self.join("wlock"), wait, self.dirstate.write,
610 return self.do_lock(self.join("wlock"), wait, self.dirstate.write,
611 self.wreload,
611 self.wreload,
612 desc=_('working directory of %s') % self.origroot)
612 desc=_('working directory of %s') % self.origroot)
613
613
614 def filecommit(self, fn, manifest1, manifest2, linkrev, transaction, changelist):
614 def filecommit(self, fn, manifest1, manifest2, linkrev, transaction, changelist):
615 """
615 """
616 commit an individual file as part of a larger transaction
616 commit an individual file as part of a larger transaction
617 """
617 """
618
618
619 t = self.wread(fn)
619 t = self.wread(fn)
620 fl = self.file(fn)
620 fl = self.file(fn)
621 fp1 = manifest1.get(fn, nullid)
621 fp1 = manifest1.get(fn, nullid)
622 fp2 = manifest2.get(fn, nullid)
622 fp2 = manifest2.get(fn, nullid)
623
623
624 meta = {}
624 meta = {}
625 cp = self.dirstate.copied(fn)
625 cp = self.dirstate.copied(fn)
626 if cp:
626 if cp:
627 # Mark the new revision of this file as a copy of another
627 # Mark the new revision of this file as a copy of another
628 # file. This copy data will effectively act as a parent
628 # file. This copy data will effectively act as a parent
629 # of this new revision. If this is a merge, the first
629 # of this new revision. If this is a merge, the first
630 # parent will be the nullid (meaning "look up the copy data")
630 # parent will be the nullid (meaning "look up the copy data")
631 # and the second one will be the other parent. For example:
631 # and the second one will be the other parent. For example:
632 #
632 #
633 # 0 --- 1 --- 3 rev1 changes file foo
633 # 0 --- 1 --- 3 rev1 changes file foo
634 # \ / rev2 renames foo to bar and changes it
634 # \ / rev2 renames foo to bar and changes it
635 # \- 2 -/ rev3 should have bar with all changes and
635 # \- 2 -/ rev3 should have bar with all changes and
636 # should record that bar descends from
636 # should record that bar descends from
637 # bar in rev2 and foo in rev1
637 # bar in rev2 and foo in rev1
638 #
638 #
639 # this allows this merge to succeed:
639 # this allows this merge to succeed:
640 #
640 #
641 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
641 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
642 # \ / merging rev3 and rev4 should use bar@rev2
642 # \ / merging rev3 and rev4 should use bar@rev2
643 # \- 2 --- 4 as the merge base
643 # \- 2 --- 4 as the merge base
644 #
644 #
645 meta["copy"] = cp
645 meta["copy"] = cp
646 if not manifest2: # not a branch merge
646 if not manifest2: # not a branch merge
647 meta["copyrev"] = hex(manifest1.get(cp, nullid))
647 meta["copyrev"] = hex(manifest1.get(cp, nullid))
648 fp2 = nullid
648 fp2 = nullid
649 elif fp2 != nullid: # copied on remote side
649 elif fp2 != nullid: # copied on remote side
650 meta["copyrev"] = hex(manifest1.get(cp, nullid))
650 meta["copyrev"] = hex(manifest1.get(cp, nullid))
651 elif fp1 != nullid: # copied on local side, reversed
651 elif fp1 != nullid: # copied on local side, reversed
652 meta["copyrev"] = hex(manifest2.get(cp))
652 meta["copyrev"] = hex(manifest2.get(cp))
653 fp2 = fp1
653 fp2 = fp1
654 else: # directory rename
654 else: # directory rename
655 meta["copyrev"] = hex(manifest1.get(cp, nullid))
655 meta["copyrev"] = hex(manifest1.get(cp, nullid))
656 self.ui.debug(_(" %s: copy %s:%s\n") %
656 self.ui.debug(_(" %s: copy %s:%s\n") %
657 (fn, cp, meta["copyrev"]))
657 (fn, cp, meta["copyrev"]))
658 fp1 = nullid
658 fp1 = nullid
659 elif fp2 != nullid:
659 elif fp2 != nullid:
660 # is one parent an ancestor of the other?
660 # is one parent an ancestor of the other?
661 fpa = fl.ancestor(fp1, fp2)
661 fpa = fl.ancestor(fp1, fp2)
662 if fpa == fp1:
662 if fpa == fp1:
663 fp1, fp2 = fp2, nullid
663 fp1, fp2 = fp2, nullid
664 elif fpa == fp2:
664 elif fpa == fp2:
665 fp2 = nullid
665 fp2 = nullid
666
666
667 # is the file unmodified from the parent? report existing entry
667 # is the file unmodified from the parent? report existing entry
668 if fp2 == nullid and not fl.cmp(fp1, t):
668 if fp2 == nullid and not fl.cmp(fp1, t):
669 return fp1
669 return fp1
670
670
671 changelist.append(fn)
671 changelist.append(fn)
672 return fl.add(t, meta, transaction, linkrev, fp1, fp2)
672 return fl.add(t, meta, transaction, linkrev, fp1, fp2)
673
673
674 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
674 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
675 if p1 is None:
675 if p1 is None:
676 p1, p2 = self.dirstate.parents()
676 p1, p2 = self.dirstate.parents()
677 return self.commit(files=files, text=text, user=user, date=date,
677 return self.commit(files=files, text=text, user=user, date=date,
678 p1=p1, p2=p2, wlock=wlock)
678 p1=p1, p2=p2, wlock=wlock)
679
679
680 def commit(self, files=None, text="", user=None, date=None,
680 def commit(self, files=None, text="", user=None, date=None,
681 match=util.always, force=False, lock=None, wlock=None,
681 match=util.always, force=False, lock=None, wlock=None,
682 force_editor=False, p1=None, p2=None, extra={}):
682 force_editor=False, p1=None, p2=None, extra={}):
683
683
684 commit = []
684 commit = []
685 remove = []
685 remove = []
686 changed = []
686 changed = []
687 use_dirstate = (p1 is None) # not rawcommit
687 use_dirstate = (p1 is None) # not rawcommit
688 extra = extra.copy()
688 extra = extra.copy()
689
689
690 if use_dirstate:
690 if use_dirstate:
691 if files:
691 if files:
692 for f in files:
692 for f in files:
693 s = self.dirstate.state(f)
693 s = self.dirstate.state(f)
694 if s in 'nmai':
694 if s in 'nmai':
695 commit.append(f)
695 commit.append(f)
696 elif s == 'r':
696 elif s == 'r':
697 remove.append(f)
697 remove.append(f)
698 else:
698 else:
699 self.ui.warn(_("%s not tracked!\n") % f)
699 self.ui.warn(_("%s not tracked!\n") % f)
700 else:
700 else:
701 changes = self.status(match=match)[:5]
701 changes = self.status(match=match)[:5]
702 modified, added, removed, deleted, unknown = changes
702 modified, added, removed, deleted, unknown = changes
703 commit = modified + added
703 commit = modified + added
704 remove = removed
704 remove = removed
705 else:
705 else:
706 commit = files
706 commit = files
707
707
708 if use_dirstate:
708 if use_dirstate:
709 p1, p2 = self.dirstate.parents()
709 p1, p2 = self.dirstate.parents()
710 update_dirstate = True
710 update_dirstate = True
711 else:
711 else:
712 p1, p2 = p1, p2 or nullid
712 p1, p2 = p1, p2 or nullid
713 update_dirstate = (self.dirstate.parents()[0] == p1)
713 update_dirstate = (self.dirstate.parents()[0] == p1)
714
714
715 c1 = self.changelog.read(p1)
715 c1 = self.changelog.read(p1)
716 c2 = self.changelog.read(p2)
716 c2 = self.changelog.read(p2)
717 m1 = self.manifest.read(c1[0]).copy()
717 m1 = self.manifest.read(c1[0]).copy()
718 m2 = self.manifest.read(c2[0])
718 m2 = self.manifest.read(c2[0])
719
719
720 if use_dirstate:
720 if use_dirstate:
721 branchname = self.workingctx().branch()
721 branchname = self.workingctx().branch()
722 try:
722 try:
723 branchname = branchname.decode('UTF-8').encode('UTF-8')
723 branchname = branchname.decode('UTF-8').encode('UTF-8')
724 except UnicodeDecodeError:
724 except UnicodeDecodeError:
725 raise util.Abort(_('branch name not in UTF-8!'))
725 raise util.Abort(_('branch name not in UTF-8!'))
726 else:
726 else:
727 branchname = ""
727 branchname = ""
728
728
729 if use_dirstate:
729 if use_dirstate:
730 oldname = c1[5].get("branch", "") # stored in UTF-8
730 oldname = c1[5].get("branch") or "default" # stored in UTF-8
731 if not commit and not remove and not force and p2 == nullid and \
731 if not commit and not remove and not force and p2 == nullid and \
732 branchname == oldname:
732 branchname == oldname:
733 self.ui.status(_("nothing changed\n"))
733 self.ui.status(_("nothing changed\n"))
734 return None
734 return None
735
735
736 xp1 = hex(p1)
736 xp1 = hex(p1)
737 if p2 == nullid: xp2 = ''
737 if p2 == nullid: xp2 = ''
738 else: xp2 = hex(p2)
738 else: xp2 = hex(p2)
739
739
740 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
740 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
741
741
742 if not wlock:
742 if not wlock:
743 wlock = self.wlock()
743 wlock = self.wlock()
744 if not lock:
744 if not lock:
745 lock = self.lock()
745 lock = self.lock()
746 tr = self.transaction()
746 tr = self.transaction()
747
747
748 # check in files
748 # check in files
749 new = {}
749 new = {}
750 linkrev = self.changelog.count()
750 linkrev = self.changelog.count()
751 commit.sort()
751 commit.sort()
752 for f in commit:
752 for f in commit:
753 self.ui.note(f + "\n")
753 self.ui.note(f + "\n")
754 try:
754 try:
755 new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
755 new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
756 m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f)))
756 m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f)))
757 except IOError:
757 except IOError:
758 if use_dirstate:
758 if use_dirstate:
759 self.ui.warn(_("trouble committing %s!\n") % f)
759 self.ui.warn(_("trouble committing %s!\n") % f)
760 raise
760 raise
761 else:
761 else:
762 remove.append(f)
762 remove.append(f)
763
763
764 # update manifest
764 # update manifest
765 m1.update(new)
765 m1.update(new)
766 remove.sort()
766 remove.sort()
767
767
768 for f in remove:
768 for f in remove:
769 if f in m1:
769 if f in m1:
770 del m1[f]
770 del m1[f]
771 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, remove))
771 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, remove))
772
772
773 # add changeset
773 # add changeset
774 new = new.keys()
774 new = new.keys()
775 new.sort()
775 new.sort()
776
776
777 user = user or self.ui.username()
777 user = user or self.ui.username()
778 if not text or force_editor:
778 if not text or force_editor:
779 edittext = []
779 edittext = []
780 if text:
780 if text:
781 edittext.append(text)
781 edittext.append(text)
782 edittext.append("")
782 edittext.append("")
783 edittext.append("HG: user: %s" % user)
783 edittext.append("HG: user: %s" % user)
784 if p2 != nullid:
784 if p2 != nullid:
785 edittext.append("HG: branch merge")
785 edittext.append("HG: branch merge")
786 edittext.extend(["HG: changed %s" % f for f in changed])
786 edittext.extend(["HG: changed %s" % f for f in changed])
787 edittext.extend(["HG: removed %s" % f for f in remove])
787 edittext.extend(["HG: removed %s" % f for f in remove])
788 if not changed and not remove:
788 if not changed and not remove:
789 edittext.append("HG: no files changed")
789 edittext.append("HG: no files changed")
790 edittext.append("")
790 edittext.append("")
791 # run editor in the repository root
791 # run editor in the repository root
792 olddir = os.getcwd()
792 olddir = os.getcwd()
793 os.chdir(self.root)
793 os.chdir(self.root)
794 text = self.ui.edit("\n".join(edittext), user)
794 text = self.ui.edit("\n".join(edittext), user)
795 os.chdir(olddir)
795 os.chdir(olddir)
796
796
797 lines = [line.rstrip() for line in text.rstrip().splitlines()]
797 lines = [line.rstrip() for line in text.rstrip().splitlines()]
798 while lines and not lines[0]:
798 while lines and not lines[0]:
799 del lines[0]
799 del lines[0]
800 if not lines:
800 if not lines:
801 return None
801 return None
802 text = '\n'.join(lines)
802 text = '\n'.join(lines)
803 if branchname:
803 if branchname:
804 extra["branch"] = branchname
804 extra["branch"] = branchname
805 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2,
805 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2,
806 user, date, extra)
806 user, date, extra)
807 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
807 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
808 parent2=xp2)
808 parent2=xp2)
809 tr.close()
809 tr.close()
810
810
811 if use_dirstate or update_dirstate:
811 if use_dirstate or update_dirstate:
812 self.dirstate.setparents(n)
812 self.dirstate.setparents(n)
813 if use_dirstate:
813 if use_dirstate:
814 self.dirstate.update(new, "n")
814 self.dirstate.update(new, "n")
815 self.dirstate.forget(remove)
815 self.dirstate.forget(remove)
816
816
817 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
817 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
818 return n
818 return n
819
819
820 def walk(self, node=None, files=[], match=util.always, badmatch=None):
820 def walk(self, node=None, files=[], match=util.always, badmatch=None):
821 '''
821 '''
822 walk recursively through the directory tree or a given
822 walk recursively through the directory tree or a given
823 changeset, finding all files matched by the match
823 changeset, finding all files matched by the match
824 function
824 function
825
825
826 results are yielded in a tuple (src, filename), where src
826 results are yielded in a tuple (src, filename), where src
827 is one of:
827 is one of:
828 'f' the file was found in the directory tree
828 'f' the file was found in the directory tree
829 'm' the file was only in the dirstate and not in the tree
829 'm' the file was only in the dirstate and not in the tree
830 'b' file was not found and matched badmatch
830 'b' file was not found and matched badmatch
831 '''
831 '''
832
832
833 if node:
833 if node:
834 fdict = dict.fromkeys(files)
834 fdict = dict.fromkeys(files)
835 for fn in self.manifest.read(self.changelog.read(node)[0]):
835 for fn in self.manifest.read(self.changelog.read(node)[0]):
836 for ffn in fdict:
836 for ffn in fdict:
837 # match if the file is the exact name or a directory
837 # match if the file is the exact name or a directory
838 if ffn == fn or fn.startswith("%s/" % ffn):
838 if ffn == fn or fn.startswith("%s/" % ffn):
839 del fdict[ffn]
839 del fdict[ffn]
840 break
840 break
841 if match(fn):
841 if match(fn):
842 yield 'm', fn
842 yield 'm', fn
843 for fn in fdict:
843 for fn in fdict:
844 if badmatch and badmatch(fn):
844 if badmatch and badmatch(fn):
845 if match(fn):
845 if match(fn):
846 yield 'b', fn
846 yield 'b', fn
847 else:
847 else:
848 self.ui.warn(_('%s: No such file in rev %s\n') % (
848 self.ui.warn(_('%s: No such file in rev %s\n') % (
849 util.pathto(self.getcwd(), fn), short(node)))
849 util.pathto(self.getcwd(), fn), short(node)))
850 else:
850 else:
851 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
851 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
852 yield src, fn
852 yield src, fn
853
853
854 def status(self, node1=None, node2=None, files=[], match=util.always,
854 def status(self, node1=None, node2=None, files=[], match=util.always,
855 wlock=None, list_ignored=False, list_clean=False):
855 wlock=None, list_ignored=False, list_clean=False):
856 """return status of files between two nodes or node and working directory
856 """return status of files between two nodes or node and working directory
857
857
858 If node1 is None, use the first dirstate parent instead.
858 If node1 is None, use the first dirstate parent instead.
859 If node2 is None, compare node1 with working directory.
859 If node2 is None, compare node1 with working directory.
860 """
860 """
861
861
862 def fcmp(fn, mf):
862 def fcmp(fn, mf):
863 t1 = self.wread(fn)
863 t1 = self.wread(fn)
864 return self.file(fn).cmp(mf.get(fn, nullid), t1)
864 return self.file(fn).cmp(mf.get(fn, nullid), t1)
865
865
866 def mfmatches(node):
866 def mfmatches(node):
867 change = self.changelog.read(node)
867 change = self.changelog.read(node)
868 mf = self.manifest.read(change[0]).copy()
868 mf = self.manifest.read(change[0]).copy()
869 for fn in mf.keys():
869 for fn in mf.keys():
870 if not match(fn):
870 if not match(fn):
871 del mf[fn]
871 del mf[fn]
872 return mf
872 return mf
873
873
874 modified, added, removed, deleted, unknown = [], [], [], [], []
874 modified, added, removed, deleted, unknown = [], [], [], [], []
875 ignored, clean = [], []
875 ignored, clean = [], []
876
876
877 compareworking = False
877 compareworking = False
878 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
878 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
879 compareworking = True
879 compareworking = True
880
880
881 if not compareworking:
881 if not compareworking:
882 # read the manifest from node1 before the manifest from node2,
882 # read the manifest from node1 before the manifest from node2,
883 # so that we'll hit the manifest cache if we're going through
883 # so that we'll hit the manifest cache if we're going through
884 # all the revisions in parent->child order.
884 # all the revisions in parent->child order.
885 mf1 = mfmatches(node1)
885 mf1 = mfmatches(node1)
886
886
887 # are we comparing the working directory?
887 # are we comparing the working directory?
888 if not node2:
888 if not node2:
889 if not wlock:
889 if not wlock:
890 try:
890 try:
891 wlock = self.wlock(wait=0)
891 wlock = self.wlock(wait=0)
892 except lock.LockException:
892 except lock.LockException:
893 wlock = None
893 wlock = None
894 (lookup, modified, added, removed, deleted, unknown,
894 (lookup, modified, added, removed, deleted, unknown,
895 ignored, clean) = self.dirstate.status(files, match,
895 ignored, clean) = self.dirstate.status(files, match,
896 list_ignored, list_clean)
896 list_ignored, list_clean)
897
897
898 # are we comparing working dir against its parent?
898 # are we comparing working dir against its parent?
899 if compareworking:
899 if compareworking:
900 if lookup:
900 if lookup:
901 # do a full compare of any files that might have changed
901 # do a full compare of any files that might have changed
902 mf2 = mfmatches(self.dirstate.parents()[0])
902 mf2 = mfmatches(self.dirstate.parents()[0])
903 for f in lookup:
903 for f in lookup:
904 if fcmp(f, mf2):
904 if fcmp(f, mf2):
905 modified.append(f)
905 modified.append(f)
906 else:
906 else:
907 clean.append(f)
907 clean.append(f)
908 if wlock is not None:
908 if wlock is not None:
909 self.dirstate.update([f], "n")
909 self.dirstate.update([f], "n")
910 else:
910 else:
911 # we are comparing working dir against non-parent
911 # we are comparing working dir against non-parent
912 # generate a pseudo-manifest for the working dir
912 # generate a pseudo-manifest for the working dir
913 # XXX: create it in dirstate.py ?
913 # XXX: create it in dirstate.py ?
914 mf2 = mfmatches(self.dirstate.parents()[0])
914 mf2 = mfmatches(self.dirstate.parents()[0])
915 for f in lookup + modified + added:
915 for f in lookup + modified + added:
916 mf2[f] = ""
916 mf2[f] = ""
917 mf2.set(f, execf=util.is_exec(self.wjoin(f), mf2.execf(f)))
917 mf2.set(f, execf=util.is_exec(self.wjoin(f), mf2.execf(f)))
918 for f in removed:
918 for f in removed:
919 if f in mf2:
919 if f in mf2:
920 del mf2[f]
920 del mf2[f]
921 else:
921 else:
922 # we are comparing two revisions
922 # we are comparing two revisions
923 mf2 = mfmatches(node2)
923 mf2 = mfmatches(node2)
924
924
925 if not compareworking:
925 if not compareworking:
926 # flush lists from dirstate before comparing manifests
926 # flush lists from dirstate before comparing manifests
927 modified, added, clean = [], [], []
927 modified, added, clean = [], [], []
928
928
929 # make sure to sort the files so we talk to the disk in a
929 # make sure to sort the files so we talk to the disk in a
930 # reasonable order
930 # reasonable order
931 mf2keys = mf2.keys()
931 mf2keys = mf2.keys()
932 mf2keys.sort()
932 mf2keys.sort()
933 for fn in mf2keys:
933 for fn in mf2keys:
934 if mf1.has_key(fn):
934 if mf1.has_key(fn):
935 if mf1.flags(fn) != mf2.flags(fn) or \
935 if mf1.flags(fn) != mf2.flags(fn) or \
936 (mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1))):
936 (mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1))):
937 modified.append(fn)
937 modified.append(fn)
938 elif list_clean:
938 elif list_clean:
939 clean.append(fn)
939 clean.append(fn)
940 del mf1[fn]
940 del mf1[fn]
941 else:
941 else:
942 added.append(fn)
942 added.append(fn)
943
943
944 removed = mf1.keys()
944 removed = mf1.keys()
945
945
946 # sort and return results:
946 # sort and return results:
947 for l in modified, added, removed, deleted, unknown, ignored, clean:
947 for l in modified, added, removed, deleted, unknown, ignored, clean:
948 l.sort()
948 l.sort()
949 return (modified, added, removed, deleted, unknown, ignored, clean)
949 return (modified, added, removed, deleted, unknown, ignored, clean)
950
950
951 def add(self, list, wlock=None):
951 def add(self, list, wlock=None):
952 if not wlock:
952 if not wlock:
953 wlock = self.wlock()
953 wlock = self.wlock()
954 for f in list:
954 for f in list:
955 p = self.wjoin(f)
955 p = self.wjoin(f)
956 if not os.path.exists(p):
956 if not os.path.exists(p):
957 self.ui.warn(_("%s does not exist!\n") % f)
957 self.ui.warn(_("%s does not exist!\n") % f)
958 elif not os.path.isfile(p):
958 elif not os.path.isfile(p):
959 self.ui.warn(_("%s not added: only files supported currently\n")
959 self.ui.warn(_("%s not added: only files supported currently\n")
960 % f)
960 % f)
961 elif self.dirstate.state(f) in 'an':
961 elif self.dirstate.state(f) in 'an':
962 self.ui.warn(_("%s already tracked!\n") % f)
962 self.ui.warn(_("%s already tracked!\n") % f)
963 else:
963 else:
964 self.dirstate.update([f], "a")
964 self.dirstate.update([f], "a")
965
965
966 def forget(self, list, wlock=None):
966 def forget(self, list, wlock=None):
967 if not wlock:
967 if not wlock:
968 wlock = self.wlock()
968 wlock = self.wlock()
969 for f in list:
969 for f in list:
970 if self.dirstate.state(f) not in 'ai':
970 if self.dirstate.state(f) not in 'ai':
971 self.ui.warn(_("%s not added!\n") % f)
971 self.ui.warn(_("%s not added!\n") % f)
972 else:
972 else:
973 self.dirstate.forget([f])
973 self.dirstate.forget([f])
974
974
975 def remove(self, list, unlink=False, wlock=None):
975 def remove(self, list, unlink=False, wlock=None):
976 if unlink:
976 if unlink:
977 for f in list:
977 for f in list:
978 try:
978 try:
979 util.unlink(self.wjoin(f))
979 util.unlink(self.wjoin(f))
980 except OSError, inst:
980 except OSError, inst:
981 if inst.errno != errno.ENOENT:
981 if inst.errno != errno.ENOENT:
982 raise
982 raise
983 if not wlock:
983 if not wlock:
984 wlock = self.wlock()
984 wlock = self.wlock()
985 for f in list:
985 for f in list:
986 p = self.wjoin(f)
986 p = self.wjoin(f)
987 if os.path.exists(p):
987 if os.path.exists(p):
988 self.ui.warn(_("%s still exists!\n") % f)
988 self.ui.warn(_("%s still exists!\n") % f)
989 elif self.dirstate.state(f) == 'a':
989 elif self.dirstate.state(f) == 'a':
990 self.dirstate.forget([f])
990 self.dirstate.forget([f])
991 elif f not in self.dirstate:
991 elif f not in self.dirstate:
992 self.ui.warn(_("%s not tracked!\n") % f)
992 self.ui.warn(_("%s not tracked!\n") % f)
993 else:
993 else:
994 self.dirstate.update([f], "r")
994 self.dirstate.update([f], "r")
995
995
996 def undelete(self, list, wlock=None):
996 def undelete(self, list, wlock=None):
997 p = self.dirstate.parents()[0]
997 p = self.dirstate.parents()[0]
998 mn = self.changelog.read(p)[0]
998 mn = self.changelog.read(p)[0]
999 m = self.manifest.read(mn)
999 m = self.manifest.read(mn)
1000 if not wlock:
1000 if not wlock:
1001 wlock = self.wlock()
1001 wlock = self.wlock()
1002 for f in list:
1002 for f in list:
1003 if self.dirstate.state(f) not in "r":
1003 if self.dirstate.state(f) not in "r":
1004 self.ui.warn("%s not removed!\n" % f)
1004 self.ui.warn("%s not removed!\n" % f)
1005 else:
1005 else:
1006 t = self.file(f).read(m[f])
1006 t = self.file(f).read(m[f])
1007 self.wwrite(f, t)
1007 self.wwrite(f, t)
1008 util.set_exec(self.wjoin(f), m.execf(f))
1008 util.set_exec(self.wjoin(f), m.execf(f))
1009 self.dirstate.update([f], "n")
1009 self.dirstate.update([f], "n")
1010
1010
1011 def copy(self, source, dest, wlock=None):
1011 def copy(self, source, dest, wlock=None):
1012 p = self.wjoin(dest)
1012 p = self.wjoin(dest)
1013 if not os.path.exists(p):
1013 if not os.path.exists(p):
1014 self.ui.warn(_("%s does not exist!\n") % dest)
1014 self.ui.warn(_("%s does not exist!\n") % dest)
1015 elif not os.path.isfile(p):
1015 elif not os.path.isfile(p):
1016 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
1016 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
1017 else:
1017 else:
1018 if not wlock:
1018 if not wlock:
1019 wlock = self.wlock()
1019 wlock = self.wlock()
1020 if self.dirstate.state(dest) == '?':
1020 if self.dirstate.state(dest) == '?':
1021 self.dirstate.update([dest], "a")
1021 self.dirstate.update([dest], "a")
1022 self.dirstate.copy(source, dest)
1022 self.dirstate.copy(source, dest)
1023
1023
1024 def heads(self, start=None):
1024 def heads(self, start=None):
1025 heads = self.changelog.heads(start)
1025 heads = self.changelog.heads(start)
1026 # sort the output in rev descending order
1026 # sort the output in rev descending order
1027 heads = [(-self.changelog.rev(h), h) for h in heads]
1027 heads = [(-self.changelog.rev(h), h) for h in heads]
1028 heads.sort()
1028 heads.sort()
1029 return [n for (r, n) in heads]
1029 return [n for (r, n) in heads]
1030
1030
1031 # branchlookup returns a dict giving a list of branches for
1031 # branchlookup returns a dict giving a list of branches for
1032 # each head. A branch is defined as the tag of a node or
1032 # each head. A branch is defined as the tag of a node or
1033 # the branch of the node's parents. If a node has multiple
1033 # the branch of the node's parents. If a node has multiple
1034 # branch tags, tags are eliminated if they are visible from other
1034 # branch tags, tags are eliminated if they are visible from other
1035 # branch tags.
1035 # branch tags.
1036 #
1036 #
1037 # So, for this graph: a->b->c->d->e
1037 # So, for this graph: a->b->c->d->e
1038 # \ /
1038 # \ /
1039 # aa -----/
1039 # aa -----/
1040 # a has tag 2.6.12
1040 # a has tag 2.6.12
1041 # d has tag 2.6.13
1041 # d has tag 2.6.13
1042 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
1042 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
1043 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
1043 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
1044 # from the list.
1044 # from the list.
1045 #
1045 #
1046 # It is possible that more than one head will have the same branch tag.
1046 # It is possible that more than one head will have the same branch tag.
1047 # callers need to check the result for multiple heads under the same
1047 # callers need to check the result for multiple heads under the same
1048 # branch tag if that is a problem for them (ie checkout of a specific
1048 # branch tag if that is a problem for them (ie checkout of a specific
1049 # branch).
1049 # branch).
1050 #
1050 #
1051 # passing in a specific branch will limit the depth of the search
1051 # passing in a specific branch will limit the depth of the search
1052 # through the parents. It won't limit the branches returned in the
1052 # through the parents. It won't limit the branches returned in the
1053 # result though.
1053 # result though.
1054 def branchlookup(self, heads=None, branch=None):
1054 def branchlookup(self, heads=None, branch=None):
1055 if not heads:
1055 if not heads:
1056 heads = self.heads()
1056 heads = self.heads()
1057 headt = [ h for h in heads ]
1057 headt = [ h for h in heads ]
1058 chlog = self.changelog
1058 chlog = self.changelog
1059 branches = {}
1059 branches = {}
1060 merges = []
1060 merges = []
1061 seenmerge = {}
1061 seenmerge = {}
1062
1062
1063 # traverse the tree once for each head, recording in the branches
1063 # traverse the tree once for each head, recording in the branches
1064 # dict which tags are visible from this head. The branches
1064 # dict which tags are visible from this head. The branches
1065 # dict also records which tags are visible from each tag
1065 # dict also records which tags are visible from each tag
1066 # while we traverse.
1066 # while we traverse.
1067 while headt or merges:
1067 while headt or merges:
1068 if merges:
1068 if merges:
1069 n, found = merges.pop()
1069 n, found = merges.pop()
1070 visit = [n]
1070 visit = [n]
1071 else:
1071 else:
1072 h = headt.pop()
1072 h = headt.pop()
1073 visit = [h]
1073 visit = [h]
1074 found = [h]
1074 found = [h]
1075 seen = {}
1075 seen = {}
1076 while visit:
1076 while visit:
1077 n = visit.pop()
1077 n = visit.pop()
1078 if n in seen:
1078 if n in seen:
1079 continue
1079 continue
1080 pp = chlog.parents(n)
1080 pp = chlog.parents(n)
1081 tags = self.nodetags(n)
1081 tags = self.nodetags(n)
1082 if tags:
1082 if tags:
1083 for x in tags:
1083 for x in tags:
1084 if x == 'tip':
1084 if x == 'tip':
1085 continue
1085 continue
1086 for f in found:
1086 for f in found:
1087 branches.setdefault(f, {})[n] = 1
1087 branches.setdefault(f, {})[n] = 1
1088 branches.setdefault(n, {})[n] = 1
1088 branches.setdefault(n, {})[n] = 1
1089 break
1089 break
1090 if n not in found:
1090 if n not in found:
1091 found.append(n)
1091 found.append(n)
1092 if branch in tags:
1092 if branch in tags:
1093 continue
1093 continue
1094 seen[n] = 1
1094 seen[n] = 1
1095 if pp[1] != nullid and n not in seenmerge:
1095 if pp[1] != nullid and n not in seenmerge:
1096 merges.append((pp[1], [x for x in found]))
1096 merges.append((pp[1], [x for x in found]))
1097 seenmerge[n] = 1
1097 seenmerge[n] = 1
1098 if pp[0] != nullid:
1098 if pp[0] != nullid:
1099 visit.append(pp[0])
1099 visit.append(pp[0])
1100 # traverse the branches dict, eliminating branch tags from each
1100 # traverse the branches dict, eliminating branch tags from each
1101 # head that are visible from another branch tag for that head.
1101 # head that are visible from another branch tag for that head.
1102 out = {}
1102 out = {}
1103 viscache = {}
1103 viscache = {}
1104 for h in heads:
1104 for h in heads:
1105 def visible(node):
1105 def visible(node):
1106 if node in viscache:
1106 if node in viscache:
1107 return viscache[node]
1107 return viscache[node]
1108 ret = {}
1108 ret = {}
1109 visit = [node]
1109 visit = [node]
1110 while visit:
1110 while visit:
1111 x = visit.pop()
1111 x = visit.pop()
1112 if x in viscache:
1112 if x in viscache:
1113 ret.update(viscache[x])
1113 ret.update(viscache[x])
1114 elif x not in ret:
1114 elif x not in ret:
1115 ret[x] = 1
1115 ret[x] = 1
1116 if x in branches:
1116 if x in branches:
1117 visit[len(visit):] = branches[x].keys()
1117 visit[len(visit):] = branches[x].keys()
1118 viscache[node] = ret
1118 viscache[node] = ret
1119 return ret
1119 return ret
1120 if h not in branches:
1120 if h not in branches:
1121 continue
1121 continue
1122 # O(n^2), but somewhat limited. This only searches the
1122 # O(n^2), but somewhat limited. This only searches the
1123 # tags visible from a specific head, not all the tags in the
1123 # tags visible from a specific head, not all the tags in the
1124 # whole repo.
1124 # whole repo.
1125 for b in branches[h]:
1125 for b in branches[h]:
1126 vis = False
1126 vis = False
1127 for bb in branches[h].keys():
1127 for bb in branches[h].keys():
1128 if b != bb:
1128 if b != bb:
1129 if b in visible(bb):
1129 if b in visible(bb):
1130 vis = True
1130 vis = True
1131 break
1131 break
1132 if not vis:
1132 if not vis:
1133 l = out.setdefault(h, [])
1133 l = out.setdefault(h, [])
1134 l[len(l):] = self.nodetags(b)
1134 l[len(l):] = self.nodetags(b)
1135 return out
1135 return out
1136
1136
1137 def branches(self, nodes):
1137 def branches(self, nodes):
1138 if not nodes:
1138 if not nodes:
1139 nodes = [self.changelog.tip()]
1139 nodes = [self.changelog.tip()]
1140 b = []
1140 b = []
1141 for n in nodes:
1141 for n in nodes:
1142 t = n
1142 t = n
1143 while 1:
1143 while 1:
1144 p = self.changelog.parents(n)
1144 p = self.changelog.parents(n)
1145 if p[1] != nullid or p[0] == nullid:
1145 if p[1] != nullid or p[0] == nullid:
1146 b.append((t, n, p[0], p[1]))
1146 b.append((t, n, p[0], p[1]))
1147 break
1147 break
1148 n = p[0]
1148 n = p[0]
1149 return b
1149 return b
1150
1150
1151 def between(self, pairs):
1151 def between(self, pairs):
1152 r = []
1152 r = []
1153
1153
1154 for top, bottom in pairs:
1154 for top, bottom in pairs:
1155 n, l, i = top, [], 0
1155 n, l, i = top, [], 0
1156 f = 1
1156 f = 1
1157
1157
1158 while n != bottom:
1158 while n != bottom:
1159 p = self.changelog.parents(n)[0]
1159 p = self.changelog.parents(n)[0]
1160 if i == f:
1160 if i == f:
1161 l.append(n)
1161 l.append(n)
1162 f = f * 2
1162 f = f * 2
1163 n = p
1163 n = p
1164 i += 1
1164 i += 1
1165
1165
1166 r.append(l)
1166 r.append(l)
1167
1167
1168 return r
1168 return r
1169
1169
1170 def findincoming(self, remote, base=None, heads=None, force=False):
1170 def findincoming(self, remote, base=None, heads=None, force=False):
1171 """Return list of roots of the subsets of missing nodes from remote
1171 """Return list of roots of the subsets of missing nodes from remote
1172
1172
1173 If base dict is specified, assume that these nodes and their parents
1173 If base dict is specified, assume that these nodes and their parents
1174 exist on the remote side and that no child of a node of base exists
1174 exist on the remote side and that no child of a node of base exists
1175 in both remote and self.
1175 in both remote and self.
1176 Furthermore base will be updated to include the nodes that exists
1176 Furthermore base will be updated to include the nodes that exists
1177 in self and remote but no children exists in self and remote.
1177 in self and remote but no children exists in self and remote.
1178 If a list of heads is specified, return only nodes which are heads
1178 If a list of heads is specified, return only nodes which are heads
1179 or ancestors of these heads.
1179 or ancestors of these heads.
1180
1180
1181 All the ancestors of base are in self and in remote.
1181 All the ancestors of base are in self and in remote.
1182 All the descendants of the list returned are missing in self.
1182 All the descendants of the list returned are missing in self.
1183 (and so we know that the rest of the nodes are missing in remote, see
1183 (and so we know that the rest of the nodes are missing in remote, see
1184 outgoing)
1184 outgoing)
1185 """
1185 """
1186 m = self.changelog.nodemap
1186 m = self.changelog.nodemap
1187 search = []
1187 search = []
1188 fetch = {}
1188 fetch = {}
1189 seen = {}
1189 seen = {}
1190 seenbranch = {}
1190 seenbranch = {}
1191 if base == None:
1191 if base == None:
1192 base = {}
1192 base = {}
1193
1193
1194 if not heads:
1194 if not heads:
1195 heads = remote.heads()
1195 heads = remote.heads()
1196
1196
1197 if self.changelog.tip() == nullid:
1197 if self.changelog.tip() == nullid:
1198 base[nullid] = 1
1198 base[nullid] = 1
1199 if heads != [nullid]:
1199 if heads != [nullid]:
1200 return [nullid]
1200 return [nullid]
1201 return []
1201 return []
1202
1202
1203 # assume we're closer to the tip than the root
1203 # assume we're closer to the tip than the root
1204 # and start by examining the heads
1204 # and start by examining the heads
1205 self.ui.status(_("searching for changes\n"))
1205 self.ui.status(_("searching for changes\n"))
1206
1206
1207 unknown = []
1207 unknown = []
1208 for h in heads:
1208 for h in heads:
1209 if h not in m:
1209 if h not in m:
1210 unknown.append(h)
1210 unknown.append(h)
1211 else:
1211 else:
1212 base[h] = 1
1212 base[h] = 1
1213
1213
1214 if not unknown:
1214 if not unknown:
1215 return []
1215 return []
1216
1216
1217 req = dict.fromkeys(unknown)
1217 req = dict.fromkeys(unknown)
1218 reqcnt = 0
1218 reqcnt = 0
1219
1219
1220 # search through remote branches
1220 # search through remote branches
1221 # a 'branch' here is a linear segment of history, with four parts:
1221 # a 'branch' here is a linear segment of history, with four parts:
1222 # head, root, first parent, second parent
1222 # head, root, first parent, second parent
1223 # (a branch always has two parents (or none) by definition)
1223 # (a branch always has two parents (or none) by definition)
1224 unknown = remote.branches(unknown)
1224 unknown = remote.branches(unknown)
1225 while unknown:
1225 while unknown:
1226 r = []
1226 r = []
1227 while unknown:
1227 while unknown:
1228 n = unknown.pop(0)
1228 n = unknown.pop(0)
1229 if n[0] in seen:
1229 if n[0] in seen:
1230 continue
1230 continue
1231
1231
1232 self.ui.debug(_("examining %s:%s\n")
1232 self.ui.debug(_("examining %s:%s\n")
1233 % (short(n[0]), short(n[1])))
1233 % (short(n[0]), short(n[1])))
1234 if n[0] == nullid: # found the end of the branch
1234 if n[0] == nullid: # found the end of the branch
1235 pass
1235 pass
1236 elif n in seenbranch:
1236 elif n in seenbranch:
1237 self.ui.debug(_("branch already found\n"))
1237 self.ui.debug(_("branch already found\n"))
1238 continue
1238 continue
1239 elif n[1] and n[1] in m: # do we know the base?
1239 elif n[1] and n[1] in m: # do we know the base?
1240 self.ui.debug(_("found incomplete branch %s:%s\n")
1240 self.ui.debug(_("found incomplete branch %s:%s\n")
1241 % (short(n[0]), short(n[1])))
1241 % (short(n[0]), short(n[1])))
1242 search.append(n) # schedule branch range for scanning
1242 search.append(n) # schedule branch range for scanning
1243 seenbranch[n] = 1
1243 seenbranch[n] = 1
1244 else:
1244 else:
1245 if n[1] not in seen and n[1] not in fetch:
1245 if n[1] not in seen and n[1] not in fetch:
1246 if n[2] in m and n[3] in m:
1246 if n[2] in m and n[3] in m:
1247 self.ui.debug(_("found new changeset %s\n") %
1247 self.ui.debug(_("found new changeset %s\n") %
1248 short(n[1]))
1248 short(n[1]))
1249 fetch[n[1]] = 1 # earliest unknown
1249 fetch[n[1]] = 1 # earliest unknown
1250 for p in n[2:4]:
1250 for p in n[2:4]:
1251 if p in m:
1251 if p in m:
1252 base[p] = 1 # latest known
1252 base[p] = 1 # latest known
1253
1253
1254 for p in n[2:4]:
1254 for p in n[2:4]:
1255 if p not in req and p not in m:
1255 if p not in req and p not in m:
1256 r.append(p)
1256 r.append(p)
1257 req[p] = 1
1257 req[p] = 1
1258 seen[n[0]] = 1
1258 seen[n[0]] = 1
1259
1259
1260 if r:
1260 if r:
1261 reqcnt += 1
1261 reqcnt += 1
1262 self.ui.debug(_("request %d: %s\n") %
1262 self.ui.debug(_("request %d: %s\n") %
1263 (reqcnt, " ".join(map(short, r))))
1263 (reqcnt, " ".join(map(short, r))))
1264 for p in xrange(0, len(r), 10):
1264 for p in xrange(0, len(r), 10):
1265 for b in remote.branches(r[p:p+10]):
1265 for b in remote.branches(r[p:p+10]):
1266 self.ui.debug(_("received %s:%s\n") %
1266 self.ui.debug(_("received %s:%s\n") %
1267 (short(b[0]), short(b[1])))
1267 (short(b[0]), short(b[1])))
1268 unknown.append(b)
1268 unknown.append(b)
1269
1269
1270 # do binary search on the branches we found
1270 # do binary search on the branches we found
1271 while search:
1271 while search:
1272 n = search.pop(0)
1272 n = search.pop(0)
1273 reqcnt += 1
1273 reqcnt += 1
1274 l = remote.between([(n[0], n[1])])[0]
1274 l = remote.between([(n[0], n[1])])[0]
1275 l.append(n[1])
1275 l.append(n[1])
1276 p = n[0]
1276 p = n[0]
1277 f = 1
1277 f = 1
1278 for i in l:
1278 for i in l:
1279 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1279 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1280 if i in m:
1280 if i in m:
1281 if f <= 2:
1281 if f <= 2:
1282 self.ui.debug(_("found new branch changeset %s\n") %
1282 self.ui.debug(_("found new branch changeset %s\n") %
1283 short(p))
1283 short(p))
1284 fetch[p] = 1
1284 fetch[p] = 1
1285 base[i] = 1
1285 base[i] = 1
1286 else:
1286 else:
1287 self.ui.debug(_("narrowed branch search to %s:%s\n")
1287 self.ui.debug(_("narrowed branch search to %s:%s\n")
1288 % (short(p), short(i)))
1288 % (short(p), short(i)))
1289 search.append((p, i))
1289 search.append((p, i))
1290 break
1290 break
1291 p, f = i, f * 2
1291 p, f = i, f * 2
1292
1292
1293 # sanity check our fetch list
1293 # sanity check our fetch list
1294 for f in fetch.keys():
1294 for f in fetch.keys():
1295 if f in m:
1295 if f in m:
1296 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1296 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1297
1297
1298 if base.keys() == [nullid]:
1298 if base.keys() == [nullid]:
1299 if force:
1299 if force:
1300 self.ui.warn(_("warning: repository is unrelated\n"))
1300 self.ui.warn(_("warning: repository is unrelated\n"))
1301 else:
1301 else:
1302 raise util.Abort(_("repository is unrelated"))
1302 raise util.Abort(_("repository is unrelated"))
1303
1303
1304 self.ui.debug(_("found new changesets starting at ") +
1304 self.ui.debug(_("found new changesets starting at ") +
1305 " ".join([short(f) for f in fetch]) + "\n")
1305 " ".join([short(f) for f in fetch]) + "\n")
1306
1306
1307 self.ui.debug(_("%d total queries\n") % reqcnt)
1307 self.ui.debug(_("%d total queries\n") % reqcnt)
1308
1308
1309 return fetch.keys()
1309 return fetch.keys()
1310
1310
1311 def findoutgoing(self, remote, base=None, heads=None, force=False):
1311 def findoutgoing(self, remote, base=None, heads=None, force=False):
1312 """Return list of nodes that are roots of subsets not in remote
1312 """Return list of nodes that are roots of subsets not in remote
1313
1313
1314 If base dict is specified, assume that these nodes and their parents
1314 If base dict is specified, assume that these nodes and their parents
1315 exist on the remote side.
1315 exist on the remote side.
1316 If a list of heads is specified, return only nodes which are heads
1316 If a list of heads is specified, return only nodes which are heads
1317 or ancestors of these heads, and return a second element which
1317 or ancestors of these heads, and return a second element which
1318 contains all remote heads which get new children.
1318 contains all remote heads which get new children.
1319 """
1319 """
1320 if base == None:
1320 if base == None:
1321 base = {}
1321 base = {}
1322 self.findincoming(remote, base, heads, force=force)
1322 self.findincoming(remote, base, heads, force=force)
1323
1323
1324 self.ui.debug(_("common changesets up to ")
1324 self.ui.debug(_("common changesets up to ")
1325 + " ".join(map(short, base.keys())) + "\n")
1325 + " ".join(map(short, base.keys())) + "\n")
1326
1326
1327 remain = dict.fromkeys(self.changelog.nodemap)
1327 remain = dict.fromkeys(self.changelog.nodemap)
1328
1328
1329 # prune everything remote has from the tree
1329 # prune everything remote has from the tree
1330 del remain[nullid]
1330 del remain[nullid]
1331 remove = base.keys()
1331 remove = base.keys()
1332 while remove:
1332 while remove:
1333 n = remove.pop(0)
1333 n = remove.pop(0)
1334 if n in remain:
1334 if n in remain:
1335 del remain[n]
1335 del remain[n]
1336 for p in self.changelog.parents(n):
1336 for p in self.changelog.parents(n):
1337 remove.append(p)
1337 remove.append(p)
1338
1338
1339 # find every node whose parents have been pruned
1339 # find every node whose parents have been pruned
1340 subset = []
1340 subset = []
1341 # find every remote head that will get new children
1341 # find every remote head that will get new children
1342 updated_heads = {}
1342 updated_heads = {}
1343 for n in remain:
1343 for n in remain:
1344 p1, p2 = self.changelog.parents(n)
1344 p1, p2 = self.changelog.parents(n)
1345 if p1 not in remain and p2 not in remain:
1345 if p1 not in remain and p2 not in remain:
1346 subset.append(n)
1346 subset.append(n)
1347 if heads:
1347 if heads:
1348 if p1 in heads:
1348 if p1 in heads:
1349 updated_heads[p1] = True
1349 updated_heads[p1] = True
1350 if p2 in heads:
1350 if p2 in heads:
1351 updated_heads[p2] = True
1351 updated_heads[p2] = True
1352
1352
1353 # this is the set of all roots we have to push
1353 # this is the set of all roots we have to push
1354 if heads:
1354 if heads:
1355 return subset, updated_heads.keys()
1355 return subset, updated_heads.keys()
1356 else:
1356 else:
1357 return subset
1357 return subset
1358
1358
1359 def pull(self, remote, heads=None, force=False, lock=None):
1359 def pull(self, remote, heads=None, force=False, lock=None):
1360 mylock = False
1360 mylock = False
1361 if not lock:
1361 if not lock:
1362 lock = self.lock()
1362 lock = self.lock()
1363 mylock = True
1363 mylock = True
1364
1364
1365 try:
1365 try:
1366 fetch = self.findincoming(remote, force=force)
1366 fetch = self.findincoming(remote, force=force)
1367 if fetch == [nullid]:
1367 if fetch == [nullid]:
1368 self.ui.status(_("requesting all changes\n"))
1368 self.ui.status(_("requesting all changes\n"))
1369
1369
1370 if not fetch:
1370 if not fetch:
1371 self.ui.status(_("no changes found\n"))
1371 self.ui.status(_("no changes found\n"))
1372 return 0
1372 return 0
1373
1373
1374 if heads is None:
1374 if heads is None:
1375 cg = remote.changegroup(fetch, 'pull')
1375 cg = remote.changegroup(fetch, 'pull')
1376 else:
1376 else:
1377 if 'changegroupsubset' not in remote.capabilities:
1377 if 'changegroupsubset' not in remote.capabilities:
1378 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1378 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1379 cg = remote.changegroupsubset(fetch, heads, 'pull')
1379 cg = remote.changegroupsubset(fetch, heads, 'pull')
1380 return self.addchangegroup(cg, 'pull', remote.url())
1380 return self.addchangegroup(cg, 'pull', remote.url())
1381 finally:
1381 finally:
1382 if mylock:
1382 if mylock:
1383 lock.release()
1383 lock.release()
1384
1384
1385 def push(self, remote, force=False, revs=None):
1385 def push(self, remote, force=False, revs=None):
1386 # there are two ways to push to remote repo:
1386 # there are two ways to push to remote repo:
1387 #
1387 #
1388 # addchangegroup assumes local user can lock remote
1388 # addchangegroup assumes local user can lock remote
1389 # repo (local filesystem, old ssh servers).
1389 # repo (local filesystem, old ssh servers).
1390 #
1390 #
1391 # unbundle assumes local user cannot lock remote repo (new ssh
1391 # unbundle assumes local user cannot lock remote repo (new ssh
1392 # servers, http servers).
1392 # servers, http servers).
1393
1393
1394 if remote.capable('unbundle'):
1394 if remote.capable('unbundle'):
1395 return self.push_unbundle(remote, force, revs)
1395 return self.push_unbundle(remote, force, revs)
1396 return self.push_addchangegroup(remote, force, revs)
1396 return self.push_addchangegroup(remote, force, revs)
1397
1397
1398 def prepush(self, remote, force, revs):
1398 def prepush(self, remote, force, revs):
1399 base = {}
1399 base = {}
1400 remote_heads = remote.heads()
1400 remote_heads = remote.heads()
1401 inc = self.findincoming(remote, base, remote_heads, force=force)
1401 inc = self.findincoming(remote, base, remote_heads, force=force)
1402
1402
1403 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1403 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1404 if revs is not None:
1404 if revs is not None:
1405 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1405 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1406 else:
1406 else:
1407 bases, heads = update, self.changelog.heads()
1407 bases, heads = update, self.changelog.heads()
1408
1408
1409 if not bases:
1409 if not bases:
1410 self.ui.status(_("no changes found\n"))
1410 self.ui.status(_("no changes found\n"))
1411 return None, 1
1411 return None, 1
1412 elif not force:
1412 elif not force:
1413 # check if we're creating new remote heads
1413 # check if we're creating new remote heads
1414 # to be a remote head after push, node must be either
1414 # to be a remote head after push, node must be either
1415 # - unknown locally
1415 # - unknown locally
1416 # - a local outgoing head descended from update
1416 # - a local outgoing head descended from update
1417 # - a remote head that's known locally and not
1417 # - a remote head that's known locally and not
1418 # ancestral to an outgoing head
1418 # ancestral to an outgoing head
1419
1419
1420 warn = 0
1420 warn = 0
1421
1421
1422 if remote_heads == [nullid]:
1422 if remote_heads == [nullid]:
1423 warn = 0
1423 warn = 0
1424 elif not revs and len(heads) > len(remote_heads):
1424 elif not revs and len(heads) > len(remote_heads):
1425 warn = 1
1425 warn = 1
1426 else:
1426 else:
1427 newheads = list(heads)
1427 newheads = list(heads)
1428 for r in remote_heads:
1428 for r in remote_heads:
1429 if r in self.changelog.nodemap:
1429 if r in self.changelog.nodemap:
1430 desc = self.changelog.heads(r, heads)
1430 desc = self.changelog.heads(r, heads)
1431 l = [h for h in heads if h in desc]
1431 l = [h for h in heads if h in desc]
1432 if not l:
1432 if not l:
1433 newheads.append(r)
1433 newheads.append(r)
1434 else:
1434 else:
1435 newheads.append(r)
1435 newheads.append(r)
1436 if len(newheads) > len(remote_heads):
1436 if len(newheads) > len(remote_heads):
1437 warn = 1
1437 warn = 1
1438
1438
1439 if warn:
1439 if warn:
1440 self.ui.warn(_("abort: push creates new remote branches!\n"))
1440 self.ui.warn(_("abort: push creates new remote branches!\n"))
1441 self.ui.status(_("(did you forget to merge?"
1441 self.ui.status(_("(did you forget to merge?"
1442 " use push -f to force)\n"))
1442 " use push -f to force)\n"))
1443 return None, 1
1443 return None, 1
1444 elif inc:
1444 elif inc:
1445 self.ui.warn(_("note: unsynced remote changes!\n"))
1445 self.ui.warn(_("note: unsynced remote changes!\n"))
1446
1446
1447
1447
1448 if revs is None:
1448 if revs is None:
1449 cg = self.changegroup(update, 'push')
1449 cg = self.changegroup(update, 'push')
1450 else:
1450 else:
1451 cg = self.changegroupsubset(update, revs, 'push')
1451 cg = self.changegroupsubset(update, revs, 'push')
1452 return cg, remote_heads
1452 return cg, remote_heads
1453
1453
1454 def push_addchangegroup(self, remote, force, revs):
1454 def push_addchangegroup(self, remote, force, revs):
1455 lock = remote.lock()
1455 lock = remote.lock()
1456
1456
1457 ret = self.prepush(remote, force, revs)
1457 ret = self.prepush(remote, force, revs)
1458 if ret[0] is not None:
1458 if ret[0] is not None:
1459 cg, remote_heads = ret
1459 cg, remote_heads = ret
1460 return remote.addchangegroup(cg, 'push', self.url())
1460 return remote.addchangegroup(cg, 'push', self.url())
1461 return ret[1]
1461 return ret[1]
1462
1462
1463 def push_unbundle(self, remote, force, revs):
1463 def push_unbundle(self, remote, force, revs):
1464 # local repo finds heads on server, finds out what revs it
1464 # local repo finds heads on server, finds out what revs it
1465 # must push. once revs transferred, if server finds it has
1465 # must push. once revs transferred, if server finds it has
1466 # different heads (someone else won commit/push race), server
1466 # different heads (someone else won commit/push race), server
1467 # aborts.
1467 # aborts.
1468
1468
1469 ret = self.prepush(remote, force, revs)
1469 ret = self.prepush(remote, force, revs)
1470 if ret[0] is not None:
1470 if ret[0] is not None:
1471 cg, remote_heads = ret
1471 cg, remote_heads = ret
1472 if force: remote_heads = ['force']
1472 if force: remote_heads = ['force']
1473 return remote.unbundle(cg, remote_heads, 'push')
1473 return remote.unbundle(cg, remote_heads, 'push')
1474 return ret[1]
1474 return ret[1]
1475
1475
1476 def changegroupinfo(self, nodes):
1476 def changegroupinfo(self, nodes):
1477 self.ui.note(_("%d changesets found\n") % len(nodes))
1477 self.ui.note(_("%d changesets found\n") % len(nodes))
1478 if self.ui.debugflag:
1478 if self.ui.debugflag:
1479 self.ui.debug(_("List of changesets:\n"))
1479 self.ui.debug(_("List of changesets:\n"))
1480 for node in nodes:
1480 for node in nodes:
1481 self.ui.debug("%s\n" % hex(node))
1481 self.ui.debug("%s\n" % hex(node))
1482
1482
1483 def changegroupsubset(self, bases, heads, source):
1483 def changegroupsubset(self, bases, heads, source):
1484 """This function generates a changegroup consisting of all the nodes
1484 """This function generates a changegroup consisting of all the nodes
1485 that are descendents of any of the bases, and ancestors of any of
1485 that are descendents of any of the bases, and ancestors of any of
1486 the heads.
1486 the heads.
1487
1487
1488 It is fairly complex as determining which filenodes and which
1488 It is fairly complex as determining which filenodes and which
1489 manifest nodes need to be included for the changeset to be complete
1489 manifest nodes need to be included for the changeset to be complete
1490 is non-trivial.
1490 is non-trivial.
1491
1491
1492 Another wrinkle is doing the reverse, figuring out which changeset in
1492 Another wrinkle is doing the reverse, figuring out which changeset in
1493 the changegroup a particular filenode or manifestnode belongs to."""
1493 the changegroup a particular filenode or manifestnode belongs to."""
1494
1494
1495 self.hook('preoutgoing', throw=True, source=source)
1495 self.hook('preoutgoing', throw=True, source=source)
1496
1496
1497 # Set up some initial variables
1497 # Set up some initial variables
1498 # Make it easy to refer to self.changelog
1498 # Make it easy to refer to self.changelog
1499 cl = self.changelog
1499 cl = self.changelog
1500 # msng is short for missing - compute the list of changesets in this
1500 # msng is short for missing - compute the list of changesets in this
1501 # changegroup.
1501 # changegroup.
1502 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1502 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1503 self.changegroupinfo(msng_cl_lst)
1503 self.changegroupinfo(msng_cl_lst)
1504 # Some bases may turn out to be superfluous, and some heads may be
1504 # Some bases may turn out to be superfluous, and some heads may be
1505 # too. nodesbetween will return the minimal set of bases and heads
1505 # too. nodesbetween will return the minimal set of bases and heads
1506 # necessary to re-create the changegroup.
1506 # necessary to re-create the changegroup.
1507
1507
1508 # Known heads are the list of heads that it is assumed the recipient
1508 # Known heads are the list of heads that it is assumed the recipient
1509 # of this changegroup will know about.
1509 # of this changegroup will know about.
1510 knownheads = {}
1510 knownheads = {}
1511 # We assume that all parents of bases are known heads.
1511 # We assume that all parents of bases are known heads.
1512 for n in bases:
1512 for n in bases:
1513 for p in cl.parents(n):
1513 for p in cl.parents(n):
1514 if p != nullid:
1514 if p != nullid:
1515 knownheads[p] = 1
1515 knownheads[p] = 1
1516 knownheads = knownheads.keys()
1516 knownheads = knownheads.keys()
1517 if knownheads:
1517 if knownheads:
1518 # Now that we know what heads are known, we can compute which
1518 # Now that we know what heads are known, we can compute which
1519 # changesets are known. The recipient must know about all
1519 # changesets are known. The recipient must know about all
1520 # changesets required to reach the known heads from the null
1520 # changesets required to reach the known heads from the null
1521 # changeset.
1521 # changeset.
1522 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1522 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1523 junk = None
1523 junk = None
1524 # Transform the list into an ersatz set.
1524 # Transform the list into an ersatz set.
1525 has_cl_set = dict.fromkeys(has_cl_set)
1525 has_cl_set = dict.fromkeys(has_cl_set)
1526 else:
1526 else:
1527 # If there were no known heads, the recipient cannot be assumed to
1527 # If there were no known heads, the recipient cannot be assumed to
1528 # know about any changesets.
1528 # know about any changesets.
1529 has_cl_set = {}
1529 has_cl_set = {}
1530
1530
1531 # Make it easy to refer to self.manifest
1531 # Make it easy to refer to self.manifest
1532 mnfst = self.manifest
1532 mnfst = self.manifest
1533 # We don't know which manifests are missing yet
1533 # We don't know which manifests are missing yet
1534 msng_mnfst_set = {}
1534 msng_mnfst_set = {}
1535 # Nor do we know which filenodes are missing.
1535 # Nor do we know which filenodes are missing.
1536 msng_filenode_set = {}
1536 msng_filenode_set = {}
1537
1537
1538 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1538 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1539 junk = None
1539 junk = None
1540
1540
1541 # A changeset always belongs to itself, so the changenode lookup
1541 # A changeset always belongs to itself, so the changenode lookup
1542 # function for a changenode is identity.
1542 # function for a changenode is identity.
1543 def identity(x):
1543 def identity(x):
1544 return x
1544 return x
1545
1545
1546 # A function generating function. Sets up an environment for the
1546 # A function generating function. Sets up an environment for the
1547 # inner function.
1547 # inner function.
1548 def cmp_by_rev_func(revlog):
1548 def cmp_by_rev_func(revlog):
1549 # Compare two nodes by their revision number in the environment's
1549 # Compare two nodes by their revision number in the environment's
1550 # revision history. Since the revision number both represents the
1550 # revision history. Since the revision number both represents the
1551 # most efficient order to read the nodes in, and represents a
1551 # most efficient order to read the nodes in, and represents a
1552 # topological sorting of the nodes, this function is often useful.
1552 # topological sorting of the nodes, this function is often useful.
1553 def cmp_by_rev(a, b):
1553 def cmp_by_rev(a, b):
1554 return cmp(revlog.rev(a), revlog.rev(b))
1554 return cmp(revlog.rev(a), revlog.rev(b))
1555 return cmp_by_rev
1555 return cmp_by_rev
1556
1556
1557 # If we determine that a particular file or manifest node must be a
1557 # If we determine that a particular file or manifest node must be a
1558 # node that the recipient of the changegroup will already have, we can
1558 # node that the recipient of the changegroup will already have, we can
1559 # also assume the recipient will have all the parents. This function
1559 # also assume the recipient will have all the parents. This function
1560 # prunes them from the set of missing nodes.
1560 # prunes them from the set of missing nodes.
1561 def prune_parents(revlog, hasset, msngset):
1561 def prune_parents(revlog, hasset, msngset):
1562 haslst = hasset.keys()
1562 haslst = hasset.keys()
1563 haslst.sort(cmp_by_rev_func(revlog))
1563 haslst.sort(cmp_by_rev_func(revlog))
1564 for node in haslst:
1564 for node in haslst:
1565 parentlst = [p for p in revlog.parents(node) if p != nullid]
1565 parentlst = [p for p in revlog.parents(node) if p != nullid]
1566 while parentlst:
1566 while parentlst:
1567 n = parentlst.pop()
1567 n = parentlst.pop()
1568 if n not in hasset:
1568 if n not in hasset:
1569 hasset[n] = 1
1569 hasset[n] = 1
1570 p = [p for p in revlog.parents(n) if p != nullid]
1570 p = [p for p in revlog.parents(n) if p != nullid]
1571 parentlst.extend(p)
1571 parentlst.extend(p)
1572 for n in hasset:
1572 for n in hasset:
1573 msngset.pop(n, None)
1573 msngset.pop(n, None)
1574
1574
1575 # This is a function generating function used to set up an environment
1575 # This is a function generating function used to set up an environment
1576 # for the inner function to execute in.
1576 # for the inner function to execute in.
1577 def manifest_and_file_collector(changedfileset):
1577 def manifest_and_file_collector(changedfileset):
1578 # This is an information gathering function that gathers
1578 # This is an information gathering function that gathers
1579 # information from each changeset node that goes out as part of
1579 # information from each changeset node that goes out as part of
1580 # the changegroup. The information gathered is a list of which
1580 # the changegroup. The information gathered is a list of which
1581 # manifest nodes are potentially required (the recipient may
1581 # manifest nodes are potentially required (the recipient may
1582 # already have them) and total list of all files which were
1582 # already have them) and total list of all files which were
1583 # changed in any changeset in the changegroup.
1583 # changed in any changeset in the changegroup.
1584 #
1584 #
1585 # We also remember the first changenode we saw any manifest
1585 # We also remember the first changenode we saw any manifest
1586 # referenced by so we can later determine which changenode 'owns'
1586 # referenced by so we can later determine which changenode 'owns'
1587 # the manifest.
1587 # the manifest.
1588 def collect_manifests_and_files(clnode):
1588 def collect_manifests_and_files(clnode):
1589 c = cl.read(clnode)
1589 c = cl.read(clnode)
1590 for f in c[3]:
1590 for f in c[3]:
1591 # This is to make sure we only have one instance of each
1591 # This is to make sure we only have one instance of each
1592 # filename string for each filename.
1592 # filename string for each filename.
1593 changedfileset.setdefault(f, f)
1593 changedfileset.setdefault(f, f)
1594 msng_mnfst_set.setdefault(c[0], clnode)
1594 msng_mnfst_set.setdefault(c[0], clnode)
1595 return collect_manifests_and_files
1595 return collect_manifests_and_files
1596
1596
1597 # Figure out which manifest nodes (of the ones we think might be part
1597 # Figure out which manifest nodes (of the ones we think might be part
1598 # of the changegroup) the recipient must know about and remove them
1598 # of the changegroup) the recipient must know about and remove them
1599 # from the changegroup.
1599 # from the changegroup.
1600 def prune_manifests():
1600 def prune_manifests():
1601 has_mnfst_set = {}
1601 has_mnfst_set = {}
1602 for n in msng_mnfst_set:
1602 for n in msng_mnfst_set:
1603 # If a 'missing' manifest thinks it belongs to a changenode
1603 # If a 'missing' manifest thinks it belongs to a changenode
1604 # the recipient is assumed to have, obviously the recipient
1604 # the recipient is assumed to have, obviously the recipient
1605 # must have that manifest.
1605 # must have that manifest.
1606 linknode = cl.node(mnfst.linkrev(n))
1606 linknode = cl.node(mnfst.linkrev(n))
1607 if linknode in has_cl_set:
1607 if linknode in has_cl_set:
1608 has_mnfst_set[n] = 1
1608 has_mnfst_set[n] = 1
1609 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1609 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1610
1610
1611 # Use the information collected in collect_manifests_and_files to say
1611 # Use the information collected in collect_manifests_and_files to say
1612 # which changenode any manifestnode belongs to.
1612 # which changenode any manifestnode belongs to.
1613 def lookup_manifest_link(mnfstnode):
1613 def lookup_manifest_link(mnfstnode):
1614 return msng_mnfst_set[mnfstnode]
1614 return msng_mnfst_set[mnfstnode]
1615
1615
1616 # A function generating function that sets up the initial environment
1616 # A function generating function that sets up the initial environment
1617 # the inner function.
1617 # the inner function.
1618 def filenode_collector(changedfiles):
1618 def filenode_collector(changedfiles):
1619 next_rev = [0]
1619 next_rev = [0]
1620 # This gathers information from each manifestnode included in the
1620 # This gathers information from each manifestnode included in the
1621 # changegroup about which filenodes the manifest node references
1621 # changegroup about which filenodes the manifest node references
1622 # so we can include those in the changegroup too.
1622 # so we can include those in the changegroup too.
1623 #
1623 #
1624 # It also remembers which changenode each filenode belongs to. It
1624 # It also remembers which changenode each filenode belongs to. It
1625 # does this by assuming the a filenode belongs to the changenode
1625 # does this by assuming the a filenode belongs to the changenode
1626 # the first manifest that references it belongs to.
1626 # the first manifest that references it belongs to.
1627 def collect_msng_filenodes(mnfstnode):
1627 def collect_msng_filenodes(mnfstnode):
1628 r = mnfst.rev(mnfstnode)
1628 r = mnfst.rev(mnfstnode)
1629 if r == next_rev[0]:
1629 if r == next_rev[0]:
1630 # If the last rev we looked at was the one just previous,
1630 # If the last rev we looked at was the one just previous,
1631 # we only need to see a diff.
1631 # we only need to see a diff.
1632 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1632 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1633 # For each line in the delta
1633 # For each line in the delta
1634 for dline in delta.splitlines():
1634 for dline in delta.splitlines():
1635 # get the filename and filenode for that line
1635 # get the filename and filenode for that line
1636 f, fnode = dline.split('\0')
1636 f, fnode = dline.split('\0')
1637 fnode = bin(fnode[:40])
1637 fnode = bin(fnode[:40])
1638 f = changedfiles.get(f, None)
1638 f = changedfiles.get(f, None)
1639 # And if the file is in the list of files we care
1639 # And if the file is in the list of files we care
1640 # about.
1640 # about.
1641 if f is not None:
1641 if f is not None:
1642 # Get the changenode this manifest belongs to
1642 # Get the changenode this manifest belongs to
1643 clnode = msng_mnfst_set[mnfstnode]
1643 clnode = msng_mnfst_set[mnfstnode]
1644 # Create the set of filenodes for the file if
1644 # Create the set of filenodes for the file if
1645 # there isn't one already.
1645 # there isn't one already.
1646 ndset = msng_filenode_set.setdefault(f, {})
1646 ndset = msng_filenode_set.setdefault(f, {})
1647 # And set the filenode's changelog node to the
1647 # And set the filenode's changelog node to the
1648 # manifest's if it hasn't been set already.
1648 # manifest's if it hasn't been set already.
1649 ndset.setdefault(fnode, clnode)
1649 ndset.setdefault(fnode, clnode)
1650 else:
1650 else:
1651 # Otherwise we need a full manifest.
1651 # Otherwise we need a full manifest.
1652 m = mnfst.read(mnfstnode)
1652 m = mnfst.read(mnfstnode)
1653 # For every file in we care about.
1653 # For every file in we care about.
1654 for f in changedfiles:
1654 for f in changedfiles:
1655 fnode = m.get(f, None)
1655 fnode = m.get(f, None)
1656 # If it's in the manifest
1656 # If it's in the manifest
1657 if fnode is not None:
1657 if fnode is not None:
1658 # See comments above.
1658 # See comments above.
1659 clnode = msng_mnfst_set[mnfstnode]
1659 clnode = msng_mnfst_set[mnfstnode]
1660 ndset = msng_filenode_set.setdefault(f, {})
1660 ndset = msng_filenode_set.setdefault(f, {})
1661 ndset.setdefault(fnode, clnode)
1661 ndset.setdefault(fnode, clnode)
1662 # Remember the revision we hope to see next.
1662 # Remember the revision we hope to see next.
1663 next_rev[0] = r + 1
1663 next_rev[0] = r + 1
1664 return collect_msng_filenodes
1664 return collect_msng_filenodes
1665
1665
1666 # We have a list of filenodes we think we need for a file, lets remove
1666 # We have a list of filenodes we think we need for a file, lets remove
1667 # all those we now the recipient must have.
1667 # all those we now the recipient must have.
1668 def prune_filenodes(f, filerevlog):
1668 def prune_filenodes(f, filerevlog):
1669 msngset = msng_filenode_set[f]
1669 msngset = msng_filenode_set[f]
1670 hasset = {}
1670 hasset = {}
1671 # If a 'missing' filenode thinks it belongs to a changenode we
1671 # If a 'missing' filenode thinks it belongs to a changenode we
1672 # assume the recipient must have, then the recipient must have
1672 # assume the recipient must have, then the recipient must have
1673 # that filenode.
1673 # that filenode.
1674 for n in msngset:
1674 for n in msngset:
1675 clnode = cl.node(filerevlog.linkrev(n))
1675 clnode = cl.node(filerevlog.linkrev(n))
1676 if clnode in has_cl_set:
1676 if clnode in has_cl_set:
1677 hasset[n] = 1
1677 hasset[n] = 1
1678 prune_parents(filerevlog, hasset, msngset)
1678 prune_parents(filerevlog, hasset, msngset)
1679
1679
1680 # A function generator function that sets up the a context for the
1680 # A function generator function that sets up the a context for the
1681 # inner function.
1681 # inner function.
1682 def lookup_filenode_link_func(fname):
1682 def lookup_filenode_link_func(fname):
1683 msngset = msng_filenode_set[fname]
1683 msngset = msng_filenode_set[fname]
1684 # Lookup the changenode the filenode belongs to.
1684 # Lookup the changenode the filenode belongs to.
1685 def lookup_filenode_link(fnode):
1685 def lookup_filenode_link(fnode):
1686 return msngset[fnode]
1686 return msngset[fnode]
1687 return lookup_filenode_link
1687 return lookup_filenode_link
1688
1688
1689 # Now that we have all theses utility functions to help out and
1689 # Now that we have all theses utility functions to help out and
1690 # logically divide up the task, generate the group.
1690 # logically divide up the task, generate the group.
1691 def gengroup():
1691 def gengroup():
1692 # The set of changed files starts empty.
1692 # The set of changed files starts empty.
1693 changedfiles = {}
1693 changedfiles = {}
1694 # Create a changenode group generator that will call our functions
1694 # Create a changenode group generator that will call our functions
1695 # back to lookup the owning changenode and collect information.
1695 # back to lookup the owning changenode and collect information.
1696 group = cl.group(msng_cl_lst, identity,
1696 group = cl.group(msng_cl_lst, identity,
1697 manifest_and_file_collector(changedfiles))
1697 manifest_and_file_collector(changedfiles))
1698 for chnk in group:
1698 for chnk in group:
1699 yield chnk
1699 yield chnk
1700
1700
1701 # The list of manifests has been collected by the generator
1701 # The list of manifests has been collected by the generator
1702 # calling our functions back.
1702 # calling our functions back.
1703 prune_manifests()
1703 prune_manifests()
1704 msng_mnfst_lst = msng_mnfst_set.keys()
1704 msng_mnfst_lst = msng_mnfst_set.keys()
1705 # Sort the manifestnodes by revision number.
1705 # Sort the manifestnodes by revision number.
1706 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1706 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1707 # Create a generator for the manifestnodes that calls our lookup
1707 # Create a generator for the manifestnodes that calls our lookup
1708 # and data collection functions back.
1708 # and data collection functions back.
1709 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1709 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1710 filenode_collector(changedfiles))
1710 filenode_collector(changedfiles))
1711 for chnk in group:
1711 for chnk in group:
1712 yield chnk
1712 yield chnk
1713
1713
1714 # These are no longer needed, dereference and toss the memory for
1714 # These are no longer needed, dereference and toss the memory for
1715 # them.
1715 # them.
1716 msng_mnfst_lst = None
1716 msng_mnfst_lst = None
1717 msng_mnfst_set.clear()
1717 msng_mnfst_set.clear()
1718
1718
1719 changedfiles = changedfiles.keys()
1719 changedfiles = changedfiles.keys()
1720 changedfiles.sort()
1720 changedfiles.sort()
1721 # Go through all our files in order sorted by name.
1721 # Go through all our files in order sorted by name.
1722 for fname in changedfiles:
1722 for fname in changedfiles:
1723 filerevlog = self.file(fname)
1723 filerevlog = self.file(fname)
1724 # Toss out the filenodes that the recipient isn't really
1724 # Toss out the filenodes that the recipient isn't really
1725 # missing.
1725 # missing.
1726 if msng_filenode_set.has_key(fname):
1726 if msng_filenode_set.has_key(fname):
1727 prune_filenodes(fname, filerevlog)
1727 prune_filenodes(fname, filerevlog)
1728 msng_filenode_lst = msng_filenode_set[fname].keys()
1728 msng_filenode_lst = msng_filenode_set[fname].keys()
1729 else:
1729 else:
1730 msng_filenode_lst = []
1730 msng_filenode_lst = []
1731 # If any filenodes are left, generate the group for them,
1731 # If any filenodes are left, generate the group for them,
1732 # otherwise don't bother.
1732 # otherwise don't bother.
1733 if len(msng_filenode_lst) > 0:
1733 if len(msng_filenode_lst) > 0:
1734 yield changegroup.genchunk(fname)
1734 yield changegroup.genchunk(fname)
1735 # Sort the filenodes by their revision #
1735 # Sort the filenodes by their revision #
1736 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1736 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1737 # Create a group generator and only pass in a changenode
1737 # Create a group generator and only pass in a changenode
1738 # lookup function as we need to collect no information
1738 # lookup function as we need to collect no information
1739 # from filenodes.
1739 # from filenodes.
1740 group = filerevlog.group(msng_filenode_lst,
1740 group = filerevlog.group(msng_filenode_lst,
1741 lookup_filenode_link_func(fname))
1741 lookup_filenode_link_func(fname))
1742 for chnk in group:
1742 for chnk in group:
1743 yield chnk
1743 yield chnk
1744 if msng_filenode_set.has_key(fname):
1744 if msng_filenode_set.has_key(fname):
1745 # Don't need this anymore, toss it to free memory.
1745 # Don't need this anymore, toss it to free memory.
1746 del msng_filenode_set[fname]
1746 del msng_filenode_set[fname]
1747 # Signal that no more groups are left.
1747 # Signal that no more groups are left.
1748 yield changegroup.closechunk()
1748 yield changegroup.closechunk()
1749
1749
1750 if msng_cl_lst:
1750 if msng_cl_lst:
1751 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1751 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1752
1752
1753 return util.chunkbuffer(gengroup())
1753 return util.chunkbuffer(gengroup())
1754
1754
1755 def changegroup(self, basenodes, source):
1755 def changegroup(self, basenodes, source):
1756 """Generate a changegroup of all nodes that we have that a recipient
1756 """Generate a changegroup of all nodes that we have that a recipient
1757 doesn't.
1757 doesn't.
1758
1758
1759 This is much easier than the previous function as we can assume that
1759 This is much easier than the previous function as we can assume that
1760 the recipient has any changenode we aren't sending them."""
1760 the recipient has any changenode we aren't sending them."""
1761
1761
1762 self.hook('preoutgoing', throw=True, source=source)
1762 self.hook('preoutgoing', throw=True, source=source)
1763
1763
1764 cl = self.changelog
1764 cl = self.changelog
1765 nodes = cl.nodesbetween(basenodes, None)[0]
1765 nodes = cl.nodesbetween(basenodes, None)[0]
1766 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1766 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1767 self.changegroupinfo(nodes)
1767 self.changegroupinfo(nodes)
1768
1768
1769 def identity(x):
1769 def identity(x):
1770 return x
1770 return x
1771
1771
1772 def gennodelst(revlog):
1772 def gennodelst(revlog):
1773 for r in xrange(0, revlog.count()):
1773 for r in xrange(0, revlog.count()):
1774 n = revlog.node(r)
1774 n = revlog.node(r)
1775 if revlog.linkrev(n) in revset:
1775 if revlog.linkrev(n) in revset:
1776 yield n
1776 yield n
1777
1777
1778 def changed_file_collector(changedfileset):
1778 def changed_file_collector(changedfileset):
1779 def collect_changed_files(clnode):
1779 def collect_changed_files(clnode):
1780 c = cl.read(clnode)
1780 c = cl.read(clnode)
1781 for fname in c[3]:
1781 for fname in c[3]:
1782 changedfileset[fname] = 1
1782 changedfileset[fname] = 1
1783 return collect_changed_files
1783 return collect_changed_files
1784
1784
1785 def lookuprevlink_func(revlog):
1785 def lookuprevlink_func(revlog):
1786 def lookuprevlink(n):
1786 def lookuprevlink(n):
1787 return cl.node(revlog.linkrev(n))
1787 return cl.node(revlog.linkrev(n))
1788 return lookuprevlink
1788 return lookuprevlink
1789
1789
1790 def gengroup():
1790 def gengroup():
1791 # construct a list of all changed files
1791 # construct a list of all changed files
1792 changedfiles = {}
1792 changedfiles = {}
1793
1793
1794 for chnk in cl.group(nodes, identity,
1794 for chnk in cl.group(nodes, identity,
1795 changed_file_collector(changedfiles)):
1795 changed_file_collector(changedfiles)):
1796 yield chnk
1796 yield chnk
1797 changedfiles = changedfiles.keys()
1797 changedfiles = changedfiles.keys()
1798 changedfiles.sort()
1798 changedfiles.sort()
1799
1799
1800 mnfst = self.manifest
1800 mnfst = self.manifest
1801 nodeiter = gennodelst(mnfst)
1801 nodeiter = gennodelst(mnfst)
1802 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1802 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1803 yield chnk
1803 yield chnk
1804
1804
1805 for fname in changedfiles:
1805 for fname in changedfiles:
1806 filerevlog = self.file(fname)
1806 filerevlog = self.file(fname)
1807 nodeiter = gennodelst(filerevlog)
1807 nodeiter = gennodelst(filerevlog)
1808 nodeiter = list(nodeiter)
1808 nodeiter = list(nodeiter)
1809 if nodeiter:
1809 if nodeiter:
1810 yield changegroup.genchunk(fname)
1810 yield changegroup.genchunk(fname)
1811 lookup = lookuprevlink_func(filerevlog)
1811 lookup = lookuprevlink_func(filerevlog)
1812 for chnk in filerevlog.group(nodeiter, lookup):
1812 for chnk in filerevlog.group(nodeiter, lookup):
1813 yield chnk
1813 yield chnk
1814
1814
1815 yield changegroup.closechunk()
1815 yield changegroup.closechunk()
1816
1816
1817 if nodes:
1817 if nodes:
1818 self.hook('outgoing', node=hex(nodes[0]), source=source)
1818 self.hook('outgoing', node=hex(nodes[0]), source=source)
1819
1819
1820 return util.chunkbuffer(gengroup())
1820 return util.chunkbuffer(gengroup())
1821
1821
1822 def addchangegroup(self, source, srctype, url):
1822 def addchangegroup(self, source, srctype, url):
1823 """add changegroup to repo.
1823 """add changegroup to repo.
1824
1824
1825 return values:
1825 return values:
1826 - nothing changed or no source: 0
1826 - nothing changed or no source: 0
1827 - more heads than before: 1+added heads (2..n)
1827 - more heads than before: 1+added heads (2..n)
1828 - less heads than before: -1-removed heads (-2..-n)
1828 - less heads than before: -1-removed heads (-2..-n)
1829 - number of heads stays the same: 1
1829 - number of heads stays the same: 1
1830 """
1830 """
1831 def csmap(x):
1831 def csmap(x):
1832 self.ui.debug(_("add changeset %s\n") % short(x))
1832 self.ui.debug(_("add changeset %s\n") % short(x))
1833 return cl.count()
1833 return cl.count()
1834
1834
1835 def revmap(x):
1835 def revmap(x):
1836 return cl.rev(x)
1836 return cl.rev(x)
1837
1837
1838 if not source:
1838 if not source:
1839 return 0
1839 return 0
1840
1840
1841 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1841 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1842
1842
1843 changesets = files = revisions = 0
1843 changesets = files = revisions = 0
1844
1844
1845 tr = self.transaction()
1845 tr = self.transaction()
1846
1846
1847 # write changelog data to temp files so concurrent readers will not see
1847 # write changelog data to temp files so concurrent readers will not see
1848 # inconsistent view
1848 # inconsistent view
1849 cl = None
1849 cl = None
1850 try:
1850 try:
1851 cl = appendfile.appendchangelog(self.sopener,
1851 cl = appendfile.appendchangelog(self.sopener,
1852 self.changelog.version)
1852 self.changelog.version)
1853
1853
1854 oldheads = len(cl.heads())
1854 oldheads = len(cl.heads())
1855
1855
1856 # pull off the changeset group
1856 # pull off the changeset group
1857 self.ui.status(_("adding changesets\n"))
1857 self.ui.status(_("adding changesets\n"))
1858 cor = cl.count() - 1
1858 cor = cl.count() - 1
1859 chunkiter = changegroup.chunkiter(source)
1859 chunkiter = changegroup.chunkiter(source)
1860 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1860 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1861 raise util.Abort(_("received changelog group is empty"))
1861 raise util.Abort(_("received changelog group is empty"))
1862 cnr = cl.count() - 1
1862 cnr = cl.count() - 1
1863 changesets = cnr - cor
1863 changesets = cnr - cor
1864
1864
1865 # pull off the manifest group
1865 # pull off the manifest group
1866 self.ui.status(_("adding manifests\n"))
1866 self.ui.status(_("adding manifests\n"))
1867 chunkiter = changegroup.chunkiter(source)
1867 chunkiter = changegroup.chunkiter(source)
1868 # no need to check for empty manifest group here:
1868 # no need to check for empty manifest group here:
1869 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1869 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1870 # no new manifest will be created and the manifest group will
1870 # no new manifest will be created and the manifest group will
1871 # be empty during the pull
1871 # be empty during the pull
1872 self.manifest.addgroup(chunkiter, revmap, tr)
1872 self.manifest.addgroup(chunkiter, revmap, tr)
1873
1873
1874 # process the files
1874 # process the files
1875 self.ui.status(_("adding file changes\n"))
1875 self.ui.status(_("adding file changes\n"))
1876 while 1:
1876 while 1:
1877 f = changegroup.getchunk(source)
1877 f = changegroup.getchunk(source)
1878 if not f:
1878 if not f:
1879 break
1879 break
1880 self.ui.debug(_("adding %s revisions\n") % f)
1880 self.ui.debug(_("adding %s revisions\n") % f)
1881 fl = self.file(f)
1881 fl = self.file(f)
1882 o = fl.count()
1882 o = fl.count()
1883 chunkiter = changegroup.chunkiter(source)
1883 chunkiter = changegroup.chunkiter(source)
1884 if fl.addgroup(chunkiter, revmap, tr) is None:
1884 if fl.addgroup(chunkiter, revmap, tr) is None:
1885 raise util.Abort(_("received file revlog group is empty"))
1885 raise util.Abort(_("received file revlog group is empty"))
1886 revisions += fl.count() - o
1886 revisions += fl.count() - o
1887 files += 1
1887 files += 1
1888
1888
1889 cl.writedata()
1889 cl.writedata()
1890 finally:
1890 finally:
1891 if cl:
1891 if cl:
1892 cl.cleanup()
1892 cl.cleanup()
1893
1893
1894 # make changelog see real files again
1894 # make changelog see real files again
1895 self.changelog = changelog.changelog(self.sopener,
1895 self.changelog = changelog.changelog(self.sopener,
1896 self.changelog.version)
1896 self.changelog.version)
1897 self.changelog.checkinlinesize(tr)
1897 self.changelog.checkinlinesize(tr)
1898
1898
1899 newheads = len(self.changelog.heads())
1899 newheads = len(self.changelog.heads())
1900 heads = ""
1900 heads = ""
1901 if oldheads and newheads != oldheads:
1901 if oldheads and newheads != oldheads:
1902 heads = _(" (%+d heads)") % (newheads - oldheads)
1902 heads = _(" (%+d heads)") % (newheads - oldheads)
1903
1903
1904 self.ui.status(_("added %d changesets"
1904 self.ui.status(_("added %d changesets"
1905 " with %d changes to %d files%s\n")
1905 " with %d changes to %d files%s\n")
1906 % (changesets, revisions, files, heads))
1906 % (changesets, revisions, files, heads))
1907
1907
1908 if changesets > 0:
1908 if changesets > 0:
1909 self.hook('pretxnchangegroup', throw=True,
1909 self.hook('pretxnchangegroup', throw=True,
1910 node=hex(self.changelog.node(cor+1)), source=srctype,
1910 node=hex(self.changelog.node(cor+1)), source=srctype,
1911 url=url)
1911 url=url)
1912
1912
1913 tr.close()
1913 tr.close()
1914
1914
1915 if changesets > 0:
1915 if changesets > 0:
1916 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1916 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1917 source=srctype, url=url)
1917 source=srctype, url=url)
1918
1918
1919 for i in xrange(cor + 1, cnr + 1):
1919 for i in xrange(cor + 1, cnr + 1):
1920 self.hook("incoming", node=hex(self.changelog.node(i)),
1920 self.hook("incoming", node=hex(self.changelog.node(i)),
1921 source=srctype, url=url)
1921 source=srctype, url=url)
1922
1922
1923 # never return 0 here:
1923 # never return 0 here:
1924 if newheads < oldheads:
1924 if newheads < oldheads:
1925 return newheads - oldheads - 1
1925 return newheads - oldheads - 1
1926 else:
1926 else:
1927 return newheads - oldheads + 1
1927 return newheads - oldheads + 1
1928
1928
1929
1929
1930 def stream_in(self, remote):
1930 def stream_in(self, remote):
1931 fp = remote.stream_out()
1931 fp = remote.stream_out()
1932 l = fp.readline()
1932 l = fp.readline()
1933 try:
1933 try:
1934 resp = int(l)
1934 resp = int(l)
1935 except ValueError:
1935 except ValueError:
1936 raise util.UnexpectedOutput(
1936 raise util.UnexpectedOutput(
1937 _('Unexpected response from remote server:'), l)
1937 _('Unexpected response from remote server:'), l)
1938 if resp == 1:
1938 if resp == 1:
1939 raise util.Abort(_('operation forbidden by server'))
1939 raise util.Abort(_('operation forbidden by server'))
1940 elif resp == 2:
1940 elif resp == 2:
1941 raise util.Abort(_('locking the remote repository failed'))
1941 raise util.Abort(_('locking the remote repository failed'))
1942 elif resp != 0:
1942 elif resp != 0:
1943 raise util.Abort(_('the server sent an unknown error code'))
1943 raise util.Abort(_('the server sent an unknown error code'))
1944 self.ui.status(_('streaming all changes\n'))
1944 self.ui.status(_('streaming all changes\n'))
1945 l = fp.readline()
1945 l = fp.readline()
1946 try:
1946 try:
1947 total_files, total_bytes = map(int, l.split(' ', 1))
1947 total_files, total_bytes = map(int, l.split(' ', 1))
1948 except ValueError, TypeError:
1948 except ValueError, TypeError:
1949 raise util.UnexpectedOutput(
1949 raise util.UnexpectedOutput(
1950 _('Unexpected response from remote server:'), l)
1950 _('Unexpected response from remote server:'), l)
1951 self.ui.status(_('%d files to transfer, %s of data\n') %
1951 self.ui.status(_('%d files to transfer, %s of data\n') %
1952 (total_files, util.bytecount(total_bytes)))
1952 (total_files, util.bytecount(total_bytes)))
1953 start = time.time()
1953 start = time.time()
1954 for i in xrange(total_files):
1954 for i in xrange(total_files):
1955 # XXX doesn't support '\n' or '\r' in filenames
1955 # XXX doesn't support '\n' or '\r' in filenames
1956 l = fp.readline()
1956 l = fp.readline()
1957 try:
1957 try:
1958 name, size = l.split('\0', 1)
1958 name, size = l.split('\0', 1)
1959 size = int(size)
1959 size = int(size)
1960 except ValueError, TypeError:
1960 except ValueError, TypeError:
1961 raise util.UnexpectedOutput(
1961 raise util.UnexpectedOutput(
1962 _('Unexpected response from remote server:'), l)
1962 _('Unexpected response from remote server:'), l)
1963 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1963 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
1964 ofp = self.sopener(name, 'w')
1964 ofp = self.sopener(name, 'w')
1965 for chunk in util.filechunkiter(fp, limit=size):
1965 for chunk in util.filechunkiter(fp, limit=size):
1966 ofp.write(chunk)
1966 ofp.write(chunk)
1967 ofp.close()
1967 ofp.close()
1968 elapsed = time.time() - start
1968 elapsed = time.time() - start
1969 if elapsed <= 0:
1969 if elapsed <= 0:
1970 elapsed = 0.001
1970 elapsed = 0.001
1971 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1971 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
1972 (util.bytecount(total_bytes), elapsed,
1972 (util.bytecount(total_bytes), elapsed,
1973 util.bytecount(total_bytes / elapsed)))
1973 util.bytecount(total_bytes / elapsed)))
1974 self.reload()
1974 self.reload()
1975 return len(self.heads()) + 1
1975 return len(self.heads()) + 1
1976
1976
1977 def clone(self, remote, heads=[], stream=False):
1977 def clone(self, remote, heads=[], stream=False):
1978 '''clone remote repository.
1978 '''clone remote repository.
1979
1979
1980 keyword arguments:
1980 keyword arguments:
1981 heads: list of revs to clone (forces use of pull)
1981 heads: list of revs to clone (forces use of pull)
1982 stream: use streaming clone if possible'''
1982 stream: use streaming clone if possible'''
1983
1983
1984 # now, all clients that can request uncompressed clones can
1984 # now, all clients that can request uncompressed clones can
1985 # read repo formats supported by all servers that can serve
1985 # read repo formats supported by all servers that can serve
1986 # them.
1986 # them.
1987
1987
1988 # if revlog format changes, client will have to check version
1988 # if revlog format changes, client will have to check version
1989 # and format flags on "stream" capability, and use
1989 # and format flags on "stream" capability, and use
1990 # uncompressed only if compatible.
1990 # uncompressed only if compatible.
1991
1991
1992 if stream and not heads and remote.capable('stream'):
1992 if stream and not heads and remote.capable('stream'):
1993 return self.stream_in(remote)
1993 return self.stream_in(remote)
1994 return self.pull(remote, heads)
1994 return self.pull(remote, heads)
1995
1995
1996 # used to avoid circular references so destructors work
1996 # used to avoid circular references so destructors work
1997 def aftertrans(files):
1997 def aftertrans(files):
1998 renamefiles = [tuple(t) for t in files]
1998 renamefiles = [tuple(t) for t in files]
1999 def a():
1999 def a():
2000 for src, dest in renamefiles:
2000 for src, dest in renamefiles:
2001 util.rename(src, dest)
2001 util.rename(src, dest)
2002 return a
2002 return a
2003
2003
2004 def instance(ui, path, create):
2004 def instance(ui, path, create):
2005 return localrepository(ui, util.drop_scheme('file', path), create)
2005 return localrepository(ui, util.drop_scheme('file', path), create)
2006
2006
2007 def islocal(path):
2007 def islocal(path):
2008 return True
2008 return True
@@ -1,172 +1,172 b''
1 adding changesets
1 adding changesets
2 adding manifests
2 adding manifests
3 adding file changes
3 adding file changes
4 added 2 changesets with 2 changes to 1 files
4 added 2 changesets with 2 changes to 1 files
5 (run 'hg update' to get a working copy)
5 (run 'hg update' to get a working copy)
6 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
6 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
7 % should fail with encoding error
7 % should fail with encoding error
8 M a
8 M a
9 ? latin-1
9 ? latin-1
10 ? latin-1-tag
10 ? latin-1-tag
11 ? utf-8
11 ? utf-8
12 abort: decoding near ' encoded: οΏ½': 'ascii' codec can't decode byte 0xe9 in position 20: ordinal not in range(128)!
12 abort: decoding near ' encoded: οΏ½': 'ascii' codec can't decode byte 0xe9 in position 20: ordinal not in range(128)!
13 transaction abort!
13 transaction abort!
14 rollback completed
14 rollback completed
15 % these should work
15 % these should work
16 % ascii
16 % ascii
17 changeset: 5:db5520b4645f
17 changeset: 5:db5520b4645f
18 branch: ?
18 branch: ?
19 tag: tip
19 tag: tip
20 user: test
20 user: test
21 date: Mon Jan 12 13:46:40 1970 +0000
21 date: Mon Jan 12 13:46:40 1970 +0000
22 summary: latin1 branch
22 summary: latin1 branch
23
23
24 changeset: 4:9cff3c980b58
24 changeset: 4:9cff3c980b58
25 user: test
25 user: test
26 date: Mon Jan 12 13:46:40 1970 +0000
26 date: Mon Jan 12 13:46:40 1970 +0000
27 summary: Added tag ? for changeset 770b9b11621d
27 summary: Added tag ? for changeset 770b9b11621d
28
28
29 changeset: 3:770b9b11621d
29 changeset: 3:770b9b11621d
30 tag: ?
30 tag: ?
31 user: test
31 user: test
32 date: Mon Jan 12 13:46:40 1970 +0000
32 date: Mon Jan 12 13:46:40 1970 +0000
33 summary: utf-8 e' encoded: ?
33 summary: utf-8 e' encoded: ?
34
34
35 changeset: 2:0572af48b948
35 changeset: 2:0572af48b948
36 user: test
36 user: test
37 date: Mon Jan 12 13:46:40 1970 +0000
37 date: Mon Jan 12 13:46:40 1970 +0000
38 summary: latin-1 e' encoded: ?
38 summary: latin-1 e' encoded: ?
39
39
40 changeset: 1:0e5b7e3f9c4a
40 changeset: 1:0e5b7e3f9c4a
41 user: test
41 user: test
42 date: Mon Jan 12 13:46:40 1970 +0000
42 date: Mon Jan 12 13:46:40 1970 +0000
43 summary: koi8-r: ????? = u'\u0440\u0442\u0443\u0442\u044c'
43 summary: koi8-r: ????? = u'\u0440\u0442\u0443\u0442\u044c'
44
44
45 changeset: 0:1e78a93102a3
45 changeset: 0:1e78a93102a3
46 user: test
46 user: test
47 date: Mon Jan 12 13:46:40 1970 +0000
47 date: Mon Jan 12 13:46:40 1970 +0000
48 summary: latin-1 e': ? = u'\xe9'
48 summary: latin-1 e': ? = u'\xe9'
49
49
50 % latin-1
50 % latin-1
51 changeset: 5:db5520b4645f
51 changeset: 5:db5520b4645f
52 branch: οΏ½
52 branch: οΏ½
53 tag: tip
53 tag: tip
54 user: test
54 user: test
55 date: Mon Jan 12 13:46:40 1970 +0000
55 date: Mon Jan 12 13:46:40 1970 +0000
56 summary: latin1 branch
56 summary: latin1 branch
57
57
58 changeset: 4:9cff3c980b58
58 changeset: 4:9cff3c980b58
59 user: test
59 user: test
60 date: Mon Jan 12 13:46:40 1970 +0000
60 date: Mon Jan 12 13:46:40 1970 +0000
61 summary: Added tag οΏ½ for changeset 770b9b11621d
61 summary: Added tag οΏ½ for changeset 770b9b11621d
62
62
63 changeset: 3:770b9b11621d
63 changeset: 3:770b9b11621d
64 tag: οΏ½
64 tag: οΏ½
65 user: test
65 user: test
66 date: Mon Jan 12 13:46:40 1970 +0000
66 date: Mon Jan 12 13:46:40 1970 +0000
67 summary: utf-8 e' encoded: οΏ½
67 summary: utf-8 e' encoded: οΏ½
68
68
69 changeset: 2:0572af48b948
69 changeset: 2:0572af48b948
70 user: test
70 user: test
71 date: Mon Jan 12 13:46:40 1970 +0000
71 date: Mon Jan 12 13:46:40 1970 +0000
72 summary: latin-1 e' encoded: οΏ½
72 summary: latin-1 e' encoded: οΏ½
73
73
74 changeset: 1:0e5b7e3f9c4a
74 changeset: 1:0e5b7e3f9c4a
75 user: test
75 user: test
76 date: Mon Jan 12 13:46:40 1970 +0000
76 date: Mon Jan 12 13:46:40 1970 +0000
77 summary: koi8-r: οΏ½οΏ½οΏ½οΏ½οΏ½ = u'\u0440\u0442\u0443\u0442\u044c'
77 summary: koi8-r: οΏ½οΏ½οΏ½οΏ½οΏ½ = u'\u0440\u0442\u0443\u0442\u044c'
78
78
79 changeset: 0:1e78a93102a3
79 changeset: 0:1e78a93102a3
80 user: test
80 user: test
81 date: Mon Jan 12 13:46:40 1970 +0000
81 date: Mon Jan 12 13:46:40 1970 +0000
82 summary: latin-1 e': οΏ½ = u'\xe9'
82 summary: latin-1 e': οΏ½ = u'\xe9'
83
83
84 % utf-8
84 % utf-8
85 changeset: 5:db5520b4645f
85 changeset: 5:db5520b4645f
86 branch: Γ©
86 branch: Γ©
87 tag: tip
87 tag: tip
88 user: test
88 user: test
89 date: Mon Jan 12 13:46:40 1970 +0000
89 date: Mon Jan 12 13:46:40 1970 +0000
90 summary: latin1 branch
90 summary: latin1 branch
91
91
92 changeset: 4:9cff3c980b58
92 changeset: 4:9cff3c980b58
93 user: test
93 user: test
94 date: Mon Jan 12 13:46:40 1970 +0000
94 date: Mon Jan 12 13:46:40 1970 +0000
95 summary: Added tag Γ© for changeset 770b9b11621d
95 summary: Added tag Γ© for changeset 770b9b11621d
96
96
97 changeset: 3:770b9b11621d
97 changeset: 3:770b9b11621d
98 tag: Γ©
98 tag: Γ©
99 user: test
99 user: test
100 date: Mon Jan 12 13:46:40 1970 +0000
100 date: Mon Jan 12 13:46:40 1970 +0000
101 summary: utf-8 e' encoded: Γ©
101 summary: utf-8 e' encoded: Γ©
102
102
103 changeset: 2:0572af48b948
103 changeset: 2:0572af48b948
104 user: test
104 user: test
105 date: Mon Jan 12 13:46:40 1970 +0000
105 date: Mon Jan 12 13:46:40 1970 +0000
106 summary: latin-1 e' encoded: Γ©
106 summary: latin-1 e' encoded: Γ©
107
107
108 changeset: 1:0e5b7e3f9c4a
108 changeset: 1:0e5b7e3f9c4a
109 user: test
109 user: test
110 date: Mon Jan 12 13:46:40 1970 +0000
110 date: Mon Jan 12 13:46:40 1970 +0000
111 summary: koi8-r: Γ’Γ”Γ•Γ”Γ˜ = u'\u0440\u0442\u0443\u0442\u044c'
111 summary: koi8-r: Γ’Γ”Γ•Γ”Γ˜ = u'\u0440\u0442\u0443\u0442\u044c'
112
112
113 changeset: 0:1e78a93102a3
113 changeset: 0:1e78a93102a3
114 user: test
114 user: test
115 date: Mon Jan 12 13:46:40 1970 +0000
115 date: Mon Jan 12 13:46:40 1970 +0000
116 summary: latin-1 e': Γ© = u'\xe9'
116 summary: latin-1 e': Γ© = u'\xe9'
117
117
118 % ascii
118 % ascii
119 tip 5:db5520b4645f
119 tip 5:db5520b4645f
120 ? 3:770b9b11621d
120 ? 3:770b9b11621d
121 % latin-1
121 % latin-1
122 tip 5:db5520b4645f
122 tip 5:db5520b4645f
123 οΏ½ 3:770b9b11621d
123 οΏ½ 3:770b9b11621d
124 % utf-8
124 % utf-8
125 tip 5:db5520b4645f
125 tip 5:db5520b4645f
126 Γ© 3:770b9b11621d
126 Γ© 3:770b9b11621d
127 % ascii
127 % ascii
128 ? 5:db5520b4645f
128 ? 5:db5520b4645f
129 4:9cff3c980b58
129 default 4:9cff3c980b58
130 % latin-1
130 % latin-1
131 οΏ½ 5:db5520b4645f
131 οΏ½ 5:db5520b4645f
132 4:9cff3c980b58
132 default 4:9cff3c980b58
133 % utf-8
133 % utf-8
134 Γ© 5:db5520b4645f
134 Γ© 5:db5520b4645f
135 4:9cff3c980b58
135 default 4:9cff3c980b58
136 % utf-8
136 % utf-8
137 changeset: 5:db5520b4645f
137 changeset: 5:db5520b4645f
138 branch: Γ©
138 branch: Γ©
139 tag: tip
139 tag: tip
140 user: test
140 user: test
141 date: Mon Jan 12 13:46:40 1970 +0000
141 date: Mon Jan 12 13:46:40 1970 +0000
142 summary: latin1 branch
142 summary: latin1 branch
143
143
144 changeset: 4:9cff3c980b58
144 changeset: 4:9cff3c980b58
145 user: test
145 user: test
146 date: Mon Jan 12 13:46:40 1970 +0000
146 date: Mon Jan 12 13:46:40 1970 +0000
147 summary: Added tag Γ© for changeset 770b9b11621d
147 summary: Added tag Γ© for changeset 770b9b11621d
148
148
149 changeset: 3:770b9b11621d
149 changeset: 3:770b9b11621d
150 tag: Γ©
150 tag: Γ©
151 user: test
151 user: test
152 date: Mon Jan 12 13:46:40 1970 +0000
152 date: Mon Jan 12 13:46:40 1970 +0000
153 summary: utf-8 e' encoded: Γ©
153 summary: utf-8 e' encoded: Γ©
154
154
155 changeset: 2:0572af48b948
155 changeset: 2:0572af48b948
156 user: test
156 user: test
157 date: Mon Jan 12 13:46:40 1970 +0000
157 date: Mon Jan 12 13:46:40 1970 +0000
158 summary: latin-1 e' encoded: Γ©
158 summary: latin-1 e' encoded: Γ©
159
159
160 changeset: 1:0e5b7e3f9c4a
160 changeset: 1:0e5b7e3f9c4a
161 user: test
161 user: test
162 date: Mon Jan 12 13:46:40 1970 +0000
162 date: Mon Jan 12 13:46:40 1970 +0000
163 summary: koi8-r: Ρ€Ρ‚ΡƒΡ‚ΡŒ = u'\u0440\u0442\u0443\u0442\u044c'
163 summary: koi8-r: Ρ€Ρ‚ΡƒΡ‚ΡŒ = u'\u0440\u0442\u0443\u0442\u044c'
164
164
165 changeset: 0:1e78a93102a3
165 changeset: 0:1e78a93102a3
166 user: test
166 user: test
167 date: Mon Jan 12 13:46:40 1970 +0000
167 date: Mon Jan 12 13:46:40 1970 +0000
168 summary: latin-1 e': И = u'\xe9'
168 summary: latin-1 e': И = u'\xe9'
169
169
170 abort: unknown encoding: dolphin, please check your locale settings
170 abort: unknown encoding: dolphin, please check your locale settings
171 abort: decoding near 'οΏ½': 'ascii' codec can't decode byte 0xe9 in position 0: ordinal not in range(128)!
171 abort: decoding near 'οΏ½': 'ascii' codec can't decode byte 0xe9 in position 0: ordinal not in range(128)!
172 abort: branch name not in UTF-8!
172 abort: branch name not in UTF-8!
@@ -1,216 +1,218 b''
1 adding a
1 adding a
2 adding b
2 adding b
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 pulling from ../b
4 pulling from ../b
5 searching for changes
5 searching for changes
6 warning: repository is unrelated
6 warning: repository is unrelated
7 adding changesets
7 adding changesets
8 adding manifests
8 adding manifests
9 adding file changes
9 adding file changes
10 added 1 changesets with 1 changes to 1 files (+1 heads)
10 added 1 changesets with 1 changes to 1 files (+1 heads)
11 (run 'hg heads' to see heads, 'hg merge' to merge)
11 (run 'hg heads' to see heads, 'hg merge' to merge)
12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 (branch merge, don't forget to commit)
13 (branch merge, don't forget to commit)
14 %% -R/--repository
14 %% -R/--repository
15 changeset: 0:8580ff50825a
15 changeset: 0:8580ff50825a
16 tag: tip
16 tag: tip
17 user: test
17 user: test
18 date: Thu Jan 01 00:00:01 1970 +0000
18 date: Thu Jan 01 00:00:01 1970 +0000
19 summary: a
19 summary: a
20
20
21 changeset: 0:b6c483daf290
21 changeset: 0:b6c483daf290
22 tag: tip
22 tag: tip
23 user: test
23 user: test
24 date: Thu Jan 01 00:00:01 1970 +0000
24 date: Thu Jan 01 00:00:01 1970 +0000
25 summary: b
25 summary: b
26
26
27 %% abbrev of long option
27 %% abbrev of long option
28 changeset: 1:b6c483daf290
28 changeset: 1:b6c483daf290
29 tag: tip
29 tag: tip
30 parent: -1:000000000000
30 parent: -1:000000000000
31 user: test
31 user: test
32 date: Thu Jan 01 00:00:01 1970 +0000
32 date: Thu Jan 01 00:00:01 1970 +0000
33 summary: b
33 summary: b
34
34
35 %% --cwd
35 %% --cwd
36 changeset: 0:8580ff50825a
36 changeset: 0:8580ff50825a
37 tag: tip
37 tag: tip
38 user: test
38 user: test
39 date: Thu Jan 01 00:00:01 1970 +0000
39 date: Thu Jan 01 00:00:01 1970 +0000
40 summary: a
40 summary: a
41
41
42 %% -y/--noninteractive - just be sure it is parsed
42 %% -y/--noninteractive - just be sure it is parsed
43 0:8580ff50825a
43 0:8580ff50825a
44 0:8580ff50825a
44 0:8580ff50825a
45 %% -q/--quiet
45 %% -q/--quiet
46 0:8580ff50825a
46 0:8580ff50825a
47 0:b6c483daf290
47 0:b6c483daf290
48 0:8580ff50825a
48 0:8580ff50825a
49 1:b6c483daf290
49 1:b6c483daf290
50 %% -v/--verbose
50 %% -v/--verbose
51 changeset: 1:b6c483daf290
51 changeset: 1:b6c483daf290
52 tag: tip
52 tag: tip
53 parent: -1:000000000000
53 parent: -1:000000000000
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:01 1970 +0000
55 date: Thu Jan 01 00:00:01 1970 +0000
56 files: b
56 files: b
57 description:
57 description:
58 b
58 b
59
59
60
60
61 changeset: 0:8580ff50825a
61 changeset: 0:8580ff50825a
62 user: test
62 user: test
63 date: Thu Jan 01 00:00:01 1970 +0000
63 date: Thu Jan 01 00:00:01 1970 +0000
64 files: a
64 files: a
65 description:
65 description:
66 a
66 a
67
67
68
68
69 changeset: 0:b6c483daf290
69 changeset: 0:b6c483daf290
70 tag: tip
70 tag: tip
71 user: test
71 user: test
72 date: Thu Jan 01 00:00:01 1970 +0000
72 date: Thu Jan 01 00:00:01 1970 +0000
73 files: b
73 files: b
74 description:
74 description:
75 b
75 b
76
76
77
77
78 %% --config
78 %% --config
79 quuxfoo
79 quuxfoo
80 abort: malformed --config option:
80 abort: malformed --config option:
81 abort: malformed --config option: a.b
81 abort: malformed --config option: a.b
82 abort: malformed --config option: a
82 abort: malformed --config option: a
83 abort: malformed --config option: a.=
83 abort: malformed --config option: a.=
84 abort: malformed --config option: .b=
84 abort: malformed --config option: .b=
85 %% --debug
85 %% --debug
86 changeset: 1:b6c483daf2907ce5825c0bb50f5716226281cc1a
86 changeset: 1:b6c483daf2907ce5825c0bb50f5716226281cc1a
87 tag: tip
87 tag: tip
88 parent: -1:0000000000000000000000000000000000000000
88 parent: -1:0000000000000000000000000000000000000000
89 parent: -1:0000000000000000000000000000000000000000
89 parent: -1:0000000000000000000000000000000000000000
90 manifest: 1:23226e7a252cacdc2d99e4fbdc3653441056de49
90 manifest: 1:23226e7a252cacdc2d99e4fbdc3653441056de49
91 user: test
91 user: test
92 date: Thu Jan 01 00:00:01 1970 +0000
92 date: Thu Jan 01 00:00:01 1970 +0000
93 files+: b
93 files+: b
94 extra: branch=default
94 description:
95 description:
95 b
96 b
96
97
97
98
98 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
99 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
99 parent: -1:0000000000000000000000000000000000000000
100 parent: -1:0000000000000000000000000000000000000000
100 parent: -1:0000000000000000000000000000000000000000
101 parent: -1:0000000000000000000000000000000000000000
101 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
102 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
102 user: test
103 user: test
103 date: Thu Jan 01 00:00:01 1970 +0000
104 date: Thu Jan 01 00:00:01 1970 +0000
104 files+: a
105 files+: a
106 extra: branch=default
105 description:
107 description:
106 a
108 a
107
109
108
110
109 %% --traceback
111 %% --traceback
110 Traceback (most recent call last):
112 Traceback (most recent call last):
111 %% --time
113 %% --time
112 Time: real x.x secs (user x.x+x.x sys x.x+x.x)
114 Time: real x.x secs (user x.x+x.x sys x.x+x.x)
113 %% --version
115 %% --version
114 Mercurial Distributed SCM (version xxx)
116 Mercurial Distributed SCM (version xxx)
115 %% -h/--help
117 %% -h/--help
116 Mercurial Distributed SCM
118 Mercurial Distributed SCM
117
119
118 list of commands (use "hg help -v" to show aliases and global options):
120 list of commands (use "hg help -v" to show aliases and global options):
119
121
120 add add the specified files on the next commit
122 add add the specified files on the next commit
121 addremove add all new files, delete all missing files
123 addremove add all new files, delete all missing files
122 annotate show changeset information per file line
124 annotate show changeset information per file line
123 archive create unversioned archive of a repository revision
125 archive create unversioned archive of a repository revision
124 backout reverse effect of earlier changeset
126 backout reverse effect of earlier changeset
125 branch set or show the current branch name
127 branch set or show the current branch name
126 branches list repository named branches
128 branches list repository named branches
127 bundle create a changegroup file
129 bundle create a changegroup file
128 cat output the current or given revision of files
130 cat output the current or given revision of files
129 clone make a copy of an existing repository
131 clone make a copy of an existing repository
130 commit commit the specified files or all outstanding changes
132 commit commit the specified files or all outstanding changes
131 copy mark files as copied for the next commit
133 copy mark files as copied for the next commit
132 diff diff repository (or selected files)
134 diff diff repository (or selected files)
133 export dump the header and diffs for one or more changesets
135 export dump the header and diffs for one or more changesets
134 grep search for a pattern in specified files and revisions
136 grep search for a pattern in specified files and revisions
135 heads show current repository heads
137 heads show current repository heads
136 help show help for a command, extension, or list of commands
138 help show help for a command, extension, or list of commands
137 identify print information about the working copy
139 identify print information about the working copy
138 import import an ordered set of patches
140 import import an ordered set of patches
139 incoming show new changesets found in source
141 incoming show new changesets found in source
140 init create a new repository in the given directory
142 init create a new repository in the given directory
141 locate locate files matching specific patterns
143 locate locate files matching specific patterns
142 log show revision history of entire repository or files
144 log show revision history of entire repository or files
143 manifest output the current or given revision of the project manifest
145 manifest output the current or given revision of the project manifest
144 merge merge working directory with another revision
146 merge merge working directory with another revision
145 outgoing show changesets not found in destination
147 outgoing show changesets not found in destination
146 parents show the parents of the working dir or revision
148 parents show the parents of the working dir or revision
147 paths show definition of symbolic path names
149 paths show definition of symbolic path names
148 pull pull changes from the specified source
150 pull pull changes from the specified source
149 push push changes to the specified destination
151 push push changes to the specified destination
150 recover roll back an interrupted transaction
152 recover roll back an interrupted transaction
151 remove remove the specified files on the next commit
153 remove remove the specified files on the next commit
152 rename rename files; equivalent of copy + remove
154 rename rename files; equivalent of copy + remove
153 revert revert files or dirs to their states as of some revision
155 revert revert files or dirs to their states as of some revision
154 rollback roll back the last transaction in this repository
156 rollback roll back the last transaction in this repository
155 root print the root (top) of the current working dir
157 root print the root (top) of the current working dir
156 serve export the repository via HTTP
158 serve export the repository via HTTP
157 showconfig show combined config settings from all hgrc files
159 showconfig show combined config settings from all hgrc files
158 status show changed files in the working directory
160 status show changed files in the working directory
159 tag add a tag for the current or given revision
161 tag add a tag for the current or given revision
160 tags list repository tags
162 tags list repository tags
161 tip show the tip revision
163 tip show the tip revision
162 unbundle apply a changegroup file
164 unbundle apply a changegroup file
163 update update working directory
165 update update working directory
164 verify verify the integrity of the repository
166 verify verify the integrity of the repository
165 version output version and copyright information
167 version output version and copyright information
166 Mercurial Distributed SCM
168 Mercurial Distributed SCM
167
169
168 list of commands (use "hg help -v" to show aliases and global options):
170 list of commands (use "hg help -v" to show aliases and global options):
169
171
170 add add the specified files on the next commit
172 add add the specified files on the next commit
171 addremove add all new files, delete all missing files
173 addremove add all new files, delete all missing files
172 annotate show changeset information per file line
174 annotate show changeset information per file line
173 archive create unversioned archive of a repository revision
175 archive create unversioned archive of a repository revision
174 backout reverse effect of earlier changeset
176 backout reverse effect of earlier changeset
175 branch set or show the current branch name
177 branch set or show the current branch name
176 branches list repository named branches
178 branches list repository named branches
177 bundle create a changegroup file
179 bundle create a changegroup file
178 cat output the current or given revision of files
180 cat output the current or given revision of files
179 clone make a copy of an existing repository
181 clone make a copy of an existing repository
180 commit commit the specified files or all outstanding changes
182 commit commit the specified files or all outstanding changes
181 copy mark files as copied for the next commit
183 copy mark files as copied for the next commit
182 diff diff repository (or selected files)
184 diff diff repository (or selected files)
183 export dump the header and diffs for one or more changesets
185 export dump the header and diffs for one or more changesets
184 grep search for a pattern in specified files and revisions
186 grep search for a pattern in specified files and revisions
185 heads show current repository heads
187 heads show current repository heads
186 help show help for a command, extension, or list of commands
188 help show help for a command, extension, or list of commands
187 identify print information about the working copy
189 identify print information about the working copy
188 import import an ordered set of patches
190 import import an ordered set of patches
189 incoming show new changesets found in source
191 incoming show new changesets found in source
190 init create a new repository in the given directory
192 init create a new repository in the given directory
191 locate locate files matching specific patterns
193 locate locate files matching specific patterns
192 log show revision history of entire repository or files
194 log show revision history of entire repository or files
193 manifest output the current or given revision of the project manifest
195 manifest output the current or given revision of the project manifest
194 merge merge working directory with another revision
196 merge merge working directory with another revision
195 outgoing show changesets not found in destination
197 outgoing show changesets not found in destination
196 parents show the parents of the working dir or revision
198 parents show the parents of the working dir or revision
197 paths show definition of symbolic path names
199 paths show definition of symbolic path names
198 pull pull changes from the specified source
200 pull pull changes from the specified source
199 push push changes to the specified destination
201 push push changes to the specified destination
200 recover roll back an interrupted transaction
202 recover roll back an interrupted transaction
201 remove remove the specified files on the next commit
203 remove remove the specified files on the next commit
202 rename rename files; equivalent of copy + remove
204 rename rename files; equivalent of copy + remove
203 revert revert files or dirs to their states as of some revision
205 revert revert files or dirs to their states as of some revision
204 rollback roll back the last transaction in this repository
206 rollback roll back the last transaction in this repository
205 root print the root (top) of the current working dir
207 root print the root (top) of the current working dir
206 serve export the repository via HTTP
208 serve export the repository via HTTP
207 showconfig show combined config settings from all hgrc files
209 showconfig show combined config settings from all hgrc files
208 status show changed files in the working directory
210 status show changed files in the working directory
209 tag add a tag for the current or given revision
211 tag add a tag for the current or given revision
210 tags list repository tags
212 tags list repository tags
211 tip show the tip revision
213 tip show the tip revision
212 unbundle apply a changegroup file
214 unbundle apply a changegroup file
213 update update working directory
215 update update working directory
214 verify verify the integrity of the repository
216 verify verify the integrity of the repository
215 version output version and copyright information
217 version output version and copyright information
216 %% not tested: --debugger
218 %% not tested: --debugger
@@ -1,59 +1,59 b''
1 # mq patch on an empty repo
1 # mq patch on an empty repo
2 tip: 0
2 tip: 0
3 No .hg/branches.cache
3 No .hg/branches.cache
4 tip: 0
4 tip: 0
5 No .hg/branches.cache
5 No .hg/branches.cache
6
6
7 # some regular revisions
7 # some regular revisions
8 Patch queue now empty
8 Patch queue now empty
9 tip: 1
9 tip: 1
10 features: unnamed
10 features: default
11 3f910abad313ff802d3a23a7529433872df9b3ae 1
11 3f910abad313ff802d3a23a7529433872df9b3ae 1
12 3f910abad313ff802d3a23a7529433872df9b3ae bar
12 3f910abad313ff802d3a23a7529433872df9b3ae bar
13 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
13 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
14
14
15 # add some mq patches
15 # add some mq patches
16 applying p1
16 applying p1
17 Now at: p1
17 Now at: p1
18 tip: 2
18 tip: 2
19 features: unnamed
19 features: default
20 3f910abad313ff802d3a23a7529433872df9b3ae 1
20 3f910abad313ff802d3a23a7529433872df9b3ae 1
21 3f910abad313ff802d3a23a7529433872df9b3ae bar
21 3f910abad313ff802d3a23a7529433872df9b3ae bar
22 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
22 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
23 tip: 3
23 tip: 3
24 features: unnamed
24 features: default
25 3f910abad313ff802d3a23a7529433872df9b3ae 1
25 3f910abad313ff802d3a23a7529433872df9b3ae 1
26 3f910abad313ff802d3a23a7529433872df9b3ae bar
26 3f910abad313ff802d3a23a7529433872df9b3ae bar
27 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
27 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
28 branch foo: 3
28 branch foo: 3
29 branch bar: 2
29 branch bar: 2
30
30
31 # removing the cache
31 # removing the cache
32 tip: 3
32 tip: 3
33 features: unnamed
33 features: default
34 3f910abad313ff802d3a23a7529433872df9b3ae 1
34 3f910abad313ff802d3a23a7529433872df9b3ae 1
35 3f910abad313ff802d3a23a7529433872df9b3ae bar
35 3f910abad313ff802d3a23a7529433872df9b3ae bar
36 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
36 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
37 branch foo: 3
37 branch foo: 3
38 branch bar: 2
38 branch bar: 2
39
39
40 # importing rev 1 (the cache now ends in one of the patches)
40 # importing rev 1 (the cache now ends in one of the patches)
41 tip: 3
41 tip: 3
42 features: unnamed
42 features: default
43 3f910abad313ff802d3a23a7529433872df9b3ae 1
43 3f910abad313ff802d3a23a7529433872df9b3ae 1
44 3f910abad313ff802d3a23a7529433872df9b3ae bar
44 3f910abad313ff802d3a23a7529433872df9b3ae bar
45 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
45 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
46 branch foo: 3
46 branch foo: 3
47 branch bar: 2
47 branch bar: 2
48 qbase: 1
48 qbase: 1
49
49
50 # detect an invalid cache
50 # detect an invalid cache
51 Patch queue now empty
51 Patch queue now empty
52 applying p0
52 applying p0
53 applying p1
53 applying p1
54 applying p2
54 applying p2
55 Now at: p2
55 Now at: p2
56 tip: 3
56 tip: 3
57 features: unnamed
57 features: default
58 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff 0
58 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff 0
59 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
59 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
@@ -1,99 +1,99 b''
1 foo
1 foo
2 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 foo
3 foo
4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 (branch merge, don't forget to commit)
5 (branch merge, don't forget to commit)
6 foo
6 foo
7 changeset: 5:5f8fb06e083e
7 changeset: 5:5f8fb06e083e
8 branch: foo
8 branch: foo
9 tag: tip
9 tag: tip
10 parent: 4:4909a3732169
10 parent: 4:4909a3732169
11 parent: 3:bf1bc2f45e83
11 parent: 3:bf1bc2f45e83
12 user: test
12 user: test
13 date: Mon Jan 12 13:46:40 1970 +0000
13 date: Mon Jan 12 13:46:40 1970 +0000
14 summary: merge
14 summary: merge
15
15
16 changeset: 4:4909a3732169
16 changeset: 4:4909a3732169
17 branch: foo
17 branch: foo
18 parent: 1:b699b1cec9c2
18 parent: 1:b699b1cec9c2
19 user: test
19 user: test
20 date: Mon Jan 12 13:46:40 1970 +0000
20 date: Mon Jan 12 13:46:40 1970 +0000
21 summary: modify a branch
21 summary: modify a branch
22
22
23 changeset: 3:bf1bc2f45e83
23 changeset: 3:bf1bc2f45e83
24 user: test
24 user: test
25 date: Mon Jan 12 13:46:40 1970 +0000
25 date: Mon Jan 12 13:46:40 1970 +0000
26 summary: clear branch name
26 summary: clear branch name
27
27
28 changeset: 2:67ec16bde7f1
28 changeset: 2:67ec16bde7f1
29 branch: bar
29 branch: bar
30 user: test
30 user: test
31 date: Mon Jan 12 13:46:40 1970 +0000
31 date: Mon Jan 12 13:46:40 1970 +0000
32 summary: change branch name
32 summary: change branch name
33
33
34 changeset: 1:b699b1cec9c2
34 changeset: 1:b699b1cec9c2
35 branch: foo
35 branch: foo
36 user: test
36 user: test
37 date: Mon Jan 12 13:46:40 1970 +0000
37 date: Mon Jan 12 13:46:40 1970 +0000
38 summary: add branch name
38 summary: add branch name
39
39
40 changeset: 0:be8523e69bf8
40 changeset: 0:be8523e69bf8
41 user: test
41 user: test
42 date: Mon Jan 12 13:46:40 1970 +0000
42 date: Mon Jan 12 13:46:40 1970 +0000
43 summary: initial
43 summary: initial
44
44
45 foo 5:5f8fb06e083e
45 foo 5:5f8fb06e083e
46 3:bf1bc2f45e83
46 default 3:bf1bc2f45e83
47 bar 2:67ec16bde7f1
47 bar 2:67ec16bde7f1
48 foo
48 foo
49
49 default
50 bar
50 bar
51 % test for invalid branch cache
51 % test for invalid branch cache
52 rolling back last transaction
52 rolling back last transaction
53 changeset: 4:4909a3732169
53 changeset: 4:4909a3732169
54 branch: foo
54 branch: foo
55 tag: tip
55 tag: tip
56 parent: 1:b699b1cec9c2
56 parent: 1:b699b1cec9c2
57 user: test
57 user: test
58 date: Mon Jan 12 13:46:40 1970 +0000
58 date: Mon Jan 12 13:46:40 1970 +0000
59 summary: modify a branch
59 summary: modify a branch
60
60
61 Invalid branch cache: unknown tip
61 Invalid branch cache: unknown tip
62 changeset: 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
62 changeset: 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
63 branch: foo
63 branch: foo
64 tag: tip
64 tag: tip
65 parent: 1:b699b1cec9c2966b3700de4fef0dc123cd754c31
65 parent: 1:b699b1cec9c2966b3700de4fef0dc123cd754c31
66 parent: -1:0000000000000000000000000000000000000000
66 parent: -1:0000000000000000000000000000000000000000
67 manifest: 4:d01b250baaa05909152f7ae07d7a649deea0df9a
67 manifest: 4:d01b250baaa05909152f7ae07d7a649deea0df9a
68 user: test
68 user: test
69 date: Mon Jan 12 13:46:40 1970 +0000
69 date: Mon Jan 12 13:46:40 1970 +0000
70 files: a
70 files: a
71 extra: branch=foo
71 extra: branch=foo
72 description:
72 description:
73 modify a branch
73 modify a branch
74
74
75
75
76 4:4909a3732169
76 4:4909a3732169
77 features: unnamed
77 features: default
78 4909a3732169c0c20011c4f4b8fdff4e3d89b23f 4
78 4909a3732169c0c20011c4f4b8fdff4e3d89b23f 4
79 bf1bc2f45e834c75404d0ddab57d53beab56e2f8
79 bf1bc2f45e834c75404d0ddab57d53beab56e2f8 default
80 4909a3732169c0c20011c4f4b8fdff4e3d89b23f foo
80 4909a3732169c0c20011c4f4b8fdff4e3d89b23f foo
81 67ec16bde7f1575d523313b9bca000f6a6f12dca bar
81 67ec16bde7f1575d523313b9bca000f6a6f12dca bar
82 % test for different branch cache features
82 % test for different branch cache features
83 branch cache: no features specified
83 branch cache: no features specified
84 foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
84 foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
85 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8
85 default 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8
86 bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca
86 bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca
87 branch cache: unknown features: dummy, foo, bar
87 branch cache: missing features: default
88 foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
88 foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
89 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8
89 default 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8
90 bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca
90 bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca
91 branch cache: missing features: unnamed
91 branch cache: missing features: default
92 foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
92 foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f
93 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8
93 default 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8
94 bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca
94 bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca
95 % test old hg reading branch cache with feature list
95 % test old hg reading branch cache with feature list
96 ValueError raised correctly, good.
96 ValueError raised correctly, good.
97 % update with no arguments: tipmost revision of the current branch
97 % update with no arguments: tipmost revision of the current branch
98 bf1bc2f45e83
98 bf1bc2f45e83
99 4909a3732169 (foo) tip
99 4909a3732169 (foo) tip
General Comments 0
You need to be logged in to leave comments. Login now